Hi, I have an attribute procedural macro I want to add to every function in a Rust file for compilation without modifying source code. This might be analogous to defining macros at compile time in C using the -D option. It allows you to make use of macros without adding #define statements.
I know how to use the syn crate to modify the syntax tree of Rust code to add my macro to every function in the syntax tree using the syn::visit_mut module. However, I do not want to use the quote macro to return new code as one might do with a syntax tree from the syn crate. Is there a way to invoke rustc on the new syntax tree I want to use directly instead?
Alternatively, are there any options in rustc or cargo to insert procedural macros for each function or struct without modifying the source code after compilation?
EDIT: You could instead use a function-like procedural macro that includes most of the file and which adds your attribute in front of every function. You should then be enable or disable that from the rustc command line using conditional compilation.
I don't see any other way of manipulating the AST processed by the compiler directly from a compiler option and without touching the original source files. Except by using build.rs and modifying these source files, but that seems a little desperate. After compilation, it would indeed be too late for anything practical (though I suppose it theoretically depends on what you want to achieve).
For the function-like macro with conditional compilation, doesn't this require modifying the code to wrap each file with the macro and #[cfg...]? Or is there some way conditional compilation can enable this without actually wrapping each file?
And, wouldn't adding an inner attribute at the crate root also require modifying the code?
I know both these methods require very minimal code changes, but it seems to me like there should be some way to apply macros at compile time, since linting attributes like warnings for dead code and unused variables are also on by default and can be turned off with compiler flags.
I understand it is not a huge priority for the compiler writers, though, and not a common problem.
Yes, it would require a small modification of the code in both cases, attribute or function proc macro. Perhaps someone else sees another way with the command-line, but I don't.
The only alternative is to process the source files with build.rs, so that they'd be modified automatically instead of manually. Maybe by copying the modified version elsewhere and compiling them, but I've never tried that, so I'm not sure it's very practical or even possible with build.rs alone. It sure sounds very cumbersome.
Out of curiosity, why would you want to do that? I can't think of any benefit one would gain from modifying existing source files inconspicuously. It looks like a recipe for problems, which is probably why it's not allowed. Rust is very much about being explicit to avoid any hidden behaviour.
I need to write a tool which probes Rust programs at runtime to extract data for a machine learning tool. Ideally I could find a way to apply the tool to an arbitrary Rust project without modifying the project's code so people don't have to wreck or copy their codebase to use the tool. Using procedural macros to probe functions seemed like a good way to do this, but inserting them is one of the issues I am facing.
That is interesting, is it possible to use llvm-cov to output the values of arguments passed to each function when it is called, or the return value for each function call? It seems like the options are mainly for understanding control flow and how much time is spent in each function.
You'll have to look into it, as I've never used those tools for that. I believe gdb is able to trace function arguments, for instance, but I've never tried on a Rust program. The question is whether the data will make any sense: what sort of data is that, integers, more complex objects, or even references to objects? It could be hard to interpret them, depending on what you want to do.
From your previous post, I understand you'd like other people to be able to extract the data themselves, so wouldn't it be easier for them to simply insert the macro? Shouldn't they know what data they need to collect and exactly which functions they're interested in? Something that collects everything will generate an awful lot of log, a lot of which probably not relevant, so maybe some manual selection is needed. Also, they must be sure they can collect the data in a printable form, so they might have to insert Debug derives on some types, or when it's not possible, tell your macro to ignore those types.
Cleaning up the code is as simple as a Git revert or a rollback.
Another option is to modify the compiler to add that functionality, but that's more complicated.