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 original syn::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 a syn::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 see with_custom_parsing.