Is there a way to use items in `proc_macro` crate?

Hello.
I try to custom a derive macro (it doesn't meet me) in following way:

let s1 = proc_macro_transform(&mut syn::DeriveInput);
let s2 = my_proc_macro_transform(s1);

proc_macro_transform is the origin proc_macro, and my_proc_macro_transform is what I want.
But when I try to export proc_macro_transform from its crate. I meet:

error: `proc-macro` crate types currently cannot export any items other than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, or `#[proc_macro_attribute]`

It seems denied by rust, is there a way to do it?

So I guess proc_macro_transform and my_proc_macro_transform are functions in your proc-macro crate? And you want to export them like pub fn my_proc_macro_transform? If that's the case that isn't possible and you need a second, non-proc-macro crate to export these functions. You can then re-export your derive macro from the non-proc-macro crate. This is a common pattern, the most famous example being serde and serde_derive (the derive suffix is usually used for the proc-macro crate providing the derive macros for the items exported by the other crate).

1 Like

Thanks for your help.
proc_macro_transform is defined in a third-party proc_macro crate. my_proc_macro_transform is defined in a proc_macro crate written by me. And I want to export proc_macro_transform, and use it in my proc_macro crate.

I'm struggling to understand what you mean here. Could you maybe share a minimal example of the code that is giving you troubles?

AFAIU, you have three crates,

  1. A third party proc-macro crate providing the proc_macro_transform! function-like procedural macro, let's call it third_party
  2. A proc-macro crate by you that provides the my_proc_macro_transform! function-like procedural macro, let's call it your_transform
  3. A proc-macro crate by you with a derive procedural macro that uses both proc_macro_transform! and my_proc_macro_transform!,

and you are trying to re-export third_party::proc_macro_transform! from your_transform?

1 Like

Hello this is the example to custom serde Deserialize

extern crate proc_macro2;
extern crate quote;
extern crate syn;

extern crate proc_macro;

extern crate serde_derive;

use proc_macro::TokenStream;
use syn::parse_macro_input;
use syn::DeriveInput;

fn my_expand_derive_serialize(input: &mut DeriveInput) -> sync::Result<TokenStream> {
    // do custom transform here
}

#[proc_macro_derive(UntaggedDeserialize, attributes(serde))]
pub fn derive_deserialize(input: TokenStream) -> TokenStream {
    let mut input = parse_macro_input!(input as DeriveInput);
    let s1 = serde_derive::de::expand_derive_deserialize(&mut input)
        .unwrap_or_else(syn::Error::into_compile_error)
        .into()?;
    
    // do custom transform here
    my_expand_derive_serialize(s1 as DeriveInput);
        .unwrap_or_else(syn::Error::into_compile_error)
        .into()
}

In this way, I want to add some modification by my_expand_derive_serialize, but can't use serde_derive::de::expand_derive_deserialize.

Well, serde_derive doesn't export it. the de module is private. Note that it couldn't export it in the first place because expand_derive_deserialize is a function and not a procedural macro.

Yes, I try to make it public, but got error like

error: `proc-macro` crate types currently cannot export any items other than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, or `#[proc_macro_attribute]`

I guess you could create a wrapper similar to the Deserialize macro but not as a derive macro but a function-like one and export that (from your fork of serde_derive) instead of the function directly:

#[proc_macro]
pub fn expand_derive_deserialize(input: TokenStream) -> TokenStream {
    let mut input = parse_macro_input!(input as DeriveInput);
    de::expand_derive_deserialize(&mut input)
        .unwrap_or_else(syn::Error::into_compile_error)
        .into()
}

Then you can call it in your macro as:

[proc_macro_derive(UntaggedDeserialize, attributes(serde))]
pub fn derive_deserialize(input: TokenStream) -> TokenStream {
-   let mut input = parse_macro_input!(input as DeriveInput);
-   let s1 = serde_derive::de::expand_derive_deserialize(&mut input)
-       .unwrap_or_else(syn::Error::into_compile_error)
-       .into()?;
+   let s1 = serde_derive::expand_derive_deserialize!(input);

Never tried this though, might not work as I wish it would because the input token stream is not expanded?


Edit: After thinking about it, the exporting-a-function-like-procedural-macro is probably not going to work, but if you don't need the Deserialize and Serialize macros in your serde_derive fork, you should be able to make your fork into a normal crate by removing this line and export serde_derive::de::expand_derive_deserialize.

Thanks. I use the proc_macro directly to reach it as:

pub fn expand_serde_derive_serialize(input: &mut DeriveInput) -> syn::Result<TokenStream> {
    Ok(quote!(
        #[derive(Deserialize)]
        #input
    ).into())
}