Skip to content
This repository has been archived by the owner on Dec 27, 2022. It is now read-only.

Hygiene inconsistent with #[proc_macro] #31

Closed
awygle opened this issue Apr 21, 2019 · 2 comments · Fixed by #32
Closed

Hygiene inconsistent with #[proc_macro] #31

awygle opened this issue Apr 21, 2019 · 2 comments · Fixed by #32

Comments

@awygle
Copy link

awygle commented Apr 21, 2019

When generating a reference to a variable that's not explicitly passed in to a #[proc_macro_hack] macro, but which is present local to the call site, behavior is inconsistent with the behavior of #[proc_macro] on nightly, probably due to the enclosing macro_rules! macro. This should be documented as a limitation (or worked around, which would be great).

Example

Defining a proc_macro_hack macro like this:

// repro-impl/src/lib.rs
extern crate proc_macro;
use proc_macro::*;
use proc_macro_hack::proc_macro_hack;
use syn;

#[proc_macro_hack]
pub fn repro(input: TokenStream) -> TokenStream {
    let mut body :Vec<TokenTree>= Vec::<TokenTree>::new();

    // parse and add `a+3`
    let ba = TokenStream::from(syn::parse_str::<proc_macro2::TokenStream>(&"a+3").unwrap());
    body.extend(ba);

    body.into_iter().collect::<TokenStream>()
}

Exporting it like this:

// repro/src/lib.rs
use proc_macro_hack::proc_macro_hack;

#[proc_macro_hack]
pub use repro_impl::repro;

And calling it like this:

// repro-test/src/main.rs
extern crate repro;

fn main() {
let a = 3;
let b = repro::repro!();
}

Fails with:

error[E0425]: cannot find value `a` in this scope
 --> repro-test\src\main.rs:5:9
  |
5 | let b = repro::repro!();
  |         ^^^^^^^^^^^^^^^ not found in this scope

However, defining a proc_macro like this:

// repro-nightly/src/lib.rs
extern crate proc_macro;
use proc_macro::*;
use syn;

#[proc_macro]
pub fn repro(input: TokenStream) -> TokenStream {
    let mut body :Vec<TokenTree>= Vec::<TokenTree>::new();

    // parse and add `a+3`
    let ba = TokenStream::from(syn::parse_str::<proc_macro2::TokenStream>(&"a+3").unwrap());
    body.extend(ba);

    body.into_iter().collect::<TokenStream>()
}

And calling it like this:

// repro-test/src/main.rs
#![feature(proc_macro_hygiene)]
extern crate repro;
extern crate repro_nightly;

fn main() {
let a = 3;
let b = repro_nightly::repro!();
}

Succeeds.

@awygle
Copy link
Author

awygle commented Apr 21, 2019

Just for the record - my original goal was to write a proc macro that took strings like this:

formata!("{var}")

and expanded to

format!("{}", var)

So if anyone has a suggestion for how to make that work, I'd also appreciate that :)

@dtolnay
Copy link
Owner

dtolnay commented May 8, 2019

Thanks for the clear repro! I developed a fix to make it work, but I can't really tell how this hack might interact with some other hacks so I haven't enabled it by default. I released proc-macro-hack 0.5.6 in which #[proc_macro_hack(fake_call_site)] in repro/src/lib.rs will almost1 make your code work. Hopefully I can make this the default behavior in a later release after getting some experience with it.

1 It only works if the macro has at least one input token, which your repro does not but your real use case sounds like it always would.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants