7

When reporting errors in macros like println! Rust compiler shows the precise location within the format string where something has gone wrong, e.g.:

11 |     println!("I'm going to substitute {foo}... or not");
   |                                       ^^^^^ not found in this scope

How I do the same thing for my format!-like macro? Both ways of reporting errors in procedural macro (quote_spanned! + compile_error! or the experimental Diagnostic API) rely on proc_macro::Span and I don't see a way to construct a Span for a location inside a token, even via experimental APIs.

Andrei Matveiakin
  • 1,558
  • 1
  • 13
  • 17
  • It may not be possible. `format` is magic in the sense that it's built into the compiler. – Colonel Thirty Two Mar 29 '22 at 21:31
  • `format_args!()` indeed uses compiler-internal data structures for that, and I'm pretty sure this is impossible to do with user code. – Chayim Friedman Mar 29 '22 at 21:55
  • I'm not quite sure, but wouldn't [`proc_macro::Literal::subspan`](https://doc.rust-lang.org/proc_macro/struct.Literal.html#method.subspan) be close to what you want? – Danya02 Jan 31 '23 at 07:21
  • @Danya02 I think it is! Do you know if it's possible to go from [`syn::Lit`](https://docs.rs/syn/latest/syn/enum.Lit.html) to [`proc_macro::Literal`](https://doc.rust-lang.org/proc_macro/struct.Literal.html)? Also there is an open FR to support subspans on `syn::LitStr` directly: https://github.com/dtolnay/syn/issues/1219 – Andrei Matveiakin Feb 03 '23 at 10:12
  • @AndreiMatveiakin I can't check this at the moment, but I think [you can turn the `syn::Lit` into a `proc_macro2::TokenStream`](https://docs.rs/syn/latest/syn/enum.Lit.html#impl-ToTokens-for-Lit), and [then turn that into a `proc_macro::TokenStream`](https://docs.rs/proc-macro2/1.0.48/proc_macro2/struct.TokenStream.html#impl-From%3CTokenStream%3E-for-TokenStream-1), then [get a `proc_macro::TokenTree`](https://doc.rust-lang.org/nightly/proc_macro/struct.TokenStream.html#impl-IntoIterator-for-TokenStream), which there should only be one and it should be the `Literal` variant. – Danya02 Feb 03 '23 at 11:28
  • @Danya02 It worked! Thanks. Feel free to post this as an answer. As a side note, it turned out pretty hard to use in practice because `Literal::subspan` works in terms of source file characters, not string characters. E.g. `subspan(0..3)` in `r"foobar"` points to `r#f` rather than `foo`. This is exactly the problem that https://github.com/dtolnay/syn/issues/1219 was describing. Still, `Literal::subspan` API allows to achieve the goal. – Andrei Matveiakin Feb 05 '23 at 15:45

0 Answers0