Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Should syn have convenience functions for (derive) macros? #1037

Closed
m-ou-se opened this issue Jun 9, 2021 · 3 comments
Closed

Should syn have convenience functions for (derive) macros? #1037

m-ou-se opened this issue Jun 9, 2021 · 3 comments

Comments

@m-ou-se
Copy link
Contributor

m-ou-se commented Jun 9, 2021

There's a few things I often need while writing a derive macro, which I keep writing small helper functions for. I'm wondering if these should be part of syn, or if they should go in a separate crate.

An example:

I often want to iterate over the Members of a list of Fields, such that Unit results in an empty iterator, Tuple results in Unnamed(0), Unnamed(1), etc., and Struct results in Named("field1"), Named("field2") etc. This makes it easy to use e.g. quote!{ #structname { #( #member: Trait::thing(), )* } } and have it work for all three kinds of structs.

(Concrete use case example: It'd reduce this match to just a single case.)

This doesn't seem to exactly fit with the rest of syn, because the tuple indexes (0, 1, ..) don't occur in the source code, but are generated for the convenience of proc macros.

A second example:

An .expect_struct() on Data that would return a Result<DataStruct>, containing a nice error in case of an enum or union (with the span set to the enum or union token). (Or a .expect_unit() on Fields, etc.)

I have a few more examples like this. But I first wanted to ask the general question: Would syn be the right place for functions like this, or should syn only focus on parsing and keep other functionality to a minimum?

@Progdrasil
Copy link

One that I had to use recently was on Data to get the span of the token, such as

pub trait ToSpan {
    fn to_span(self) -> Span;
}

impl ToSpan for Data {
    fn to_span(self) -> Span {
        match self {
            Data::Struct(d) => d.struct_token.span(),
            Data::Enum(d) => d.enum_token.span(),
            Data::Union(d) => d.union_token.span(),
        }
    }
}

Now I realize that implementing syn::spanned::Spanned on Data is quite complex and should give the entire definition, a helper method Data.token_span() or something like that would be really great.

@TedDriggs
Copy link
Contributor

I found myself reaching for a number of conveniences that were outside syn's stated scope, which led me to create darling. In this particular case, you may find darling::ast::Data helpful.

@dtolnay
Copy link
Owner

dtolnay commented Jan 23, 2023

They need to go in a separate crate. The suggestion of adding various conveniences would make this crate effectively unbounded in scope which is not appealing to me or the users who rely on it to compile fast.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants