Macro enum_unitary::macro_attr []

macro_rules! macro_attr {
    ( $ ( $ item : tt ) * ) => { ... };
}

When given an item definition, including its attributes, this macro parses said attributes and dispatches any attributes or derivations suffixed with ! to user-defined macros. This allows multiple macros to process the same item.

This is similar to, but distinct from, the function of "procedural" macros and compiler plugins.

Supported Forms

In particular, this macro looks for two kinds of syntax:

Unlike "real" attributes, optional parenthesised arguments after the ! are allowed to be entirely arbitrary token trees, meaning they can effectively contain any token sequence. These are supported to allow custom attribute macros to easily take arguments.

Derivations parse the item and emit whatever additional definitions needed. They cannot change the item itself, and do not receive any other attributes attached to the item.

Attributes receive everything lexically after themselves, and must re-emit the item. This allows attributes to make changes to the item, drop or alter other attributes, etc.. This power makes writing attribute macros more difficult, however.

Macro Derivations

Given the following input:

This example is not tested
#[derive(Copy, Name!(args...), Clone, Another!, Debug)]
struct Foo;

macro_attr! will expand to the equivalent of:

This example is not tested
#[derive(Copy, Clone, Debug)]
struct Foo;

Name!((args...) struct Foo;);
Another!(() struct Foo;);

Note that macro derives may be mixed with regular derives, or put in their own #[derive(...)] attribute. Also note that macro derive invocations are not passed the other attributes on the item; input will consist of the arguments provided to the derivation (i.e. (args...) in this example), the item's visibility (if any), and the item definition itself.

A macro derivation invoked without arguments will be treated as though it was invoked with empty parentheses. i.e. #[derive(Name!)] is equivalent to #[derive(Name!())].

A derivation macro may expand to any number of new items derived from the provided input. There is no way for a derivation macro to alter the item itself (for that, use a macro attribute).

Macro Attributes

When macro_attr! encounters an attribute suffixed with a ! (e.g. #[name!(args...)]), it invokes the macro name! with everything lexically after that attribute. A macro attribute is free to add to, remove from, or alter the provided input as it sees fit, before instructing macro_attr! to resume parsing.

For example, given the following input:

This example is not tested
#[make_unitary!]
#[repr(C)]
#[rename_to!(Quux)]
#[doc="Test."]
struct Bar { field: i32 }

macro_attr! will expand to:

This example is not tested
make_unitary! {
    (), then $resume,
    #[repr(C)]
    #[rename_to!(Quux)]
    #[doc="Test."]
    struct Bar { field: i32 };
}

Note that $resume is not literal. When implementing an attribute macro, you should accept this part as $resume:tt, and not attempt to inspect or deconstruct the contents.

Assuming make_unitary! removes the body of the struct it is attached to, macro_attr! requires that it expand to:

This example is not tested
macro_attr_callback! {
    $resume,
    #[repr(C)]
    #[rename_to!(Quxx)]
    #[doc="Test."]
    struct Bar;
}

macro_attr! will then resume parsing, and expand to:

This example is not tested
rename_to! {
    (Quxx), then $resume,
    #[doc="Test."]
    struct Bar;
}

Assuming rename_to! does the obvious thing and changes the name of the item it is attached to, it should expand to:

This example is not tested
macro_attr_callback! {
    $resume,
    #[doc="Test."]
    struct Quxx;
}

Once more, macro_attr! will resume, and produce the final expansion of:

This example is not tested
#[repr(C)]
#[doc="Test."]
struct Quxx;

Note that normal attributes are automatically carried through and re-attached to the item.

Macro attributes should be used as sparingly as possible: due to the way Rust macros work, they must expand recursively in sequence, which can quickly consume the available macro recursion limit. This limit can be raised, but it makes for a less-than-ideal user experience if you are authoring macros to be used by others.