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

Replace all instances of Self in methods #3631

Closed
wants to merge 1 commit into from

Conversation

snOm3ad
Copy link
Contributor

@snOm3ad snOm3ad commented Sep 23, 2023

Currently the wasm_bindgen macro only replaces top level occurrences of the generic parameter Self in exported methods. This leads to an error when returning parametrized types containing Self due to Rust inner items rules.

This PR fixes the issue by drilling down the return type TokenStream and replacing all instances of Self with the actual type name.

Fixes #3105

Currently the `wasm_bindgen` macro only replaces top level occurrences
of the generic parameter `Self` in exported methods. This leads to an
error when returning parametrized types containing `Self` due to Rust
inner items rules.

This PR fixes the issue by drilling down the return type `TokenStream`
and replacing all instances of `Self` with the actual type name.

Signed-off-by: Oliver T <[email protected]>
}
}

let path_str = quote::quote! { #path }.to_string();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let path_str = quote::quote! { #path }.to_string();
let path_str = path.to_token_stream().to_string();

}

let path_str = quote::quote! { #path }.to_string();
if path_str.contains("Self") {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't we just call walk_path_change_self() and let it just do it's thing anyway? I'm assuming to_token_stream() and to_string() have to go through the Path to build it anyway, so it wouldn't be more expensive.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah now that you mentioned it, seems like there is no way around it. Will push an update later today!

Comment on lines +835 to +841
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)
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should handle types other than paths too. For example, Box<[Self]> is still broken because Type::Slice is ignored.

Copy link
Contributor Author

@snOm3ad snOm3ad Sep 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Yeah I'll try to handle all possible types. AFAIK references are not allowed in return types, but I did noticed this function is also called for types in arguments which do allow references. So maybe I'll handle function args differently.

Comment on lines +813 to +816
let mut path = match get_ty(&ty) {
syn::Type::Path(syn::TypePath { qself: None, path }) => path.clone(),
other => return other.clone(),
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should also handle types other than paths.

@snOm3ad
Copy link
Contributor Author

snOm3ad commented Sep 23, 2023

I was also thinking of turning the type into a String then use replace("Self", self_ty) then parse it back into a syn::Type, this would have the advantage of not having to change this function when new types are supported for return types or argument types. Do you have any thoughts on this?

@daxpedda
Copy link
Collaborator

daxpedda commented Sep 23, 2023

I was also thinking of turning the type into a String then use replace("Self", self_ty) then parse it back into a syn::Type, this would have the advantage of not having to change this function when new types are supported for return types or argument types. Do you have any thoughts on this?

I guess this could work and would obviously simplify the code a lot. The only thing I can think of that could use the word "Self" in a type is a macro or a const generic, Rust only supports integers right now.

My impression is that the additional code required to cover the remaining bases shouldn't be too bad. I'm not too worried about what else Rust might add in the future, it would be most likely a breaking change in syn anyway, so it's not like we would miss it.

@Liamolucko
Copy link
Collaborator

I was also thinking of turning the type into a String then use replace("Self", self_ty) then parse it back into a syn::Type, this would have the advantage of not having to change this function when new types are supported for return types or argument types. Do you have any thoughts on this?

That would also catch types that contain Self in their name though, e.g. SelfPortrait.

@snOm3ad
Copy link
Contributor Author

snOm3ad commented Sep 24, 2023

I was also thinking of turning the type into a String then use replace("Self", self_ty) then parse it back into a syn::Type, this would have the advantage of not having to change this function when new types are supported for return types or argument types. Do you have any thoughts on this?

That would also catch types that contain Self in their name though, e.g. SelfPortrait.

That's so sad! Oh well, will try to have an update on this later today. Thank you both for your comments!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
waiting for author Waiting for author to respond
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Returning Option<Self> in #[wasm_bindgen] impl block gives error
3 participants