I was reading this issue and it sounds like the main problem with proc-macros is dying early when they have unexpected input, and rust-analyzer wants to see the expansion. Maybe there are other issues but this seems like at least one. I'm thinking where possible I should pass through user tokens without parsing them. I assume if I ask the syn crate to just pull a token tree from the input stream, it will only care that opening/closing pairs match, and ignore other syntax errors inside? If that won't work is there an idiom or library that helps write proc-macros like this?
Also curious if there are any other dos and don'ts for writing your proc-macros in a way that keeps code completion working as much as possible.
I assume in a perfect world the proc macro API itself would change to somehow better accommodate tools like rust-analyzer, but I'm wondering what practical steps as a proc-macro author I can take in the meantime to give my users the best experience. Where possible I have just tried to use macro_rules based macros instead, but this isn't always possible.
Yes, either that, or you need to parse them in a way that lets you recover from syntax errors instead of making the whole macro expand to compile_error!("<some syntax error>"), which is what the existing libraries like syn do.
The macro input can be assumed to have all delimiters matched (this is enforced by construction of the proc_macro API, and rust-analyzer will insert fake delimiters if they are mismatched).
Ideally, we would have a syn 2.0, or some fork of it, that doesn't return Results, but recovers from parse errors and uses something like (Element, Vec<Error>) (hopefully with better ergonomics), and migrate the proc_macro ecosystem to that.
Off the top of my head, there's a few things:
Avoid I/O, state, and reliance on expansion order.
Keep input tokens intact wherever possible, don't parse &strs (this makes it impossible to map an input token to the macro output).
Don't use Span::call_site() for identifiers that you want IDE support for (instead, use the source identifier token as-is). Do use Span::call_site() for internal code you don't want IDE support for.
And, just to reiterate: Be resilient to malformed input. Emit as much as possible of the "usual" macro output even when there are errors in the input. Errors should be emitted as compile_error! invocations in addition to the normal macro output.