proc-macro2 icon indicating copy to clipboard operation
proc-macro2 copied to clipboard

Span pointing to its own construction site like Location::caller

Open ia0 opened this issue 2 years ago • 1 comments

I couldn't easily figure by myself reading the code and documentation of proc-macro and proc-macro2, so asking here. Is it possible to generate a Span that points to its own span? Or maybe asked differently, is it possible to create a Span from (or similarly to) a core::panic::Location (or 2 to make it an actual span, but a point in code is enough for me)?

Here's the motivation (which I would understand if it's not something proc-macro is trying to address):

  • I would like to use proc-macro to generate code from some data-structure describing the code to be generated (see below for why this data-structure cannot be a token stream).
  • This data-structure would be built within the proc-macro crate itself (or eventually from a dependency crate).
  • When building this data-structure, I would like to "capture" the Span of constructor calls.
  • I would then use quote_spanned!() to generate the proc-macro output based on those Spans.
  • (I hope rust-analyzer would then be able to point me back to the correct place in the proc-macro crate when looking for definitions generated by the proc-macro.)

Here's a sketch of a concret example:

enum Input { ... }  // data-structure to generate code from

#[proc_macro]
pub fn generate(_: TokenStream) -> TokenStream {
    // We don't need the proc-macro input. We use the `Input` data-structure.
    let input = Input::generate();  // The input is actually constant, but built dynamically.
    input.into_token_stream().into()
}

impl Input {
    fn generate() -> Input {
        ...
        let span = Span::caller();  // This behaves like Location::caller();
        let sub_input = SubInput::Foo { span, ... };
        ...
        let span = Span::caller();  // All those calls could be split in multiple modules and possibly crates.
        Input::Bar { span, sub_input, ... }
    }
}

impl ToTokens for Input {
    fn to_tokens(&self, tokens: &mut TokenStream2) {
        ...
        tokens.extend(quote_spanned!(sub_input.span => ...));
        ...
    }
}

The reason to not use the input token stream is that it doesn't support going through modules recursively. I would like my data-structure (or equivalently the proc-macro input) to be hierarchically structured through multiple files like the module hierarchy in a crate does. In other words, the following alternatives would also work for me:

  • It is possible within a proc-macro to read the content of a file (relative to the initial macro invocation) as a token stream.
  • It is possible for a proc-macro to take a full crate as input, where modules are flattened as if it was a single file. (But this assumes the proc-macro input is valid Rust, which adds a usage constraint.)

ia0 avatar Nov 15 '22 15:11 ia0

Actually found out about https://github.com/rust-lang/rust/issues/92565, https://github.com/rust-lang/rfcs/pull/3200, and in particular https://github.com/rust-lang/rust/issues/55904, which may be solving my problem in a different way.

ia0 avatar Dec 20 '22 11:12 ia0