How do I make my proc macros rust-analyzer "friendly"?

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.

3 Likes

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.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.