rust-lang / rust-lang/rust

`#[cfg(...)]` attribute is incorrectly removed from a function parameter inside of the derive macro

Open
#130,004 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-cfg A-macros A-proc-macros C-bug T-compiler
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

I tried this code:

// my_macro proc-macro crate
use use proc_macro::TokenStream;

#[proc_macro_derive(Derive)]
pub fn derive(input: TokenStream) -> TokenStream {
    eprintln!("{input}");
    TokenStream::default()
}

// lib.rs
#[derive(my_macro::Derive)]
enum Enum {
    X = {
        fn foo(#[cfg(any())] arg1: (), arg2: ()) {}
        0
    },
}

I'm not sure what exactly I expected to see in the stderr output when I compile the crate. Maybe this with the #[cfg(...)] attribute left intact on the function

enum Enum { X = { fn foo(#[cfg(any())] arg1: (), arg2: ()) {} 0 }, }

or this with the #[cfg(...)] attribute completely removed togther with the syntax it was placed on:

enum Enum { X = { fn foo(arg2: ()) {} 0 }, }

Instead, the output is invalid rust code:

enum Enum { X = { fn foo(, arg2: ()) {} 0 }, }

Notice that the #[cfg(...)] was removed, but... the coma after the function parameter was not removed. This output results in an invalid token tree not parsable by syn

Meta

rustc --version --verbose:

rustc 1.81.0 (eeb90cda1 2024-09-04)
binary: rustc
commit-hash: eeb90cda1969383f56a2637cbd3037bdf598841c
commit-date: 2024-09-04
host: x86_64-unknown-linux-gnu
release: 1.81.0
LLVM version: 18.1.7

Context

Why would I ever write such code? I'd like to support conditional compilation with my proc macro that generates a builder from a function called bon. But... since it's a proc macro attribute, the #[cfg(...)] and #[cfg_attr(...)] attributes aren't automatically removed when the macro runs.

So I thought I'd workaround it by delegating to a derive macro that accepts the function item in the expression position as the first item of the block for the default value of the enum's variant. This is because derive macros benefit from automatic expansion of #[cfg(...)/cfg_attr(...)] attributes before they run. Thus, by using a derive macro wrapper I could get the results of cfg evaluation this way.

I could work around that by passing the item as an argument to the proc-macro derive... But IDEs and Rust Analyzer don't work this way. They somehow mess up span information.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the issue with the proc-macro derive and enum example from the report, then inspect the compiler's derive-macro expansion handling for cfg attributes on function parameters. Verify the emitted token stream remains valid Rust and is parsable by syn, with the parameter and separator handled consistently when the cfg condition is removed.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.