Skip to content
This repository was archived by the owner on Jul 9, 2023. It is now read-only.
This repository was archived by the owner on Jul 9, 2023. It is now read-only.

Procedural macro reimplementation of quote! to resolve longstanding limitations #8

Closed
@dtolnay

Description

@dtolnay

Originally filed as dtolnay/quote#82 but I would like this to begin its life as a separate library.

The current macro_rules-based quote macro has the limitation that duplicate interpolations inside of a repetition are not allowed. quote! { #a #a } works but quote! { #(#a #a)* } does not work. The reason boils down to macro_rules macros having no way to determine that two identifiers are equal.

Some background on the quote macro:

  • quote! { #a #a } expands to:

    {
        let mut _s = TokenStream::new();
        let _span = Span::call_site();
        ToTokens::to_tokens(&a, &mut _s);
        ToTokens::to_tokens(&a, &mut _s);
        _s
    }
  • quote! { #(#a)* } expands to:

    {
        let mut _s = TokenStream::new();
        let _span = Span::call_site();
        for a in a {
            ToTokens::to_tokens(&a, &mut _s);
        }
        _s
    }
  • quote! { #(#a #b)* } expands to:

    {
        let mut _s = TokenStream::new();
        let _span = Span::call_site();
        for (a, b) in a.into_iter().zip(b) {
            ToTokens::to_tokens(&a, &mut _s);
            ToTokens::to_tokens(&b, &mut _s);
        }
        _s
    }
  • quote! { #(#a #a)* } expands to:

    {
        let mut _s = TokenStream::new();
        let _span = Span::call_site();
        for (a, a) in a.into_iter().zip(a) {
            ToTokens::to_tokens(&a, &mut _s);
            ToTokens::to_tokens(&a, &mut _s);
        }
        _s
    }

That last expansion is illegal so the quote invocation fails.

error[E0416]: identifier `a` is bound more than once in the same pattern
  --> src/main.rs:12:17
   |
12 |         for (a, a) in a.into_iter().zip(a) {
   |                 ^ used in a pattern more than once

In an implementation as a procedural macro we would want that invocation to expand instead as:

{
    let mut _s = TokenStream::new();
    let _span = Span::call_site();
    for a in a {
        ToTokens::to_tokens(&a, &mut _s);
        ToTokens::to_tokens(&a, &mut _s);
    }
    _s
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions