Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using $crate with a proc macro #37637

Closed
dtolnay opened this issue Nov 7, 2016 · 10 comments
Closed

Using $crate with a proc macro #37637

dtolnay opened this issue Nov 7, 2016 · 10 comments
Assignees

Comments

@dtolnay
Copy link
Member

dtolnay commented Nov 7, 2016

It seems that TokenStream::parse() does not understand $crate, yet $crate gets passed as input to proc macros. That doesn't seem fair!

Here is a script to reproduce the issue. It creates a proc macro called noop_derive which just reparses the input TokenStream. It creates a crate called repro which uses #[derive(Noop)] from within macro_rules. Expected behavior would be either of the following:

  • $crate is eliminated before invoking the proc macro, or
  • $crate is passed to the proc macro and can be parsed by TokenStream.
#!/bin/bash

cargo new noop_derive
cat >> noop_derive/Cargo.toml <<-'EOF'
    [lib]
    proc-macro = true
EOF

cat > noop_derive/src/lib.rs <<-'EOF'
    #![feature(proc_macro, proc_macro_lib)]

    extern crate proc_macro;
    use proc_macro::TokenStream;

    #[proc_macro_derive(Noop)]
    pub fn noop(input: TokenStream) -> TokenStream {
        input.to_string().parse().unwrap()
    }
EOF

cargo new repro
cat >> repro/Cargo.toml <<-'EOF'
    noop_derive = { path = "../noop_derive" }
EOF

cat > repro/src/lib.rs <<-'EOF'
    #![feature(proc_macro)]

    #[macro_use]
    extern crate noop_derive;

    struct A;

    macro_rules! B {
        () => {
            #[derive(Noop)]
            struct B {
                a: $crate::A
            }
        };
    }

    B!();
EOF

cd repro
cargo build
$ ./repro.sh
     Created library `noop_derive` project
     Created library `repro` project
   Compiling noop_derive v0.1.0
   Compiling repro v0.1.0
error: custom derive attribute panicked
  --> src/lib.rs:10:22
   |
10 |             #[derive(Noop)]
   |                      ^^^^
...
17 |     B!();
   |     ----- in this macro invocation
   |
   = help: message: called `Result::unwrap()` on an `Err` value: LexError { _inner: () }

error: Could not compile `repro`.

To learn more, run the command again with --verbose.
@jseyfried
Copy link
Contributor

jseyfried commented Nov 7, 2016

This will be fixed when #37614 lands.

@dtolnay
Copy link
Member Author

dtolnay commented Nov 7, 2016

@jseyfried can you clarify how this is fixed by #37614? It isn't obvious from the thread. I see that custom derives no longer return the original item but that doesn't fix other uses of $crate in the output, for example in derive_new:

impl B {
    pub fn new(a: $crate::A) -> Self {
        B { a: A }
    }
}

@jseyfried
Copy link
Contributor

jseyfried commented Nov 7, 2016

@dtolnay good point, #37614 won't fix other uses of $crate in the output.

I think the best solution would be to eliminate $crate where possible before invoking the proc macro.
This is not always possible though, for example:

/// crate A
pub struct Foo;
#[macro_export]
macro_rules! m { () => {
    #[derive(custom)] struct Bar($crate::Foo);
} }

/// crate B
#[macro_reexport(m)] extern crate A;

/// crate C
#[macro_use(m)] extern crate B;
m!();
//^ expands to `#[derive(custom)] struct Bar($crate::Foo);`
// There is no way to eliminate the `$crate` before invoking the custom derive
// since there's no other way to name `Foo`.

In this case, I think we'll have to error on $crate (this wouldn't have worked even without the custom derive before #37213).
A don't think we'll be able to avoid an error in this case until we stabilize more TokenStream API.

@jseyfried jseyfried self-assigned this Nov 7, 2016
@alexcrichton
Copy link
Member

If we can't solve this today I wonder if we could perhaps provide a more targeted error message? E.g. just print an error saying "sorry, but $crate in a struct tagged with #[derive] is not yet supported"

@jseyfried
Copy link
Contributor

I'll solve this today.

@jseyfried
Copy link
Contributor

Fixed in #37645.

bors added a commit that referenced this issue Nov 10, 2016
Fix regression involving custom derives on items with `$crate`

The regression was introduced in #37213.

I believe we cannot make the improvements from #37213 work with the current custom derive setup (c.f. #37637 (comment)) -- we'll have to wait for `TokenStream`'s API to improve.

Fixes #37637.
r? @nrc
@tikue
Copy link
Contributor

tikue commented Mar 9, 2017

This seems to have regressed again, this time with function-like proc macros:

Proc Macro crate:

#[proc_macro]
pub fn print_input(input: TokenStream) -> TokenStream {
    let source = input.to_string();
    println!("{}", source);
    input
}

Used here:

#![feature(use_extern_macros)]

extern crate print_input;

fn hello() {
    println!("Hello");
}

macro_rules! print_hello {
    () => {
        print_input::print_input!($crate::hello())
    }
}

fn main() {
    print_hello!();
}

cargo build output: $crate :: hello ( )

@jseyfried
Copy link
Contributor

@tikue I don't think that's a regression -- the fix has only ever applied to #[proc_macro_derive].

Since the fix is fairly invasive and often doesn't work (esp. when used with other macros 2.0), I think we should wait for better TokenStream API (landing next week) that will fully support $crate.

@tikue
Copy link
Contributor

tikue commented Mar 9, 2017

@jseyfried sounds good, thanks!

@seunlanlege
Copy link

Does using $crate work in proc_macros now? And if it doesn't I'd love to help out.

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

No branches or pull requests

5 participants