PoignardAzur / venial

"A very small syn"
MIT License
192 stars 9 forks source link

Failure to parse macro-generated struct #35

Closed jcaesar closed 9 months ago

jcaesar commented 1 year ago

(This issue was pointed out to me by @dtolnay on https://github.com/jcaesar/structstruck/issues/1)

When parsing a rule macro-generated struct (e.g. one where the visibility comes from a macro variable), some tokens may be in delimiter-less groups, e.g.

TokenStream [
    Group {
        delimiter: None,
        stream: TokenStream [
            Ident {
                ident: "pub",
                span: #0 bytes(196..199),
            },
        ],
        span: #4 bytes(85..96),
    },
    Ident {
        ident: "struct",
        span: #4 bytes(97..103),
    },
    Ident {
        ident: "Struct",
        span: #4 bytes(104..110),
    },
    Group {…}
  }
]

This fails to parse with panic message cannot parse declaration: expected keyword struct/enum/union/fn, found token Group { delimiter: None, stream: TokenStream [Ident { ident: "pub", span: #0 bytes(196..199) }], span: #4 bytes(85..96) }.

I'm not sure what the correct way of handling this is. Delimiter-less groups can be important in some situations, but I don't think there are any in struct parsing. If that holds true, you could just normalize such groups away. (It may be very weird to have something like foo $blubb Bar with $blubb substituted by : Hax, hax2 : — but I don't see why it should be forbidden.)
It might be worth checking how syn handles this kind of input, it definitely doesn't panic on the example in my gist.

PoignardAzur commented 1 year ago

If that holds true, you could just normalize such groups away. (It may be very weird to have something like foo $blubb Bar with $blubb substituted by : Hax, hax2 : — but I don't see why it should be forbidden.) It might be worth checking how syn handles this kind of input, it definitely doesn't panic on the example in my gist.

Interestingly, syn does seem to reject the case where a delimiter-less group spans "parsing rules" (not sure what the exact term would be). Eg see this (tweaked from your gist):

macro_rules! repro {
    ($visibility:vis) => {
        macros::non_repro_syn! {
            $visibility Struct {
                $visibility member: Type,
            }
        }
    };
}

repro!(pub struct);

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

Honestly, I think we could get away with a best-effort approach on this. The use-case is pretty niche.

On one hand, normalization might be easier to write. On the other hand, manually adding cases for delimiter-less groups might be more performant. I don't know.

jcaesar commented 1 year ago

(I was surprised to find that

macro_rules! repro {
    ($name:ident : $($field:ident),+ : $type:ty) => {
        macros::repro! {
            struct $name {
                $($field: $type),+
            }
        }
    };
}

repro! {ABC : a,b,c : bool}

already works. Can't think of much else I'd definitely want to work.)

PoignardAzur commented 9 months ago

Closing until someone finds a use-case where this is a problem.