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

$crate is missing from attribute and derive macro input #56622

Closed
dtolnay opened this issue Dec 8, 2018 · 2 comments
Closed

$crate is missing from attribute and derive macro input #56622

dtolnay opened this issue Dec 8, 2018 · 2 comments
Assignees
Labels
A-macros Area: All kinds of macros (custom derive, macro_rules!, proc macros, ..)

Comments

@dtolnay
Copy link
Member

dtolnay commented Dec 8, 2018

I have some macros that mostly just print their inputs:

procedural/src/lib.rs (edition = 2015 or 2018)

extern crate proc_macro;
use proc_macro::TokenStream;

#[proc_macro]
pub fn m(input: TokenStream) -> TokenStream {
    println!("PROC MACRO INPUT:  {}", input);
    input.into_iter().collect()
}

#[proc_macro_attribute]
pub fn a(_args: TokenStream, input: TokenStream) -> TokenStream {
    println!("ATTRIBUTE INPUT:   {}", input);
    input //.into_iter().collect() // emits invalid code if uncommented
}

#[proc_macro_derive(d)]
pub fn d(input: TokenStream) -> TokenStream {
    println!("DERIVE INPUT:      {}", input);
    TokenStream::new()
}



And I invoke them each from a 2018 crate:

repro/src/main.rs (edition = 2018)

#![allow(dead_code)]

type S = bool;

macro_rules! m {
    () => {
        procedural::m! {
            struct M($crate::S);
        }

        #[procedural::a]
        struct A($crate::S);

        #[derive(procedural::d)]
        struct D($crate::S);
    };
}

m!();

fn main() {}



The unexpected output is:

PROC MACRO INPUT:  struct M ( $crate :: S ) ;
ATTRIBUTE INPUT:   struct A(::S);
DERIVE INPUT:      struct D(::S);

The attribute macro and derive macro inputs are missing the $crate. The $crate is missing both from the to_string() representation as well as when iterating over the TokenStream.

If you uncomment the commented half line in procedural::a, the downstream crate fails to compile.

error[E0412]: cannot find type `S` in the crate root
help: possible candidate is found in another module, you can import it into scope
  |
1 | use crate::S;
  |

error: aborting due to previous error

I think this means procedural macros can't in general emit correct code when invoked from a 2018 edition crate with $crate in the input item.

If we really don't want to pass $crate through in the macro input, then it needs to decay to crate if it is from a 2018 crate. (You know how in 2015 we have $crate conceptually replaced by either nothing or the crate name, depending on whether the use is from the same crate or from a different crate. In 2018 we need this to be crate or the crate name.)

Mentioning @jimmycuadra who reported this in serde-rs/serde#1440.
Mentioning @petrochenkov @eddyb @alexcrichton for thoughts.

@petrochenkov petrochenkov added the A-macros Area: All kinds of macros (custom derive, macro_rules!, proc macros, ..) label Dec 8, 2018
@petrochenkov petrochenkov self-assigned this Dec 8, 2018
@petrochenkov
Copy link
Contributor

Similar to #55640.

I think we should start with removing the eliminate_crate_var hack and generating "$crate identifier" (#55640 (comment)) instead, then look whether something breaks in derives stabilized in 1.15 or not before thinking about alternatives.
(Self-assigning.)

@petrochenkov
Copy link
Contributor

Fixed in #56647

bors added a commit that referenced this issue Dec 20, 2018
Rework treatment of `$crate` in procedural macros

Important clarification: `$crate` below means "processed `$crate`" or "output `$crate`". In the input of a decl macro `$crate` is just two separate tokens, but in the *output of a decl macro* `$crate` is a single keyword identifier (#55640 (comment)).

First of all, this PR removes the `eliminate_crate_var` hack.
`$crate::foo` is no longer replaced with `::foo` or `::crate_name::foo` in the input of derive proc macros, it's passed to the macro instead with its precise span and hygiene data, and can be treated as any other path segment keyword (like `crate` or `self`) after that. (Note: `eliminate_crate_var` was never used for non-derive proc macros.)

This creates an annoying problem - derive macros still may stringify their input before processing and expect `$crate` survive that stringification and refer to the same crate (the Rust 1.15-1.29 way of doing things).
Moreover, the input of proc macro attributes and derives (but not fn-like proc macros) also effectively survives stringification before being passed to the macro (also for legacy implementation reasons).

So we kind of resurrect the `eliminate_crate_var` hack in reduced form, but apply it only to AST pretty-printing.
If an AST fragment is pretty-printed, the resulting *text* will have `$crate` replaced with `crate` or `::crate_name`. This should be enough to keep all the legacy cases working.

Closes #55640
Closes #56622
r? @ghost
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-macros Area: All kinds of macros (custom derive, macro_rules!, proc macros, ..)
Projects
None yet
Development

No branches or pull requests

2 participants