-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Use conventional meta-item syntax for attributes #2875
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
Conversation
@@ -0,0 +1,383 @@ | |||
//! Structs to receive attribute options for each supported syntax type. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This module needs a different name.
pub mod fields { | ||
use darling::util::Flag; | ||
|
||
macro_rules! field_trait { | ||
($name:ident, $return:ty, $field:ident) => { | ||
pub trait $name { | ||
fn $field(&self) -> $return; | ||
} | ||
}; | ||
} | ||
|
||
field_trait!(JsName, Option<&syn::LitStr>, js_name); | ||
field_trait!(SkipTypescript, Flag, skip_typescript); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried - and failed - to find a good way to make these driven by a single macro. It'd be very doable with a proc-macro, but having a proc-macro
crate that supports macro-support
...which is a proc-macro... felt too recursive.
impl<'a> From<&'a ImplItemMethod> for ast::OperationKind { | ||
fn from(opts: &'a ImplItemMethod) -> Self { | ||
let mut operation_kind = ast::OperationKind::Regular; | ||
if let Some(g) = &opts.getter { | ||
operation_kind = ast::OperationKind::Getter(g.deref().clone().explicit()); | ||
} | ||
if let Some(s) = &opts.setter { | ||
operation_kind = ast::OperationKind::Setter(s.deref().clone().explicit()); | ||
} | ||
if opts.indexing_getter.is_present() { | ||
operation_kind = ast::OperationKind::IndexingGetter; | ||
} | ||
if opts.indexing_setter.is_present() { | ||
operation_kind = ast::OperationKind::IndexingSetter; | ||
} | ||
if opts.indexing_deleter.is_present() { | ||
operation_kind = ast::OperationKind::IndexingDeleter; | ||
} | ||
|
||
operation_kind | ||
} | ||
} | ||
|
||
impl<'a> From<&'a ItemFn> for ast::OperationKind { | ||
fn from(opts: &'a ItemFn) -> Self { | ||
let mut operation_kind = ast::OperationKind::Regular; | ||
if let Some(g) = &opts.getter { | ||
operation_kind = ast::OperationKind::Getter(g.deref().clone().explicit()); | ||
} | ||
if let Some(s) = &opts.setter { | ||
operation_kind = ast::OperationKind::Setter(s.deref().clone().explicit()); | ||
} | ||
if opts.indexing_getter.is_present() { | ||
operation_kind = ast::OperationKind::IndexingGetter; | ||
} | ||
if opts.indexing_setter.is_present() { | ||
operation_kind = ast::OperationKind::IndexingSetter; | ||
} | ||
if opts.indexing_deleter.is_present() { | ||
operation_kind = ast::OperationKind::IndexingDeleter; | ||
} | ||
|
||
operation_kind | ||
} | ||
} | ||
|
||
impl<'a> From<&'a ForeignItemFn> for ast::OperationKind { | ||
fn from(opts: &'a ForeignItemFn) -> Self { | ||
let mut operation_kind = ast::OperationKind::Regular; | ||
if let Some(g) = &opts.getter { | ||
operation_kind = ast::OperationKind::Getter(g.deref().clone().explicit()); | ||
} | ||
if let Some(s) = &opts.setter { | ||
operation_kind = ast::OperationKind::Setter(s.deref().clone().explicit()); | ||
} | ||
if opts.indexing_getter.is_present() { | ||
operation_kind = ast::OperationKind::IndexingGetter; | ||
} | ||
if opts.indexing_setter.is_present() { | ||
operation_kind = ast::OperationKind::IndexingSetter; | ||
} | ||
if opts.indexing_deleter.is_present() { | ||
operation_kind = ast::OperationKind::IndexingDeleter; | ||
} | ||
|
||
operation_kind | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These shouldn't be copy-paste duplicated. A macro or intermediate trait would be better.
let attr_args = syn::parse_macro_input!(attr as syn::AttributeArgs); | ||
match wasm_bindgen_macro_support::expand(attr_args, input.into()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There may be a way to construct an AttributeArgs
from a proc_macro2::TokenStream
but I couldn't get it to work. If that does exist, the syn
dependency can be removed from this crate.
#[wasm_bindgen( | ||
extends = "::js_sys::Object", | ||
js_name = "AbortController", | ||
typescript_type = "AbortController" | ||
)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For anyone wondering how this diff is +55k, here's the answer; rustfmt
now moves these to new lines.
|
||
use self::fields::SkipTypescript; | ||
|
||
/// A JS namespace, which can be represented as `"value"` or `r#"["first", "second"]#"`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having to write #[wasm_bindgen(js_namespace = r#"["window", "document"]"#)]
is probably the single most unfortunate consequence of this change, and I think it can be avoided in most cases by accepting one of three alternate syntaxes:
// Approach 1
#[wasm_bindgen(js_namespace(window, document))]
// Approach 2
#[wasm_bindgen(js_namespace(window::document))]
// Approach 3
#[wasm_bindgen(js_namespace = "window.document")]
It would be straightforward to add any of these to impl FromMeta for JsNamespace
.
Thanks for the PR here, but unfortunately this project isn't really in a place to manage or merge large breaking changes. I don't think that there's going to be a viable way to land this, even if the repo is fully updated, any time soon. |
Makes sense: Getting that feedback was part of why I posted after 5 hours of work so I wouldn't be too deeply invested. A couple follow-up questions:
|
Sorry this is a big change I don't have the time to review, but overall making the proc-macro more readable or easier to understand/modify seems reasonable to me. Whether that could be achieved with the struct-per-input-item-type I'm not sure. Personally I would prefer just one syntax. I'm not wed to with or without quotes, but I'd rather not have both. |
Using the conventional form for attributes, which is compatible with rustfmt and syn's meta-item parsing. This change is breaking and substantial, but the necessary edits are largely mechanical. With meta-items, it becomes possible to offload attribute parsing and validation to `darling`. This offload fixes rustwasm#2874, as `darling` will automatically generate errors for unexpected meta-item names. This also makes it easier for people who are new to the codebase to understand how to add new options. This commit does not build: It changes the macro parser, but does not update the usage of the macro.
If I take out the diff parts caused by the breaking change, the new size is +554/-578; is that more tractable?
I have two ideas for this. Both of them start by adding Option 1
Option 2Create a proc-macro crate Option 1 is much easier and more maintainable, at the expense of traversing the attribute twice and sometimes doing more allocations. |
Option 1 actually works! I'm going to update the review shortly. |
I'm closing this in favor of #2880. |
Disclaimers
crates/macro-support
- the other files are changed to avoid any commits that won't build.darling
, the attribute-parsing library this uses. There's probably a way to do this withoutdarling
, but it's surprisingly difficult to maintain the level of error quality without it.Commit message
Using the conventional form for attributes, which is compatible with rustfmt and syn's meta-item parsing. This change is breaking and substantial, but the necessary edits are largely mechanical.
With meta-items, it becomes possible to offload attribute parsing and validation to
darling
. This offload fixes #2874, asdarling
will automatically generate errors for unexpected meta-item names. This also makes it easier for people who are new to the codebase to understand how to add new options.