You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Some custom derives have helper attributes that let the user pass through expressions into the generated code to customize behavior. This is usually done by parsing some tokens from the attribute as a single syn::Expr or as comma-separated sequence of them. By far the heaviest user of this is clap_derive (example) but it also occurs at a smaller scale in other derives such as darling or educe. In many simple cases this works fine with only the "derive" feature, but some kinds of expressions can only be parsed with "full" enabled. When that distinction matters, there's no great option at the moment:
If the derive itself enables syn/full, that carries a build-time cost for all users, at least in workspaces where nothing else enables the feature. Most of that cost is unnecessary because these derives only need to deal with expressions, rather than the full suite of items and statements gated under syn/full.
If syn/full is not enabled by default, user experience is sub-par: some expressions will be rejected with the helpful error message unsupported expression; enable syn's feature=["full"] but others get a rather less helpful error message. Some derives try this anyway (e.g., num-derive has feature "complex-expressions" which forwards to syn/full) but in other cases the unsupported expressions are so commonly needed that it's not much better than enabling syn/full unconditionally.
Wrapping the expression in a string (num_args = "0..=1" as opposed to num_args = 0..=1 ) could be abused to skip expression parsing entirely, but is much less convenient for users, defers all validation to when rustc parses the derive's output, and (as far as I know) gives worse diagnostics for syntax errors in these strings because there's no way to turn the string contents into tokens with individualized, accurate spans. I believe this style of attribute mostly exist for historical reasons (limitations in earlier Rust versions) and not because anyone considers it a good idea today.
Moving more expression variants out of "full" and into "derive" would reduce the problem, but of course this adds a bunch of types and parsing code to everyone's syn builds, even when they're not needed. A simpler solution is to note that these expressions are usually passed through without being inspected, so parsing them as Expr::Verbatim is fine for at least some derives. Current syn versions already have some code that can do this (the expression scanner) included in non-full builds, but it's not exported and only used for parsing discriminants in enums (#1513). Parsing as syn::Expr doesn't hit that code path so it fails on ranges, arrays, tuples, closures, and other full-exclusive expression variants when "full" is not enabled.
I'm open to other solutions, but right now exposing the "parse any expression, as Expr::Verbatim if necessary" capability in some form seems like the best way to me. Per discussion in clap-rs/clap#5657 this could allow the next major version of Clap to drop syn/full (no guarantees of course), which would slightly but measurably improve build times for many CLI programs. For me clap is the motivating use case but other derives may also benefit (educe seems plausible, but darling currently inspects the parsed Expr in some cases).
The text was updated successfully, but these errors were encountered:
Some custom derives have helper attributes that let the user pass through expressions into the generated code to customize behavior. This is usually done by parsing some tokens from the attribute as a single
syn::Expr
or as comma-separated sequence of them. By far the heaviest user of this is clap_derive (example) but it also occurs at a smaller scale in other derives such as darling or educe. In many simple cases this works fine with only the "derive" feature, but some kinds of expressions can only be parsed with "full" enabled. When that distinction matters, there's no great option at the moment:unsupported expression; enable syn's feature=["full"]
but others get a rather less helpful error message. Some derives try this anyway (e.g., num-derive has feature "complex-expressions" which forwards to syn/full) but in other cases the unsupported expressions are so commonly needed that it's not much better than enabling syn/full unconditionally.num_args = "0..=1"
as opposed tonum_args = 0..=1
) could be abused to skip expression parsing entirely, but is much less convenient for users, defers all validation to when rustc parses the derive's output, and (as far as I know) gives worse diagnostics for syntax errors in these strings because there's no way to turn the string contents into tokens with individualized, accurate spans. I believe this style of attribute mostly exist for historical reasons (limitations in earlier Rust versions) and not because anyone considers it a good idea today.Moving more expression variants out of "full" and into "derive" would reduce the problem, but of course this adds a bunch of types and parsing code to everyone's syn builds, even when they're not needed. A simpler solution is to note that these expressions are usually passed through without being inspected, so parsing them as
Expr::Verbatim
is fine for at least some derives. Current syn versions already have some code that can do this (the expression scanner) included in non-full builds, but it's not exported and only used for parsing discriminants in enums (#1513). Parsing assyn::Expr
doesn't hit that code path so it fails on ranges, arrays, tuples, closures, and other full-exclusive expression variants when "full" is not enabled.I'm open to other solutions, but right now exposing the "parse any expression, as
Expr::Verbatim
if necessary" capability in some form seems like the best way to me. Per discussion in clap-rs/clap#5657 this could allow the next major version of Clap to drop syn/full (no guarantees of course), which would slightly but measurably improve build times for many CLI programs. For me clap is the motivating use case but other derives may also benefit (educe seems plausible, but darling currently inspects the parsedExpr
in some cases).The text was updated successfully, but these errors were encountered: