[Solved] Is it possible to reexport a proc_macro?


#1

Hi, I was experimenting with new proc macros recently and wondered if it’s possible to reexport them. Consider the following scenario:

Let’s say there’s a crate called definition containing a foo macro:

#![feature(proc_macro)]
extern crate proc_macro;
use proc_macro::TokenStream;

#[proc_macro]
pub fn foo(_input: TokenStream) -> TokenStream { unimplemented!() }

I’ve created a reexport crate:

#![feature(proc_macro)]
extern crate definition;
pub use definition::foo;

Now, when I try to use the reexported macro:

#[feature(proc_macro)]

// #[macro_use]
extern crate reexport;

use reexport::foo;

fn main() {
    foo!()
}

It fails with unresolved import:

error[E0432]: unresolved import `reexport::foo`
 --> src/main.rs:6:5
  |
6 | use reexport::foo;
  |     ^^^^^^^^^^^^^ no `foo` in the root

I’ve created a repository with such a setup, if anybody wants to experiment.

Now, the fun part is that if I comment out the use line and uncomment the macro_use instead, the reexported macro runs, ie. the message proc macro paniced is printed. But if I change the macro not to panic, I get the following error:

error: procedural macros cannot be imported with `#[macro_use]`
 --> src/main.rs:9:5
  |
9 |     foo!()
  |     ^^^
help: instead, import the procedural macro like any other item
  |
8 | use definition::foo;
  |

Which is exactly what I was trying in the first place!

So, is it actually possible to reexport a procedural macro?

In the case I’ve fallen into some XY-problem trap: My original problem was “how to export both macro_rules and a proc-macro macros in the same crate” and I’ve tried to put the proc macro in separate definition crate (to sidestep the limitation that proc-macro = true crate can’t contain macro_rules macros).


#2

The failure crate exports both macro_rules and proc macros from the same crate. Take a look at what they do.


#3

I’ve tried grepping proc_macro in failure’s source, but it seems that failure exports only macro_rules macros and failure_derive exports the procedural derive. Perhaps I’ve missed something?


#4

The same crate exports both the Fail custom derive procedural macro and the macro rules listed in https://docs.rs/failure/0.1/failure/#macros.

#[macro_use]
extern crate failure;

#[derive(Debug, Fail)]
enum ToolchainError {
    #[fail(display = "invalid toolchain name")]
    InvalidToolchainName,
}

fn main() {
    let _ = format_err!("Error code: {}", 101);
}

#5

Sorry, my bad then – the failure crate indeed reexports proc macro from failure_derive. It does so by #[macro_use] + pub use and as per your example, requires only #[macro_use] on usage site. Unfortunately, when I try to apply this pattern to my usecase, I get the same procedural macros cannot be imported with #[macro_use] error as before. Perhaps the difference is that I try to use expression-style, not derive-style macro.


#6

I have this same problem, as documented in the most recent comments of https://github.com/rust-lang/rust/pull/51265. Is this something that’s supposed to work eventually?


#7

I’ve looked again at the example repository I posted before and… it turns out I was just missing ! in #![feature(proc_macro)] in one of the files, and after fixing this, it works! I’ve updated the repository.

I have a crate that’s using such a reexport and I had thought that the crate works because a bug was fixed in nightly, but it turns out now that it was just a typo in my example.

So, tldr:

  • #![feature(proc_macro)] in all the crates involved (defining, reexporting and using).
  • Reexport:
    extern crate defining;
    pub use defining::my_macro;
    
  • Usage:
    extern crate reexporting;
    use reexporting::my_macro;
    

So no #[macro_use] nor #[macro_export] anywhere!

@tikue I’m not sure it solves your problem though…