Best approach to extend the scope of proc macros

I am trying to develop some procedural macros that are intended to help Rust code interface with a foreign language runtime, but have run into a rather tricky challenge.

I have a macro called "make_special_object" which uses the definition of a struct to generate the object for the foreign runtime as well as a Rust struct that implicitly talks to it. I have this working for properties, but I now want to deal with methods as well. Specifically I want to be able to do something like

#[make_special_object]
struct TestStruct {
     ...
}

impl TestStruct {
    #[special_method]
    pub fn test(name: &str) {
    }
}

in order to define that there should be a method on the foreign object. The problem here is that make_special_object needs to know everything about the foreign object to be able to construct it, but the TokenStream passed in only provides information on the struct and not for any methods implemented on it. I could just have some fancy syntax to define the function directly inside the struct and my problems would be solved, but I want to maintain the idiomatic Rust look.

The "easiest" way I can think to do this is to have something inside build.rs that parses all the code and writes up the definitions of all methods in a JSON file that make_special_object can then read to get all the information it needs. This feels a bit hacky though, and I have no idea if there is something I can use to help me parse all the Rust code in a project for this purpose?

The other hacky idea I have is to set an env var, run the compiler in a mode where it does not emit anything so that the special_method macros can dump their metadata to JSON and then run the compiler again in normal mode with make_special_object now reading the JSON. Is this even possible?

Even if either of these options could be made to work, it would feel a bit wrong, so I am totally open to suggestions for entirely different approaches to this problem.

You can apply an attribute to a module: GitHub - dtolnay/cxx: Safe interop between Rust and C++

1 Like

Thanks, I was literally just now looking at cxx and coming to the same conclusion myself :sweat_smile: . I am still interested if this is possible without resorting to applying an attribute to a full module though because I have a another similar problem in that I need to do a final registration of all the objects that I have created as well and right now that requires manually listing them all in one place, but I would like to be able to do that automatically and not necessarily require them all to be in the same module.

Also not sure if a module attribute can cope with sub-modules defined in different files.

https://crates.io/crates/linkme

(srry for short link drops, a bit busy right now :slight_smile:

2 Likes

Thanks, that also looks interesting. I'll have to look into how it works some more, but it looks like it might be using a trick that only works because it only needs to aggregate at the linker stage