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

Fix some Hook edge cases #2592

Merged
merged 2 commits into from Apr 15, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 16 additions & 2 deletions packages/yew-macro/src/hook/lifetime.rs
Expand Up @@ -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.
Expand All @@ -13,6 +13,7 @@ pub struct CollectLifetimes {
pub name: &'static str,
pub default_span: Span,

pub type_trait_obj_lock: Arc<Mutex<()>>,
pub impl_trait_lock: Arc<Mutex<()>>,
pub impl_fn_lock: Arc<Mutex<()>>,
}
Expand All @@ -26,6 +27,7 @@ impl CollectLifetimes {
default_span,

impl_trait_lock: Arc::default(),
type_trait_obj_lock: Arc::default(),
impl_fn_lock: Arc::default(),
}
}
Expand All @@ -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()
}
Expand Down Expand Up @@ -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);
}
Expand Down
9 changes: 8 additions & 1 deletion packages/yew-macro/src/hook/mod.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -131,19 +132,25 @@ pub fn hook_impl(hook: HookFn) -> syn::Result<TokenStream> {
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<dyn #hook_lifetime_plus ::std::ops::FnOnce(&mut ::yew::functional::HookContext) #inner_fn_rt> };

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)
}
Expand Down
30 changes: 24 additions & 6 deletions packages/yew-macro/src/hook/signature.rs
Expand Up @@ -51,12 +51,30 @@ impl HookSignature {
parse_quote! { -> impl #bound ::yew::functional::Hook<Output = ()> },
parse_quote! { () },
),
ReturnType::Type(arrow, ref return_type) => (
parse_quote_spanned! {
return_type.span() => #arrow impl #bound ::yew::functional::Hook<Output = #return_type>
},
*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<Output = #return_type_ref>
},
return_type_ref,
);
}
}

(
parse_quote_spanned! {
return_type.span() => #arrow impl #bound ::yew::functional::Hook<Output = #return_type>
},
*return_type.clone(),
)
}
}
}

Expand Down
@@ -0,0 +1,8 @@
use yew::prelude::*;
futursolo marked this conversation as resolved.
Show resolved Hide resolved

#[hook]
fn use_boxed_fn(_f: Box<dyn Fn(&str) -> &str>) {
todo!()
}

fn main() {}
11 changes: 11 additions & 0 deletions packages/yew-macro/tests/hook_attr/hook-return-impl-trait-pass.rs
@@ -0,0 +1,11 @@
use std::ops::Deref;
futursolo marked this conversation as resolved.
Show resolved Hide resolved
use std::rc::Rc;

use yew::prelude::*;

#[hook]
fn use_deref_as_u32() -> impl Deref<Target = u32> {
Rc::new(0)
}

fn main() {}
8 changes: 8 additions & 0 deletions packages/yew-macro/tests/hook_attr/hook-return-ref-pass.rs
@@ -0,0 +1,8 @@
use yew::prelude::*;
futursolo marked this conversation as resolved.
Show resolved Hide resolved

#[hook]
fn use_str_ref(f: &str) -> &str {
f
}

fn main() {}