mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
Fix Call Generics & FnOnce macro hygiene (#2437)
* Fix Call Generics & FnOnce macro hygiene. * Add a failure case as well. * Remove a unused struct in test case. * Update packages/yew-macro/tests/hook_attr/hook-const-generic-pass.rs Co-authored-by: Muhammad Hamza <muhammadhamza1311@gmail.com>
This commit is contained in:
parent
95fb5dc8fb
commit
fc067ab56c
@ -1,9 +1,9 @@
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use proc_macro_error::emit_error;
|
||||
use quote::quote;
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::parse::{Parse, ParseStream};
|
||||
use syn::visit_mut;
|
||||
use syn::{parse_file, Ident, ItemFn, LitStr, ReturnType, Signature};
|
||||
use syn::{parse_file, GenericParam, Ident, ItemFn, LitStr, ReturnType, Signature};
|
||||
|
||||
mod body;
|
||||
mod lifetime;
|
||||
@ -98,6 +98,19 @@ When used in function components and hooks, this hook is equivalent to:
|
||||
let output_type = &hook_sig.output_type;
|
||||
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
let call_generics = {
|
||||
let mut generics = generics.clone();
|
||||
|
||||
// We need to filter out lifetimes.
|
||||
generics.params = generics
|
||||
.params
|
||||
.into_iter()
|
||||
.filter(|m| !matches!(m, GenericParam::Lifetime(_)))
|
||||
.collect();
|
||||
|
||||
let (_impl_generics, ty_generics, _where_clause) = generics.split_for_impl();
|
||||
ty_generics.as_turbofish().to_token_stream()
|
||||
};
|
||||
|
||||
let ctx_ident = Ident::new("ctx", Span::mixed_site());
|
||||
|
||||
@ -120,13 +133,13 @@ When used in function components and hooks, this hook is equivalent to:
|
||||
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 FnOnce(&mut ::yew::functional::HookContext) #inner_fn_rt> };
|
||||
let boxed_fn_type = quote! { ::std::boxed::Box<dyn #hook_lifetime_plus ::std::ops::FnOnce(&mut ::yew::functional::HookContext) #inner_fn_rt> };
|
||||
|
||||
// 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,)*)
|
||||
#inner_fn_ident #call_generics (#ctx_ident, #(#input_args,)*)
|
||||
}
|
||||
) as #boxed_fn_type;
|
||||
|
||||
@ -138,8 +151,6 @@ When used in function components and hooks, this hook is equivalent to:
|
||||
let args_ident = Ident::new("args", Span::mixed_site());
|
||||
let hook_struct_name = Ident::new("HookProvider", Span::mixed_site());
|
||||
|
||||
let call_generics = ty_generics.as_turbofish();
|
||||
|
||||
let phantom_types = hook_sig.phantom_types();
|
||||
let phantom_lifetimes = hook_sig.phantom_lifetimes();
|
||||
|
||||
@ -155,7 +166,7 @@ When used in function components and hooks, this hook is equivalent to:
|
||||
fn run(mut self, #ctx_ident: &mut ::yew::functional::HookContext) -> Self::Output {
|
||||
let (#(#input_args,)*) = self.#args_ident;
|
||||
|
||||
#inner_fn_ident(#ctx_ident, #(#input_args,)*)
|
||||
#inner_fn_ident #call_generics (#ctx_ident, #(#input_args,)*)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,12 @@
|
||||
use yew::prelude::*;
|
||||
|
||||
#[hook]
|
||||
fn use_reducer_default_action<T>() -> T::Action
|
||||
where
|
||||
T: Reducible + 'static,
|
||||
T::Action: Default + 'static,
|
||||
{
|
||||
T::Action::default()
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@ -0,0 +1,8 @@
|
||||
#![no_implicit_prelude]
|
||||
|
||||
#[::yew::prelude::hook]
|
||||
fn use_a_const<const N: u32>() -> u32 {
|
||||
N
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@ -0,0 +1,9 @@
|
||||
// we need to re-test the macro hygiene here as it uses a different implementation for impl traits.
|
||||
#![no_implicit_prelude]
|
||||
|
||||
#[::yew::prelude::hook]
|
||||
fn use_some_string(a: impl ::std::convert::Into<::std::string::String>) -> ::std::string::String {
|
||||
a.into()
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
8
packages/yew-macro/tests/hook_attr/hook-lifetime-pass.rs
Normal file
8
packages/yew-macro/tests/hook_attr/hook-lifetime-pass.rs
Normal file
@ -0,0 +1,8 @@
|
||||
use yew::prelude::*;
|
||||
|
||||
#[hook]
|
||||
fn use_as_is<'a>(input: &'a ()) -> &'a () {
|
||||
input
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
39
packages/yew-macro/tests/hook_attr/hook_location-fail.rs
Normal file
39
packages/yew-macro/tests/hook_attr/hook_location-fail.rs
Normal file
@ -0,0 +1,39 @@
|
||||
use yew::prelude::*;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
struct Ctx;
|
||||
|
||||
#[hook]
|
||||
fn use_some_html() -> Html {
|
||||
if let Some(_m) = use_context::<Ctx>() {
|
||||
use_context::<Ctx>().unwrap();
|
||||
todo!()
|
||||
}
|
||||
|
||||
let _ = || {
|
||||
use_context::<Ctx>().unwrap();
|
||||
todo!()
|
||||
};
|
||||
|
||||
for _ in 0..10 {
|
||||
use_context::<Ctx>().unwrap();
|
||||
}
|
||||
|
||||
while let Some(_m) = use_context::<Ctx>() {
|
||||
use_context::<Ctx>().unwrap();
|
||||
}
|
||||
|
||||
match use_context::<Ctx>() {
|
||||
Some(_) => use_context::<Ctx>(),
|
||||
None => {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
use_context::<Ctx>().unwrap();
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
69
packages/yew-macro/tests/hook_attr/hook_location-fail.stderr
Normal file
69
packages/yew-macro/tests/hook_attr/hook_location-fail.stderr
Normal file
@ -0,0 +1,69 @@
|
||||
error: hooks cannot be called at this position.
|
||||
|
||||
= help: move hooks to the top-level of your function.
|
||||
= note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks
|
||||
|
||||
--> tests/hook_attr/hook_location-fail.rs:9:9
|
||||
|
|
||||
9 | use_context::<Ctx>().unwrap();
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
error: hooks cannot be called at this position.
|
||||
|
||||
= help: move hooks to the top-level of your function.
|
||||
= note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks
|
||||
|
||||
--> tests/hook_attr/hook_location-fail.rs:14:9
|
||||
|
|
||||
14 | use_context::<Ctx>().unwrap();
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
error: hooks cannot be called at this position.
|
||||
|
||||
= help: move hooks to the top-level of your function.
|
||||
= note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks
|
||||
|
||||
--> tests/hook_attr/hook_location-fail.rs:19:9
|
||||
|
|
||||
19 | use_context::<Ctx>().unwrap();
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
error: hooks cannot be called at this position.
|
||||
|
||||
= help: move hooks to the top-level of your function.
|
||||
= note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks
|
||||
|
||||
--> tests/hook_attr/hook_location-fail.rs:22:26
|
||||
|
|
||||
22 | while let Some(_m) = use_context::<Ctx>() {
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
error: hooks cannot be called at this position.
|
||||
|
||||
= help: move hooks to the top-level of your function.
|
||||
= note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks
|
||||
|
||||
--> tests/hook_attr/hook_location-fail.rs:23:9
|
||||
|
|
||||
23 | use_context::<Ctx>().unwrap();
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
error: hooks cannot be called at this position.
|
||||
|
||||
= help: move hooks to the top-level of your function.
|
||||
= note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks
|
||||
|
||||
--> tests/hook_attr/hook_location-fail.rs:27:20
|
||||
|
|
||||
27 | Some(_) => use_context::<Ctx>(),
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
error: hooks cannot be called at this position.
|
||||
|
||||
= help: move hooks to the top-level of your function.
|
||||
= note: see: https://yew.rs/docs/next/concepts/function-components/introduction#hooks
|
||||
|
||||
--> tests/hook_attr/hook_location-fail.rs:34:9
|
||||
|
|
||||
34 | use_context::<Ctx>().unwrap();
|
||||
| ^^^^^^^^^^^
|
||||
30
packages/yew-macro/tests/hook_attr/hook_location-pass.rs
Normal file
30
packages/yew-macro/tests/hook_attr/hook_location-pass.rs
Normal file
@ -0,0 +1,30 @@
|
||||
#![no_implicit_prelude]
|
||||
|
||||
#[derive(
|
||||
::std::prelude::rust_2021::Debug,
|
||||
::std::prelude::rust_2021::PartialEq,
|
||||
::std::prelude::rust_2021::Clone,
|
||||
)]
|
||||
struct Ctx;
|
||||
|
||||
#[::yew::prelude::hook]
|
||||
fn use_some_html() -> ::yew::prelude::Html {
|
||||
::yew::prelude::use_context::<Ctx>().unwrap();
|
||||
|
||||
if let ::std::prelude::rust_2021::Some(_m) = ::yew::prelude::use_context::<Ctx>() {
|
||||
::std::todo!()
|
||||
}
|
||||
|
||||
let _ctx = { ::yew::prelude::use_context::<Ctx>() };
|
||||
|
||||
match ::yew::prelude::use_context::<Ctx>() {
|
||||
::std::prelude::rust_2021::Some(_) => {
|
||||
::std::todo!()
|
||||
}
|
||||
::std::prelude::rust_2021::None => {
|
||||
::std::todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
7
packages/yew-macro/tests/hook_attr_test.rs
Normal file
7
packages/yew-macro/tests/hook_attr_test.rs
Normal file
@ -0,0 +1,7 @@
|
||||
#[allow(dead_code)]
|
||||
#[rustversion::attr(stable(1.56), test)]
|
||||
fn tests() {
|
||||
let t = trybuild::TestCases::new();
|
||||
t.pass("tests/hook_attr/*-pass.rs");
|
||||
t.compile_fail("tests/hook_attr/*-fail.rs");
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user