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

Feature Request: strip custom attributes on items #18

Open
colin-kiegel opened this issue Aug 4, 2016 · 2 comments
Open

Feature Request: strip custom attributes on items #18

colin-kiegel opened this issue Aug 4, 2016 · 2 comments

Comments

@colin-kiegel
Copy link

@killercup have a use case, were we could use some custom attribute-annotations of struct fields (like #[customDefault(42i32)]. In our case only our own custom-derive extension would be able to make sense of it.

As illustration imagine the following macro invocation to implement the Default trait - but with special_info : i32 = 42i32 instead of 0 for Channel. This is actually quite close to our use case.

custom_derive! {
    #[derive(CustomDefault)]
    struct Channel {
        id: Uuid,
        token: Authentication,
        #[customDefault(42i32)]
        special_info: i32,
    }
}

Now the question is, is there a feasible way of extending rust-custom-derive to filter out those custom attributes on items when writing the rust code?

  • one spontaneous idea would be an explicit blacklist, e.g. #[strip_attributes(customDefault)] in front of the struct itself
  • another spontaneous idea would be an implicit whitelist in rust-custom-derive itself (probably not so good?)

@DanielKeep: How do you feel about the idea in general?

@colin-kiegel
Copy link
Author

colin-kiegel commented Aug 6, 2016

two other ideas would be

  • invent a new convention (orthogonal to established rust coding styles), like all attributes with an underscore #[_xyz] (or at-sign #[@xyz]) get stripped (generic blacklist).
  • or use all identifiers of custom-derivations as explicit blacklist (in our example #[derive(..., CustomDefault)]: this would be CustomDefault, i.e. we could use #[CustomDefault] or #[CustomDefault(...)] inside the struct).

Currently I find the last option most attractive, i.e. blacklisting all identifiers from custom derivations. The only flaw is, that it will break the camelCase-style.

EDIT: explicit blacklists (first and fourth option) require comparisons on two matched identifiers $x:ident and $y:ident. To my current knowledge such comparisons are at least a bit tricky with macro_rules (I've definitely done it with higher-order-macros once, but this approach had some drawbacks).

@DanielKeep
Copy link
Owner

It is technically doable, but horrible in every respect to the point that I'm not sure it'd be worth it. There are two approaches:

  1. Parse the top-level item, filter the attributes, re-emit.
  2. Do a blind filtering of the input tokens without respect to structure.

The fundamental problem with both of these is that they require parsing the entire input. This would consume 𝓞(𝓷) recursion levels, where 𝓷 is the number of input tokens. This is on top of having to parse the attributes. Basically, every token you use in the item itself is a token you can't use in the derive attributes, and it's already relatively simple to hit the recursion limit. Currently, because it doesn't care what the item is, custom_derive! can get away with just consuming it all at once.

Also, to address your other ideas:

  • Having to specify which tokens to eliminate actually feels like a good idea, although it's getting into "could step on stuff the language itself introduces in the future" territory, which makes me nervous.
  • custom_derive! already has to keep a whitelist for built-in derive traits. I've actually been wanting to move to changing the syntax for custom derivations to macro-like syntax for a while now (e.g. #[derive(Thing!, Whatsis!(blah))]. This also presents a plausible way forward for stable custom derive in the language.
  • Underscore won't work: you cannot parse identifiers.

This touches on an RFC for official custom derive attributes I put together a while ago (but was blocked on stable parse_generics!): if an attribute has a ! on the end, it's actually a macro invocation and is used to transform the thing it's attached to.

Unfortunately, actually doing this is basically impossible in macro_rules!, so we can't really use that here.


In this particular case, the simplest thing is probably to just attach the necessary information to the derivation attribute instead. i.e.

#[derive(CustomDefault(special_info = 42))]
...

This requires less recursion because derivation macros cannot modify the item, so custom_derive! doesn't have to examine their output.


I'm leaving this open because, while I'm not convinced actually doing this right now makes much sense (lots of work, probably hugely inefficient), it is possible, and could be useful. How useful will probably change if parse_generics! gets accepted, or stable procedural macros happen.

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

2 participants