First off, thanks for writing this excellent blog post on structuring and testing procedural macros! I really like the way it's broken down in to parsing, analysis, intermediate representation, and code generation. I am currently attempting to implement a procedural macro of my own following this structure but I've run into an issue that is probably more specific to proc-macro-error
than it is your guide, but I thought it would be worth raising as an issue here anyway before I start trying to dig into that codebase.
The issue is with writing test cases to validate what happens when the parsing stage encounters invalid syntax. I imagine that tests for the analysis stage could be similarly affected, but I haven't made it that far yet in my own macro implementation :upside_down_face:. (I also wasn't intentionally trying to test invalid syntax, I just haven't made it far enough in my parse implementation for the syntax i'm targeting to be properly parsed.)
So the problem is that the parse function calls proc-macro-error
macros like abort
and abort_call_site
which results in a panic in tests that validate error cases since according to its docs:
This attribute MUST be present on the top level of your macro (the function annotated with any of #[proc_macro], #[proc_macro_derive], #[proc_macro_attribute]).
To illustrate with an example, here is a contrived test that I created in a clone of this repo:
zsh/3 3540 [148] (git)-[main]-% git diff
diff --git a/src/parse.rs b/src/parse.rs
index 5572a87..709b66b 100644
--- a/src/parse.rs
+++ b/src/parse.rs
@@ -51,4 +51,18 @@ mod tests {
),
);
}
+
+ #[test]
+ fn invalid_syntax() {
+ parse(
+ quote!(ident),
+ quote!(
+ #[inline]
+ #[precondition(x <>> == 0)]
+ fn even_to_odd(x: u32) -> u32 {
+ x + 1
+ }
+ ),
+ );
+ }
}
(basically just the existing parse::valid_syntax
test with a simple ident argument added in the first positional passed to parse
)
The docs for proc_macro_error
indicate an argument that can be passed to the called allow_not_macro
(eg #[proc_macro_error(allow_not_macro)]
, but that comes with the caveat:
Pay attention: the function this attribute is applied to must return proc_macro::TokenStream
.
This is problematic since the parse
example in your tutorial relies on proc_macro2::TokenStream
.
Anyway, like I mentioned earlier I don't think this is necessarily a problem with this tutorial but thought it would be useful to mention in case any other bright-eyed wannabe Rustaceans find themselves dealing with a similar situation. I'll probably dig into the proc-macro-error
crate after submitting this issue to see if it's possible for #[proc_macro_error(allow_not_macro)]
to accept proc_macro2::TokenStream
as well as proc_macro::TokenStream
.
Thanks again for the great tutorial!