diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b5e9de0542a..60567280971 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -29,7 +29,7 @@ jobs: - run: rustup update --no-self-update stable && rustup default stable - run: rustup component add rustfmt - run: cargo fmt --all -- --check - + # Check TOML style by using Taplo. taplo: name: Taplo @@ -325,7 +325,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - run: rustup update --no-self-update 1.76.0 && rustup default 1.76.0 + - run: rustup update --no-self-update 1.78.0 && rustup default 1.78.0 - run: cargo test -p wasm-bindgen-macro - run: cargo test -p wasm-bindgen-test-macro diff --git a/Cargo.toml b/Cargo.toml index 873238e5999..bdbf869aad3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,12 +22,20 @@ features = ["serde-serialize"] test = false [features] -default = ["spans", "std"] +default = ["spans", "std", "diagnostic"] enable-interning = ["std"] serde-serialize = ["serde", "serde_json", "std"] spans = ["wasm-bindgen-macro/spans"] std = ["wasm-bindgen-macro/std", "once_cell/std"] +# Enables the use of Rust's `diagnostic` macro for several traits to provide +# better error messages when those traits are not implemented. +# +# Diagnostic messages are only available since Rust 1.78. Enabling this feature +# for older compilers will NOT improve error message and will NOT result in a +# compilation error. +diagnostic = ["rustversion"] + # Whether or not the `#[wasm_bindgen]` macro is strict and generates an error on # all unused attributes strict-macro = ["wasm-bindgen-macro/strict-macro"] @@ -43,6 +51,7 @@ xxx_debug_only_print_generated_code = ["wasm-bindgen-macro/xxx_debug_only_print_ [dependencies] cfg-if = "1.0.0" once_cell = { version = "1.12", default-features = false } +rustversion = { version = "1.0", optional = true } serde = { version = "1.0", optional = true } serde_json = { version = "1.0", optional = true } wasm-bindgen-macro = { path = "crates/macro", version = "=0.2.95", default-features = false } diff --git a/crates/backend/src/codegen.rs b/crates/backend/src/codegen.rs index d899d9916fa..84e9cbf45cd 100644 --- a/crates/backend/src/codegen.rs +++ b/crates/backend/src/codegen.rs @@ -226,6 +226,13 @@ impl ToTokens for ast::Struct { let wasm_bindgen = &self.wasm_bindgen; let maybe_no_coverage = coverage(); (quote! { + #[automatically_derived] + impl #wasm_bindgen::__rt::marker::SupportsConstructor for #name {} + #[automatically_derived] + impl #wasm_bindgen::__rt::marker::SupportsInstanceProperty for #name {} + #[automatically_derived] + impl #wasm_bindgen::__rt::marker::SupportsStaticProperty for #name {} + #[automatically_derived] impl #wasm_bindgen::describe::WasmDescribe for #name { fn describe() { @@ -783,12 +790,44 @@ 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() { + // little helper function to make sure the check points to the + // location of the function causing the assert to fail + let mut add_check = |token_stream| { + checks.push(respan(token_stream, &self.rust_name)); + }; + + match &self.method_kind { + ast::MethodKind::Constructor => { + add_check(quote! { + struct CheckSupportsConstructor(T); + let _: CheckSupportsConstructor<#class>; + }); + } + ast::MethodKind::Operation(operation) => match operation.kind { + ast::OperationKind::Getter(_) | ast::OperationKind::Setter(_) => { + if operation.is_static { + add_check(quote! { + struct CheckSupportsStaticProperty(T); + let _: CheckSupportsStaticProperty<#class>; + }); + } else { + add_check(quote! { + struct CheckSupportsInstanceProperty(T); + let _: CheckSupportsInstanceProperty<#class>; + }); + } + } + _ => {} + }, + } + } + let maybe_no_coverage = coverage(); (quote! { @@ -801,7 +840,10 @@ impl TryToTokens for ast::Export { )] #maybe_no_coverage 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 diff --git a/crates/macro/ui-tests/async-errors.stderr b/crates/macro/ui-tests/async-errors.stderr index 5526851f1d3..a13c8fceac8 100644 --- a/crates/macro/ui-tests/async-errors.stderr +++ b/crates/macro/ui-tests/async-errors.stderr @@ -22,7 +22,7 @@ error[E0277]: the trait bound `wasm_bindgen::JsValue: From` is not sati --> ui-tests/async-errors.rs:35:24 | 35 | pub async fn bad3() -> BadType { loop {} } - | ^^^^^^^ the trait `From` is not implemented for `wasm_bindgen::JsValue` + | ^^^^^^^ the trait `From` is not implemented for `wasm_bindgen::JsValue`, which is required by `BadType: IntoJsResult` | = help: the following other types implement trait `From`: > diff --git a/crates/macro/ui-tests/main-debug.stderr b/crates/macro/ui-tests/main-debug.stderr index 2b3c670ecf6..fc9ed9e23c4 100644 --- a/crates/macro/ui-tests/main-debug.stderr +++ b/crates/macro/ui-tests/main-debug.stderr @@ -3,3 +3,11 @@ error: the main function has to be called main | 18 | fn fail() {} | ^^^^ + +warning: unused variable: `f` + --> ui-tests/main-debug.rs:12:19 + | +12 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + | ^ help: if this is intentional, prefix it with an underscore: `_f` + | + = note: `#[warn(unused_variables)]` on by default diff --git a/crates/macro/ui-tests/main-infallible.stderr b/crates/macro/ui-tests/main-infallible.stderr index fd8c9c99769..4487bdcfcf6 100644 --- a/crates/macro/ui-tests/main-infallible.stderr +++ b/crates/macro/ui-tests/main-infallible.stderr @@ -3,3 +3,20 @@ error: the main function has to be called main | 10 | fn fail() {} | ^^^^ + +warning: unreachable expression + --> ui-tests/main-infallible.rs:4:1 + | +4 | #[wasm_bindgen(main)] + | ^^^^^^^^^^^^^^^^^^^^^ + | | + | unreachable expression + | any code following this expression is unreachable + | +note: this expression has type `Infallible`, which is uninhabited + --> ui-tests/main-infallible.rs:4:1 + | +4 | #[wasm_bindgen(main)] + | ^^^^^^^^^^^^^^^^^^^^^ + = note: `#[warn(unreachable_code)]` on by default + = note: this warning originates in the attribute macro `wasm_bindgen` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/macro/ui-tests/missing-catch.stderr b/crates/macro/ui-tests/missing-catch.stderr index 4fc6b3ed6de..b420b787da7 100644 --- a/crates/macro/ui-tests/missing-catch.stderr +++ b/crates/macro/ui-tests/missing-catch.stderr @@ -18,3 +18,38 @@ error[E0277]: the trait bound `Result: FromWasmAbi` is not satisfied + --> ui-tests/missing-catch.rs:3:1 + | +3 | #[wasm_bindgen] + | ^^^^^^^^^^^^^^^ the trait `FromWasmAbi` is not implemented for `Result` + | + = help: the following other types implement trait `FromWasmAbi`: + bool + char + isize + i8 + i16 + i32 + i64 + i128 + and $N others + = note: this error originates in the attribute macro `wasm_bindgen` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `Result: FromWasmAbi` is not satisfied + --> ui-tests/missing-catch.rs:6:18 + | +6 | pub fn foo() -> Result; + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromWasmAbi` is not implemented for `Result` + | + = help: the following other types implement trait `FromWasmAbi`: + bool + char + isize + i8 + i16 + i32 + i64 + i128 + and $N others diff --git a/crates/macro/ui-tests/traits-not-implemented.stderr b/crates/macro/ui-tests/traits-not-implemented.stderr index 82aeb56b6fe..2f7e9887747 100644 --- a/crates/macro/ui-tests/traits-not-implemented.stderr +++ b/crates/macro/ui-tests/traits-not-implemented.stderr @@ -15,3 +15,41 @@ error[E0277]: the trait bound `A: IntoWasmAbi` is not satisfied i128 and $N others = note: this error originates in the attribute macro `wasm_bindgen` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `A: IntoWasmAbi` is not satisfied + --> ui-tests/traits-not-implemented.rs:8:19 + | +8 | pub fn foo(a: A); + | ^ the trait `IntoWasmAbi` is not implemented for `A` + | + = help: the following other types implement trait `IntoWasmAbi`: + bool + char + isize + i8 + i16 + i32 + i64 + i128 + and $N others + +error[E0277]: the trait bound `A: IntoWasmAbi` is not satisfied + --> ui-tests/traits-not-implemented.rs:8:12 + | +5 | #[wasm_bindgen] + | --------------- in this procedural macro expansion +... +8 | pub fn foo(a: A); + | ^^^ the trait `IntoWasmAbi` is not implemented for `A` + | + = help: the following other types implement trait `IntoWasmAbi`: + bool + char + isize + i8 + i16 + i32 + i64 + i128 + and $N others + = note: this error originates in the attribute macro `wasm_bindgen` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/macro/ui-tests/unsupported-options.rs b/crates/macro/ui-tests/unsupported-options.rs new file mode 100644 index 00000000000..1589027f30c --- /dev/null +++ b/crates/macro/ui-tests/unsupported-options.rs @@ -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() {} diff --git a/crates/macro/ui-tests/unsupported-options.stderr b/crates/macro/ui-tests/unsupported-options.stderr new file mode 100644 index 00000000000..c9fc3b2e902 --- /dev/null +++ b/crates/macro/ui-tests/unsupported-options.stderr @@ -0,0 +1,110 @@ +error[E0277]: JavaScript constructors are not supported for `RustEnum` + --> ui-tests/unsupported-options.rs:56:12 + | +49 | #[wasm_bindgen] + | --------------- in this procedural macro expansion +... +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::_::CheckSupportsConstructor` + --> ui-tests/unsupported-options.rs:56:12 + | +49 | #[wasm_bindgen] + | --------------- in this procedural macro expansion +... +56 | pub fn new() -> Self { + | ^^^ required by this bound in `CheckSupportsConstructor` + = note: this error originates in the attribute macro `wasm_bindgen::prelude::__wasm_bindgen_class_marker` which comes from the expansion of 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 + | +49 | #[wasm_bindgen] + | --------------- in this procedural macro expansion +... +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::_::CheckSupportsInstanceProperty` + --> ui-tests/unsupported-options.rs:61:12 + | +49 | #[wasm_bindgen] + | --------------- in this procedural macro expansion +... +61 | pub fn prop(self) -> u32 { + | ^^^^ required by this bound in `CheckSupportsInstanceProperty` + = note: this error originates in the attribute macro `wasm_bindgen::prelude::__wasm_bindgen_class_marker` which comes from the expansion of 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 + | +49 | #[wasm_bindgen] + | --------------- in this procedural macro expansion +... +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::_::CheckSupportsInstanceProperty` + --> ui-tests/unsupported-options.rs:65:12 + | +49 | #[wasm_bindgen] + | --------------- in this procedural macro expansion +... +65 | pub fn set_prop(self, _value: u32) {} + | ^^^^^^^^ required by this bound in `CheckSupportsInstanceProperty` + = note: this error originates in the attribute macro `wasm_bindgen::prelude::__wasm_bindgen_class_marker` which comes from the expansion of 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 + | +49 | #[wasm_bindgen] + | --------------- in this procedural macro expansion +... +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::_::CheckSupportsStaticProperty` + --> ui-tests/unsupported-options.rs:68:12 + | +49 | #[wasm_bindgen] + | --------------- in this procedural macro expansion +... +68 | pub fn static_prop() -> u32 { + | ^^^^^^^^^^^ required by this bound in `CheckSupportsStaticProperty` + = note: this error originates in the attribute macro `wasm_bindgen::prelude::__wasm_bindgen_class_marker` which comes from the expansion of 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 + | +49 | #[wasm_bindgen] + | --------------- in this procedural macro expansion +... +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::_::CheckSupportsStaticProperty` + --> ui-tests/unsupported-options.rs:72:12 + | +49 | #[wasm_bindgen] + | --------------- in this procedural macro expansion +... +72 | pub fn set_static_prop(_value: u32) {} + | ^^^^^^^^^^^^^^^ required by this bound in `CheckSupportsStaticProperty` + = note: this error originates in the attribute macro `wasm_bindgen::prelude::__wasm_bindgen_class_marker` which comes from the expansion of the attribute macro `wasm_bindgen` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/macro/ui-tests/wasm-bindgen.stderr b/crates/macro/ui-tests/wasm-bindgen.stderr index 2ec13836be6..a831c1e08a0 100644 --- a/crates/macro/ui-tests/wasm-bindgen.stderr +++ b/crates/macro/ui-tests/wasm-bindgen.stderr @@ -7,7 +7,7 @@ error[E0433]: failed to resolve: could not find `convert` in `test` = note: this error originates in the attribute macro `wasm_bindgen` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider importing one of these items | -3 + use crate::test::test::convert; +3 + use crate::extern_test::convert; | 3 + use wasm_bindgen::convert; | diff --git a/src/lib.rs b/src/lib.rs index 403a119fa0e..027daf08f53 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,7 +20,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, @@ -77,6 +77,7 @@ pub mod convert; pub mod describe; mod externref; mod link; +mod marker; mod cast; pub use crate::cast::{JsCast, JsObject}; @@ -96,7 +97,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 @@ -123,7 +124,7 @@ impl JsValue { const fn _new(idx: u32) -> JsValue { JsValue { idx, - _marker: marker::PhantomData, + _marker: PhantomData, } } @@ -1582,6 +1583,10 @@ pub mod __rt { use alloc::boxed::Box; use alloc::rc::Rc; + pub mod marker { + pub use super::super::marker::*; + } + pub mod once_cell { #[cfg(any(target_feature = "atomics", feature = "std"))] pub use once_cell::*; diff --git a/src/marker.rs b/src/marker.rs new file mode 100644 index 00000000000..3b6b7ffdca4 --- /dev/null +++ b/src/marker.rs @@ -0,0 +1,85 @@ +//! This module contains marker traits used by `wasm-bindgen` to verify its +//! generated code. +//! +//! NOTE: This module is not exported as is. It is re-exported under +//! `wasm_bindgen::__rt::marker`. +//! +//! # ⚠️ Unstable +//! +//! This is an internal module, no stability guarantees are provided. Use at +//! your own risk. + +#[cfg(feature = "diagnostic")] +extern crate rustversion; + +/// 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. +#[cfg_attr( + feature = "diagnostic", + rustversion::attr( + since(1.78), + 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. +#[cfg_attr( + feature = "diagnostic", + rustversion::attr( + since(1.78), + 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. +#[cfg_attr( + feature = "diagnostic", + rustversion::attr( + since(1.78), + 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 {}