Skip to content

Proc Macros sometimes recieve unnecessary None Groups #21379

@chorman0773

Description

@chorman0773

rust-analyzer version: rust-analyzer version: 0.4.2738-standalone [[redacted home dir]/.vscode-insiders/extensions/rust-lang.rust-analyzer-0.4.2738-linux-x64/server/rust-analyzer]

rustc version: rustc 1.93.0-nightly (b84478a1c 2025-11-30)

editor or extension: VSCode pre-release (also occurs on release version)

relevant settings: N/A

code snippet to reproduce:

use test_helper::Foo;

#[derive(Foo)]
pub struct Bar {
    x: u8,
}

fn main() {
    println!("Hello, world!");
}

// crate test_helper
// --proc-macro

use proc_macro::{Group, Ident, Literal, Punct, Span, TokenStream, TokenTree};
#[proc_macro_derive(Foo)]
pub fn foo(attrs: TokenStream) -> TokenStream {
    let tt_str = format!("{attrs:?}");

    [
        TokenTree::Ident(Ident::new("core", Span::mixed_site())),
        TokenTree::Punct(Punct::new(':', proc_macro::Spacing::Joint)),
        TokenTree::Punct(Punct::new(':', proc_macro::Spacing::Alone)),
        TokenTree::Ident(Ident::new("compile_error", Span::mixed_site())),
        TokenTree::Punct(Punct::new('!', proc_macro::Spacing::Alone)),
        TokenTree::Group(Group::new(
            proc_macro::Delimiter::Parenthesis,
            [TokenTree::Literal(Literal::string(&tt_str))]
                .into_iter()
                .collect(),
        )),
        TokenTree::Punct(Punct::new(';', proc_macro::Spacing::Alone)),
    ]
    .into_iter()
    .collect()
}

The behaviour of the above code is as follows:

On rustc, it produces a compile_error with roughly the following output (which is expected per the reference):

error: TokenStream [Ident { ident: "pub", span: #0 bytes(38..41) }, Ident { ident: "struct", span: #0 bytes(42..48) }, Ident { ident: "Bar", span: #0 bytes(49..52) }, Group { delimiter: Brace, stream: TokenStream [Ident { ident: "x", span: #0 bytes(59..60) }, Punct { ch: ':', spacing: Alone, span: #0 bytes(60..61) }, Ident { ident: "u8", span: #0 bytes(62..64) }, Punct { ch: ',', spacing: Alone, span: #0 bytes(64..65) }], span: #0 bytes(53..67) }]
 --> src/main.rs:3:10
  |
3 | #[derive(Foo)]
  |          ^^^
  |
  = note: this error originates in the derive macro `Foo` (in Nightly builds, run with -Z macro-backtrace for more info)

On rust-analyzer, it produces a different compile_error, which demonstrates a different (and incorrect) input TokenStream:

TokenStream [Group { delimiter: None, stream: TokenStream [Ident { ident: "pub", span: /* simplified */), ctx: SyntaxContext(4294967036) } }, Ident { ident: "struct", span: /* simplified */), ctx: SyntaxContext(4294967036) } }, Ident { ident: "Bar", span: /* simplified */), ctx: SyntaxContext(4294967036) } }, Group { delimiter: Brace, stream: TokenStream [Ident { ident: "x", span: /* simplified */), ctx: SyntaxContext(4294967036) } }, Punct { ch: ':', spacing: Alone, span: /* simplified */), ctx: SyntaxContext(4294967036) } }, Ident { ident: "u8", span: /* simplified */), ctx: SyntaxContext(4294967036) } }, Punct { ch: ',', spacing: Alone, span: /* simplified */), ctx: SyntaxContext(4294967036) } }], span: /* simplified */), ctx: SyntaxContext(4294967036) } }], span: /* simplified */), ctx: SyntaxContext(4294967036) } }]

(Span markers simplified manually to avoid unnecessary clutter).

In the above, rustc passes the Foo definition directly, while r-a encloses it in a None delimited group (which is both unnecessary and incorrect - None groups are for delimiting the contents of expanded non-literal metavariables (like $expr, $item, or $stmt) only).

Note that while this is a contrived example and only meaningfully differs in compiler diagnostics, the added None group where not expected can affect the ability to parse some of the input. As an example, I get unsupported type errors using the bitfield_struct crate on rust-analyzer only, which I can only assume is caused by the same bug and the macro not being forgiving about None groups arround the type names (which it expects to be literal identifiers).

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions