Skip to content

$crate incorrectly substituted in attribute macro invocation containing bang macro invocation #62325

Closed
@dtolnay

Description

@dtolnay

When an attribute macro input contains $ident, it is correctly substituted both inside and outside of a function-like macro invocation i.e. both of the $ident's in [u8; $ident + m!($ident)] would be substituted as expected.

But when an attribute macro input contains $crate, it is only substituted outside of a function-like macro. Inside, it incorrectly disintegrates into Punct('$'), Ident(crate) when the whole bang macro invocation is passed to an attribute macro (or derive macro). In the case of [u8; $crate::N + m!($crate::N)] the first $crate would be handled correctly but the second one would be passed incorrectly as two tokens. See where I put // WRONG in the output below.


Cargo.toml

[package]
name = "repro"
version = "0.0.0"
edition = "2018"
publish = false

[lib]
proc-macro = true

src/main.rs

#[macro_export]
macro_rules! repro {
    ($ident:tt) => {
        #[repro::repro]
        type T = [$crate::$ident; x!($crate::$ident)];
    };
}

repro!(N);

fn main() {}

src/lib.rs

extern crate proc_macro;
use proc_macro::TokenStream;

#[proc_macro_attribute]
pub fn repro(_args: TokenStream, input: TokenStream) -> TokenStream {
    println!("{:#?}", input);
    TokenStream::new()
}

Output of cargo check:

TokenStream [
    Ident {
        ident: "type",
        span: #0 bytes(0..0),
    },
    Ident {
        ident: "T",
        span: #0 bytes(0..0),
    },
    Punct {
        ch: '=',
        spacing: Alone,
        span: #0 bytes(0..0),
    },
    Group {
        delimiter: Bracket,
        stream: TokenStream [
            Ident {
                ident: "crate",
                span: #0 bytes(0..0),
            },
            Punct {
                ch: ':',
                spacing: Joint,
                span: #0 bytes(0..0),
            },
            Punct {
                ch: ':',
                spacing: Alone,
                span: #0 bytes(0..0),
            },
            Ident {
                ident: "N",
                span: #0 bytes(0..0),
            },
            Punct {
                ch: ';',
                spacing: Alone,
                span: #0 bytes(0..0),
            },
            Ident {
                ident: "x",
                span: #0 bytes(0..0),
            },
            Punct {
                ch: '!',
                spacing: Alone,
                span: #0 bytes(0..0),
            },
            Group {
                delimiter: Parenthesis,
                stream: TokenStream [
                    Punct {                              // WRONG
                        ch: '$',
                        spacing: Alone,
                        span: #0 bytes(0..0),
                    },
                    Ident {
                        ident: "crate",
                        span: #0 bytes(0..0),
                    },
                    Punct {
                        ch: ':',
                        spacing: Joint,
                        span: #0 bytes(0..0),
                    },
                    Punct {
                        ch: ':',
                        spacing: Alone,
                        span: #0 bytes(0..0),
                    },
                    Ident {
                        ident: "N",
                        span: #0 bytes(0..0),
                    },
                ],
                span: #0 bytes(0..0),
            },
        ],
        span: #0 bytes(0..0),
    },
    Punct {
        ch: ';',
        spacing: Alone,
        span: #0 bytes(0..0),
    },
]

Mentioning @petrochenkov who fixed my previous three $crate woes (#57089, #56622, #38016).

Metadata

Metadata

Assignees

Labels

A-macrosArea: All kinds of macros (custom derive, macro_rules!, proc macros, ..)C-bugCategory: This is a bug.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions