diff --git a/crates/macro-support/src/parser.rs b/crates/macro-support/src/parser.rs index 73b9c2166f0..1642b8bf39f 100644 --- a/crates/macro-support/src/parser.rs +++ b/crates/macro-support/src/parser.rs @@ -803,6 +803,57 @@ pub(crate) fn is_js_keyword(keyword: &str) -> bool { JS_KEYWORDS.contains(&keyword) } +/// Replace instances of `Self` with the actual type `T` +fn replace_self(ty: syn::Type, self_ty: Option<&Ident>) -> syn::Type { + let self_ty = match self_ty { + Some(i) => i, + None => return ty, + }; + + let mut path = match get_ty(&ty) { + syn::Type::Path(syn::TypePath { qself: None, path }) => path.clone(), + other => return other.clone(), + }; + + if path.segments.len() == 1 && path.segments[0].ident == "Self" { + let new_path = self_ty.clone().into(); + return syn::Type::Path(syn::TypePath { + qself: None, + path: new_path, + }); + } + + fn walk_path_change_self(path: &mut syn::Path, self_ty: &Ident) { + for seg in path.segments.iter_mut() { + if let syn::PathArguments::AngleBracketed(ref mut brackets) = seg.arguments { + let types = brackets.args.iter_mut().filter_map(|arg| match arg { + syn::GenericArgument::Type(ref mut ty) => Some(ty), + _ => None, + }); + for ty in types { + match ty { + syn::Type::Path(syn::TypePath { ref mut path, .. }) => { + if path.segments.len() == 1 && path.segments[0].ident == "Self" { + path.segments[0].ident = self_ty.clone(); + } else { + walk_path_change_self(path, self_ty) + } + } + _ => {} + } + } + } + } + } + + let path_str = quote::quote! { #path }.to_string(); + if path_str.contains("Self") { + walk_path_change_self(&mut path, self_ty); + } + + syn::Type::Path(syn::TypePath { qself: None, path }) +} + /// Construct a function (and gets the self type if appropriate) for our AST from a syn function. #[allow(clippy::too_many_arguments)] fn function_from_decl( @@ -829,26 +880,6 @@ fn function_from_decl( let syn::Signature { inputs, output, .. } = sig; - let replace_self = |t: syn::Type| { - let self_ty = match self_ty { - Some(i) => i, - None => return t, - }; - let path = match get_ty(&t) { - syn::Type::Path(syn::TypePath { qself: None, path }) => path.clone(), - other => return other.clone(), - }; - let new_path = if path.segments.len() == 1 && path.segments[0].ident == "Self" { - self_ty.clone().into() - } else { - path - }; - syn::Type::Path(syn::TypePath { - qself: None, - path: new_path, - }) - }; - let replace_colliding_arg = |i: &mut syn::PatType| { if let syn::Pat::Ident(ref mut i) = *i.pat { let ident = i.ident.to_string(); @@ -864,7 +895,7 @@ fn function_from_decl( .filter_map(|arg| match arg { syn::FnArg::Typed(mut c) => { replace_colliding_arg(&mut c); - c.ty = Box::new(replace_self(*c.ty)); + c.ty = Box::new(replace_self(*c.ty, self_ty)); Some(c) } syn::FnArg::Receiver(r) => { @@ -886,7 +917,7 @@ fn function_from_decl( let ret = match output { syn::ReturnType::Default => None, - syn::ReturnType::Type(_, ty) => Some(replace_self(*ty)), + syn::ReturnType::Type(_, ty) => Some(replace_self(*ty, self_ty)), }; let (name, name_span, renamed_via_js_name) = if let Some((js_name, js_name_span)) = diff --git a/tests/wasm/codegen.rs b/tests/wasm/codegen.rs new file mode 100644 index 00000000000..24bdcf572f7 --- /dev/null +++ b/tests/wasm/codegen.rs @@ -0,0 +1,28 @@ +//! This tests that the `wasm_bindgen` macro produces code that compiles for these use cases. +//! `cargo test --target wasm32-unknown-unknown` will not run if any of these tests breaks. +use wasm_bindgen::prelude::*; + +macro_rules! my_export { + ($i: ident, $s: ty) => { + #[wasm_bindgen] + pub fn $i(_: $s) {} + }; +} + +my_export!(should_compile, &[i32]); + +#[wasm_bindgen] +pub struct A; + +#[wasm_bindgen] +impl A { + pub fn test_only_self() -> Self { + A + } + pub fn test_option_self() -> Option { + None + } + pub fn test_nested_self() -> Result, String> { + Ok(None) + } +} diff --git a/tests/wasm/macro_rules.rs b/tests/wasm/macro_rules.rs deleted file mode 100644 index 42c4b2a1d1d..00000000000 --- a/tests/wasm/macro_rules.rs +++ /dev/null @@ -1,12 +0,0 @@ -//! This tests that the `wasm_bindgen` macro produces code that compiles for this use case. -//! `cargo test --target wasm32-unknown-unknown` will not run if this test breaks. -use wasm_bindgen::prelude::*; - -macro_rules! my_export { - ($i: ident, $s: ty) => { - #[wasm_bindgen] - pub fn $i(_: $s) {} - }; -} - -my_export!(should_compile, &[i32]); diff --git a/tests/wasm/main.rs b/tests/wasm/main.rs index 30ea94b422d..dacf0bff78b 100644 --- a/tests/wasm/main.rs +++ b/tests/wasm/main.rs @@ -20,6 +20,7 @@ pub mod bigint; pub mod char; pub mod classes; pub mod closures; +pub mod codegen; pub mod comments; pub mod duplicate_deps; pub mod duplicates; @@ -35,7 +36,6 @@ pub mod js_keywords; pub mod js_objects; pub mod jscast; pub mod link_to; -pub mod macro_rules; pub mod math; pub mod no_shims; pub mod node;