Attribute Macro macro_magic_macros::import_tokens_attr
source · #[import_tokens_attr]
Expand description
Can be attached to an attribute proc macro function, causing it to receive the tokens for
the external item referred to by the path provided as the attr
/ first argument to the
attribute macro.
The item whose path is provided as the attr
/ first argument must have the
#[export_tokens]
attribute attached to it, or this will not
work.
For example:
#[import_tokens_attr]
#[proc_macro_attribute]
pub fn my_attribute(attr: TokenStream, tokens: TokenStream) -> TokenStream {
let external_item = parse_macro_input!(attr as Item);
let attached_item = parse_macro_input!(tokens as Item);
// ...
}
Which could then be used like:
#[my_attribute(path::to::AnItem)]
mod my_mod {
// ...
}
This would result in the external_item
variable having the parsed tokens of the external
path::to::AnItem
item, and the attached_item
variable having the parsed tokens of the
item the attribute is attached to (my_mod
) as usual.
This allows for the creation of extremely powerful attribute macros that take in an export
tokens path as their attr
and internally receive the tokens for that external item. For
example you could write an attribute macro that combines two modules or two structs
together, among many other things. Custom parsing, covered below, makes these capabilities
even more powerful.
§Overriding MACRO_MAGIC_ROOT
You can also provide a module path as an optional argument to this attribute macro and that
path will be used as the override for MACRO_MAGIC_ROOT
within the context of code
generated by this attribute. Instead of a Path
, you are also free to provide any Expr
that evaluates to something compatible with Into<String>
so you can dynamically
generate this path based on format!
and other string manipulation machinery, if
necessary.
Here is an example of providing a Path
as the override for MACRO_MAGIC_ROOT
:
#[import_tokens_attr(my_crate::__private::macro_magic)]
pub fn my_macro(attr: TokenStream, tokens: TokenStream) -> TokenStream {
// ..
}
and here is an example of providing an Into<String>
-compatible Expr
as the override
for MACRO_MAGIC_ROOT
:
#[import_tokens_proc(format!("{}::__private::macro_magic", generate_crate_access_2018("my_crate")))]
pub fn my_macro(attr: TokenStream, tokens: TokenStream) -> TokenStream {
// ..
}
§Optional Feature: #[with_custom_parsing(..)]
By default, #[import_tokens_attr]
-based attribute macros
expect the foreign item path to be passed directly as the only argument to the resulting
macro. Sometimes, however, it is desirable to support multiple arguments, or otherwise
implement some kind of custom parsing that determines how the foreign path is obtained. You
can do this by attaching the optional attribute
#[with_custom_parsing(..)]
to the same proc macro
attribute definition that you attached #[import_tokens_attr]
to.
This optional attribute takes one argument, which should be the path to a struct that
implements syn::parse::Parse
, quote::ToTokens
, and ForeignPath
. To access the
tokens for your custom parsed input, you can use the magic variable __custom_tokens: TokenStream
anywhere in your attribute proc macro.
Here is a full example:
#[derive(Parse)]
struct MyCustomParsing {
foreign_path: syn::Path,
_comma: syn::token::Comma,
custom_path: syn::Path,
}
impl ToTokens for MyCustomParsing {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
tokens.extend(self.foreign_path.to_token_stream());
tokens.extend(self._comma.to_token_stream());
tokens.extend(self.custom_path.to_token_stream());
}
}
impl ForeignPath for MyCustomParsing {
fn foreign_path(&self) -> &syn::Path {
&self.foreign_path
}
}
#[import_tokens_attr]
#[with_custom_parsing(MyCustomParsing)]
#[proc_macro_attribute]
pub fn my_attribute(attr: TokenStream, tokens: TokenStream) -> TokenStream {
let external_item = parse_macro_input!(attr as Item);
let attached_item = parse_macro_input!(tokens as Item);
let custom_parsed_item = parse_macro_input!(__custom_tokens as MyCustomParsing);
// ...
}
Usage would look like:
#[my_attribute(foreign::path, some_other::path)]
struct SomeItem {}
This is just an example, you could implement the parsing any way you want, maybe even using
something that isn’t initially a syn::Path
but is transformed into one. The possibilities
are endless.
§Notes
- See
tests.rs
for more examples. - Can only be used within a proc macro crate.
- A handy
__source_path: TokenStream
variable is also injected into your proc macro function definition which provides access to the originalsyn::Path
that was provided as the path for the foreign item before its tokens were imported. You can access this directly simply by referring to__source_path
. This should parse to asyn::Path
. - When using the custom parsing feature, you can also access the original tokens for the
input attribute within your proc macro body using the magic variable
__custom_tokens
. For more information and an example seewith_custom_parsing
.