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:
- Derivations such as the
Name
in#[derive(Name!)]
or#[derive(Name!(...))]
. - Top-level attributes written as
#[name!]
or#![name!(...)]
.
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:
#[derive(Copy, Name!(args...), Clone, Another!, Debug)] struct Foo;
macro_attr!
will expand to the equivalent of:
#[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:
#[make_unitary!] #[repr(C)] #[rename_to!(Quux)] #[doc="Test."] struct Bar { field: i32 }
macro_attr!
will expand to:
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:
macro_attr_callback! { $resume, #[repr(C)] #[rename_to!(Quxx)] #[doc="Test."] struct Bar; }
macro_attr!
will then resume parsing, and expand to:
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:
macro_attr_callback! { $resume, #[doc="Test."] struct Quxx; }
Once more, macro_attr!
will resume, and produce the final expansion of:
#[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.