diff --git a/packages/yew-macro/src/hook/lifetime.rs b/packages/yew-macro/src/hook/lifetime.rs index 9ea6c19fc86..8b94ac5cdb6 100644 --- a/packages/yew-macro/src/hook/lifetime.rs +++ b/packages/yew-macro/src/hook/lifetime.rs @@ -3,7 +3,7 @@ use std::sync::{Arc, Mutex}; use syn::visit_mut::{self, VisitMut}; use syn::{ GenericArgument, Lifetime, ParenthesizedGenericArguments, Receiver, TypeBareFn, TypeImplTrait, - TypeParamBound, TypeReference, + TypeParamBound, TypeReference, TypeTraitObject, }; // borrowed from the awesome async-trait crate. @@ -13,6 +13,7 @@ pub struct CollectLifetimes { pub name: &'static str, pub default_span: Span, + pub type_trait_obj_lock: Arc>, pub impl_trait_lock: Arc>, pub impl_fn_lock: Arc>, } @@ -26,6 +27,7 @@ impl CollectLifetimes { default_span, impl_trait_lock: Arc::default(), + type_trait_obj_lock: Arc::default(), impl_fn_lock: Arc::default(), } } @@ -34,6 +36,10 @@ impl CollectLifetimes { self.impl_trait_lock.try_lock().is_err() } + fn is_type_trait_obj(&self) -> bool { + self.type_trait_obj_lock.try_lock().is_err() + } + fn is_impl_fn(&self) -> bool { self.impl_fn_lock.try_lock().is_err() } @@ -102,12 +108,20 @@ impl VisitMut for CollectLifetimes { visit_mut::visit_type_impl_trait_mut(self, impl_trait); } + fn visit_type_trait_object_mut(&mut self, type_trait_obj: &mut TypeTraitObject) { + let type_trait_obj_lock = self.type_trait_obj_lock.clone(); + let _locked = type_trait_obj_lock.try_lock(); + + visit_mut::visit_type_trait_object_mut(self, type_trait_obj); + } + fn visit_parenthesized_generic_arguments_mut( &mut self, generic_args: &mut ParenthesizedGenericArguments, ) { let impl_fn_lock = self.impl_fn_lock.clone(); - let _maybe_locked = self.is_impl_trait().then(|| impl_fn_lock.try_lock()); + let _maybe_locked = + (self.is_impl_trait() || self.is_type_trait_obj()).then(|| impl_fn_lock.try_lock()); visit_mut::visit_parenthesized_generic_arguments_mut(self, generic_args); } diff --git a/packages/yew-macro/src/hook/mod.rs b/packages/yew-macro/src/hook/mod.rs index e620b020b76..ba186d323b2 100644 --- a/packages/yew-macro/src/hook/mod.rs +++ b/packages/yew-macro/src/hook/mod.rs @@ -4,6 +4,7 @@ use quote::quote; use syn::parse::{Parse, ParseStream}; use syn::{ parse_file, parse_quote, visit_mut, Attribute, Ident, ItemFn, LitStr, ReturnType, Signature, + Type, }; mod body; @@ -131,19 +132,25 @@ pub fn hook_impl(hook: HookFn) -> syn::Result { let inner_fn = quote! { fn #inner_fn_ident #generics (#ctx_ident: &mut ::yew::functional::HookContext, #inputs) #inner_fn_rt #where_clause #block }; let inner_type_impl = if hook_sig.needs_boxing { + let with_output = !matches!(hook_sig.output_type, Type::ImplTrait(_),); + let inner_fn_rt = with_output.then(|| &inner_fn_rt); + let output_type = with_output.then(|| &output_type); + let hook_lifetime = &hook_sig.hook_lifetime; let hook_lifetime_plus = quote! { #hook_lifetime + }; let boxed_inner_ident = Ident::new("boxed_inner", Span::mixed_site()); let boxed_fn_type = quote! { ::std::boxed::Box }; + let as_boxed_fn = with_output.then(|| quote! { as #boxed_fn_type }); + // We need boxing implementation for `impl Trait` arguments. quote! { let #boxed_inner_ident = ::std::boxed::Box::new( move |#ctx_ident: &mut ::yew::functional::HookContext| #inner_fn_rt { #inner_fn_ident (#ctx_ident, #(#input_args,)*) } - ) as #boxed_fn_type; + ) #as_boxed_fn; ::yew::functional::BoxedHook::<#hook_lifetime, #output_type>::new(#boxed_inner_ident) } diff --git a/packages/yew-macro/src/hook/signature.rs b/packages/yew-macro/src/hook/signature.rs index 62f3e8ee2cb..f4772619df4 100644 --- a/packages/yew-macro/src/hook/signature.rs +++ b/packages/yew-macro/src/hook/signature.rs @@ -51,12 +51,30 @@ impl HookSignature { parse_quote! { -> impl #bound ::yew::functional::Hook }, parse_quote! { () }, ), - ReturnType::Type(arrow, ref return_type) => ( - parse_quote_spanned! { - return_type.span() => #arrow impl #bound ::yew::functional::Hook - }, - *return_type.clone(), - ), + ReturnType::Type(arrow, ref return_type) => { + if let Type::Reference(ref m) = &**return_type { + if m.lifetime.is_none() { + let mut return_type_ref = m.clone(); + return_type_ref.lifetime = parse_quote!('hook); + + let return_type_ref = Type::Reference(return_type_ref); + + return ( + parse_quote_spanned! { + return_type.span() => #arrow impl #bound ::yew::functional::Hook + }, + return_type_ref, + ); + } + } + + ( + parse_quote_spanned! { + return_type.span() => #arrow impl #bound ::yew::functional::Hook + }, + *return_type.clone(), + ) + } } } diff --git a/packages/yew-macro/tests/hook_attr/hook-dynamic-dispatch-pass.rs b/packages/yew-macro/tests/hook_attr/hook-dynamic-dispatch-pass.rs new file mode 100644 index 00000000000..ae045161536 --- /dev/null +++ b/packages/yew-macro/tests/hook_attr/hook-dynamic-dispatch-pass.rs @@ -0,0 +1,8 @@ +#![no_implicit_prelude] + +#[::yew::prelude::hook] +fn use_boxed_fn(_f: ::std::boxed::Box &str>) { + ::std::todo!() +} + +fn main() {} diff --git a/packages/yew-macro/tests/hook_attr/hook-return-impl-trait-pass.rs b/packages/yew-macro/tests/hook_attr/hook-return-impl-trait-pass.rs new file mode 100644 index 00000000000..93da94e6041 --- /dev/null +++ b/packages/yew-macro/tests/hook_attr/hook-return-impl-trait-pass.rs @@ -0,0 +1,8 @@ +#![no_implicit_prelude] + +#[::yew::prelude::hook] +fn use_deref_as_u32() -> impl ::std::ops::Deref { + ::std::rc::Rc::new(0) +} + +fn main() {} diff --git a/packages/yew-macro/tests/hook_attr/hook-return-ref-pass.rs b/packages/yew-macro/tests/hook_attr/hook-return-ref-pass.rs new file mode 100644 index 00000000000..01022da3806 --- /dev/null +++ b/packages/yew-macro/tests/hook_attr/hook-return-ref-pass.rs @@ -0,0 +1,8 @@ +#![no_implicit_prelude] + +#[::yew::prelude::hook] +fn use_str_ref(f: &::std::primitive::str) -> &::std::primitive::str { + f +} + +fn main() {}