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

Make getters, setters, and constructors compiler errors for enums #4278

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
51 changes: 46 additions & 5 deletions crates/backend/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,13 @@ impl ToTokens for ast::Struct {
let unwrap_fn = Ident::new(&shared::unwrap_function(&name_str), Span::call_site());
let wasm_bindgen = &self.wasm_bindgen;
(quote! {
#[automatically_derived]
impl #wasm_bindgen::marker::SupportsConstructor for #name {}
#[automatically_derived]
impl #wasm_bindgen::marker::SupportsInstanceProperty for #name {}
#[automatically_derived]
impl #wasm_bindgen::marker::SupportsStaticProperty for #name {}

#[automatically_derived]
impl #wasm_bindgen::describe::WasmDescribe for #name {
fn describe() {
Expand Down Expand Up @@ -780,12 +787,43 @@ impl TryToTokens for ast::Export {
let nargs = self.function.arguments.len() as u32;
let attrs = &self.function.rust_attrs;

let start_check = if self.start {
quote! { const _ASSERT: fn() = || -> #projection::Abi { loop {} }; }
} else {
quote! {}
let mut checks = Vec::new();
if self.start {
checks.push(quote! { const _ASSERT: fn() = || -> #projection::Abi { loop {} }; });
};

if let Some(class) = self.rust_class.as_ref() {
// change span of class, so it points to the location of the
// function causing the assert to fail
let mut class = class.clone();
class.set_span(self.rust_name.span());
RunDevelopment marked this conversation as resolved.
Show resolved Hide resolved

match &self.method_kind {
ast::MethodKind::Constructor => {
checks.push(quote! {
const fn assert_supports_constructor<T: #wasm_bindgen::marker::SupportsConstructor>() {}
assert_supports_constructor::<#class>();
});
}
ast::MethodKind::Operation(operation) => match operation.kind {
ast::OperationKind::Getter(_) | ast::OperationKind::Setter(_) => {
if operation.is_static {
checks.push(quote! {
const fn assert_supports_static_property<T: #wasm_bindgen::marker::SupportsStaticProperty>() {}
assert_supports_static_property::<#class>();
});
} else {
checks.push(quote! {
const fn assert_supports_instance_property<T: #wasm_bindgen::marker::SupportsInstanceProperty>() {}
assert_supports_instance_property::<#class>();
});
}
}
_ => {}
},
}
}

(quote! {
#[automatically_derived]
const _: () = {
Expand All @@ -796,7 +834,10 @@ impl TryToTokens for ast::Export {
)]
#[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
pub unsafe extern "C" fn #generated_name(#(#args),*) -> #wasm_bindgen::convert::WasmRet<#projection::Abi> {
#start_check
#[automatically_derived]
const _: () = {
#(#checks)*
};

let #ret = #call;
#convert_ret
Expand Down
82 changes: 82 additions & 0 deletions crates/macro/ui-tests/unsupported-options.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub struct RustStruct {
data: u32,
}

#[wasm_bindgen]
impl RustStruct {
pub fn instance_method(&self) {}
fn priv_instance_method(&self) {}
pub fn static_method() {}

#[wasm_bindgen(constructor)]
pub fn new() -> Self {
Self { data: 0 }
}

#[wasm_bindgen(getter)]
pub fn prop(self) -> u32 {
32
}
#[wasm_bindgen(setter)]
pub fn set_prop(self, _value: u32) {}

#[wasm_bindgen(getter)]
pub fn static_prop() -> u32 {
32
}
#[wasm_bindgen(setter)]
pub fn set_static_prop(_value: u32) {}

#[wasm_bindgen(indexing_getter)]
pub fn indexing_getter(self) -> u32 {
32
}
#[wasm_bindgen(indexing_setter)]
pub fn indexing_setter(self, _value: u32) {}
#[wasm_bindgen(indexing_deleter)]
pub fn indexing_deleter(self, _value: u32) {}
}

#[wasm_bindgen]
pub enum RustEnum {
A = 0,
B = 1,
}

#[wasm_bindgen]
impl RustEnum {
pub fn instance_method(self) {}
fn priv_instance_method(self) {}
pub fn static_method() {}

#[wasm_bindgen(constructor)]
pub fn new() -> Self {
Self::A
}

#[wasm_bindgen(getter)]
pub fn prop(self) -> u32 {
32
}
#[wasm_bindgen(setter)]
pub fn set_prop(self, _value: u32) {}

#[wasm_bindgen(getter)]
pub fn static_prop() -> u32 {
32
}
#[wasm_bindgen(setter)]
pub fn set_static_prop(_value: u32) {}
}

pub struct NonWasmType;

#[wasm_bindgen]
impl NonWasmType {
pub fn static_method() {}
}

fn main() {}
80 changes: 80 additions & 0 deletions crates/macro/ui-tests/unsupported-options.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
error[E0277]: JavaScript constructors are not supported for `RustEnum`
--> ui-tests/unsupported-options.rs:56:12
|
56 | pub fn new() -> Self {
| ^^^ this function cannot be the constructor of `RustEnum`
|
= help: the trait `SupportsConstructor` is not implemented for `RustEnum`
= note: `#[wasm_bindgen(constructor)]` is generally only supported for `struct`s with `#[wasm_bindgen]` and cannot be used for `enum`s.
= note: Consider removing the `constructor` option and using a regular static method instead.
= help: the trait `SupportsConstructor` is implemented for `RustStruct`
note: required by a bound in `__wasm_bindgen_generated_RustEnum_new::_::assert_supports_constructor`
--> ui-tests/unsupported-options.rs:49:1
|
49 | #[wasm_bindgen]
| ^^^^^^^^^^^^^^^ required by this bound in `assert_supports_constructor`
= note: this error originates in the attribute macro `wasm_bindgen` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: JavaScript instance getters and setters are not supported for `RustEnum`
--> ui-tests/unsupported-options.rs:61:12
|
61 | pub fn prop(self) -> u32 {
| ^^^^ this method cannot be a getter or setter for `RustEnum`
|
= help: the trait `SupportsInstanceProperty` is not implemented for `RustEnum`
= note: `#[wasm_bindgen(getter)]` and `#[wasm_bindgen(setter)]` are generally only supported for `struct`s with `#[wasm_bindgen]`. They cannot be used for `enum`s.
= help: the trait `SupportsInstanceProperty` is implemented for `RustStruct`
note: required by a bound in `__wasm_bindgen_generated_RustEnum_prop::_::assert_supports_instance_property`
--> ui-tests/unsupported-options.rs:49:1
|
49 | #[wasm_bindgen]
| ^^^^^^^^^^^^^^^ required by this bound in `assert_supports_instance_property`
= note: this error originates in the attribute macro `wasm_bindgen` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: JavaScript instance getters and setters are not supported for `RustEnum`
--> ui-tests/unsupported-options.rs:65:12
|
65 | pub fn set_prop(self, _value: u32) {}
| ^^^^^^^^ this method cannot be a getter or setter for `RustEnum`
|
= help: the trait `SupportsInstanceProperty` is not implemented for `RustEnum`
= note: `#[wasm_bindgen(getter)]` and `#[wasm_bindgen(setter)]` are generally only supported for `struct`s with `#[wasm_bindgen]`. They cannot be used for `enum`s.
= help: the trait `SupportsInstanceProperty` is implemented for `RustStruct`
note: required by a bound in `__wasm_bindgen_generated_RustEnum_set_prop::_::assert_supports_instance_property`
--> ui-tests/unsupported-options.rs:49:1
|
49 | #[wasm_bindgen]
| ^^^^^^^^^^^^^^^ required by this bound in `assert_supports_instance_property`
= note: this error originates in the attribute macro `wasm_bindgen` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: JavaScript static getters and setters are not supported for `RustEnum`
--> ui-tests/unsupported-options.rs:68:12
|
68 | pub fn static_prop() -> u32 {
| ^^^^^^^^^^^ this static function cannot be a static getter or setter on `RustEnum`
|
= help: the trait `SupportsStaticProperty` is not implemented for `RustEnum`
= note: `#[wasm_bindgen(getter)]` and `#[wasm_bindgen(setter)]` are generally only supported for `struct`s with `#[wasm_bindgen]`. They cannot be used for `enum`s.
= help: the trait `SupportsStaticProperty` is implemented for `RustStruct`
note: required by a bound in `__wasm_bindgen_generated_RustEnum_static_prop::_::assert_supports_static_property`
--> ui-tests/unsupported-options.rs:49:1
|
49 | #[wasm_bindgen]
| ^^^^^^^^^^^^^^^ required by this bound in `assert_supports_static_property`
= note: this error originates in the attribute macro `wasm_bindgen` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: JavaScript static getters and setters are not supported for `RustEnum`
--> ui-tests/unsupported-options.rs:72:12
|
72 | pub fn set_static_prop(_value: u32) {}
| ^^^^^^^^^^^^^^^ this static function cannot be a static getter or setter on `RustEnum`
|
= help: the trait `SupportsStaticProperty` is not implemented for `RustEnum`
= note: `#[wasm_bindgen(getter)]` and `#[wasm_bindgen(setter)]` are generally only supported for `struct`s with `#[wasm_bindgen]`. They cannot be used for `enum`s.
= help: the trait `SupportsStaticProperty` is implemented for `RustStruct`
note: required by a bound in `__wasm_bindgen_generated_RustEnum_set_static_prop::_::assert_supports_static_property`
--> ui-tests/unsupported-options.rs:49:1
|
49 | #[wasm_bindgen]
| ^^^^^^^^^^^^^^^ required by this bound in `assert_supports_static_property`
= note: this error originates in the attribute macro `wasm_bindgen` (in Nightly builds, run with -Z macro-backtrace for more info)
7 changes: 4 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec::Vec;
use core::convert::TryFrom;
use core::marker;
use core::marker::PhantomData;
use core::mem;
use core::ops::{
Add, BitAnd, BitOr, BitXor, Deref, DerefMut, Div, Mul, Neg, Not, Rem, Shl, Shr, Sub,
Expand Down Expand Up @@ -71,6 +71,7 @@ pub use wasm_bindgen_macro::link_to;
pub mod closure;
pub mod convert;
pub mod describe;
pub mod marker;
mod link;

mod cast;
Expand All @@ -92,7 +93,7 @@ if_std! {
/// but for now it may be slightly slow.
pub struct JsValue {
idx: u32,
_marker: marker::PhantomData<*mut u8>, // not at all threadsafe
_marker: PhantomData<*mut u8>, // not at all threadsafe
}

const JSIDX_OFFSET: u32 = 128; // keep in sync with js/mod.rs
Expand All @@ -119,7 +120,7 @@ impl JsValue {
const fn _new(idx: u32) -> JsValue {
JsValue {
idx,
_marker: marker::PhantomData,
_marker: PhantomData,
}
}

Expand Down
61 changes: 61 additions & 0 deletions src/marker.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//! This module contains marker traits used by `wasm-bindgen` to verify its
//! generated code.
//!
//! # ⚠️ Unstable
//!
//! This is an internal module, no stability guarantees are provided. Use at
//! your own risk.

/// Marker trait for types that support `#[wasm_bindgen(constructor)]`.
///
/// **DO NOT** implement this trait manually. It is implemented automatically
/// for types that support constructors.
///
/// # ⚠️ Unstable
///
/// This is part of the internal [`convert`](crate::marker) module, **no
/// stability guarantees** are provided. Use at your own risk. See its
/// documentation for more details.
#[diagnostic::on_unimplemented(
message = "JavaScript constructors are not supported for `{Self}`",
label = "this function cannot be the constructor of `{Self}`",
note = "`#[wasm_bindgen(constructor)]` is generally only supported for `struct`s with `#[wasm_bindgen]` and cannot be used for `enum`s.",
note = "Consider removing the `constructor` option and using a regular static method instead."
)]
pub trait SupportsConstructor {}

/// Marker trait for types that support `#[wasm_bindgen(getter)]` or
/// `#[wasm_bindgen(Setter)]` on instance methods.
///
/// **DO NOT** implement this trait manually. It is implemented automatically
/// for types that support instance properties.
///
/// # ⚠️ Unstable
///
/// This is part of the internal [`convert`](crate::marker) module, **no
/// stability guarantees** are provided. Use at your own risk. See its
/// documentation for more details.
#[diagnostic::on_unimplemented(
message = "JavaScript instance getters and setters are not supported for `{Self}`",
label = "this method cannot be a getter or setter for `{Self}`",
note = "`#[wasm_bindgen(getter)]` and `#[wasm_bindgen(setter)]` are generally only supported for `struct`s with `#[wasm_bindgen]`. They cannot be used for `enum`s."
)]
pub trait SupportsInstanceProperty {}

/// Marker trait for types that support `#[wasm_bindgen(getter)]` or
/// `#[wasm_bindgen(Setter)]` on static methods.
///
/// **DO NOT** implement this trait manually. It is implemented automatically
/// for types that support static properties.
///
/// # ⚠️ Unstable
///
/// This is part of the internal [`convert`](crate::marker) module, **no
/// stability guarantees** are provided. Use at your own risk. See its
/// documentation for more details.
#[diagnostic::on_unimplemented(
message = "JavaScript static getters and setters are not supported for `{Self}`",
label = "this static function cannot be a static getter or setter on `{Self}`",
note = "`#[wasm_bindgen(getter)]` and `#[wasm_bindgen(setter)]` are generally only supported for `struct`s with `#[wasm_bindgen]`. They cannot be used for `enum`s."
)]
pub trait SupportsStaticProperty {}
Loading