From 91fbfb804115c480736dec81cfc3a9f89d534d8e Mon Sep 17 00:00:00 2001 From: Luke Chu <37006668+lukechu10@users.noreply.github.com> Date: Mon, 22 Feb 2021 14:38:21 -0800 Subject: [PATCH 01/18] Generic functional components --- packages/yew-functional-macro/src/lib.rs | 65 ++++++++++++++----- .../tests/function_attr/generic-fail.rs | 18 ----- .../tests/function_attr/generic-fail.stderr | 5 -- .../tests/function_attr/generic-pass.rs | 33 ++++++++++ 4 files changed, 80 insertions(+), 41 deletions(-) delete mode 100644 packages/yew-functional-macro/tests/function_attr/generic-fail.rs delete mode 100644 packages/yew-functional-macro/tests/function_attr/generic-fail.stderr create mode 100644 packages/yew-functional-macro/tests/function_attr/generic-pass.rs diff --git a/packages/yew-functional-macro/src/lib.rs b/packages/yew-functional-macro/src/lib.rs index eeb3355740b..53509e71513 100644 --- a/packages/yew-functional-macro/src/lib.rs +++ b/packages/yew-functional-macro/src/lib.rs @@ -1,15 +1,19 @@ use proc_macro2::TokenStream; use quote::{quote, quote_spanned, ToTokens}; use syn::parse::{Parse, ParseStream}; +use syn::punctuated::Punctuated; use syn::spanned::Spanned; +use syn::token::Comma; use syn::{ - parse_macro_input, Attribute, Block, FnArg, Ident, Item, ItemFn, ReturnType, Type, Visibility, + parse_macro_input, Attribute, Block, FnArg, Generics, Ident, Item, ItemFn, ReturnType, Type, + Visibility, }; struct FunctionComponent { block: Box, props_type: Box, arg: FnArg, + generics: Generics, vis: Visibility, attrs: Vec, name: Ident, @@ -29,13 +33,6 @@ impl Parse for FunctionComponent { block, } = func; - if !sig.generics.params.is_empty() { - return Err(syn::Error::new_spanned( - sig.generics, - "function components can't contain generics", - )); - } - if sig.asyncness.is_some() { return Err(syn::Error::new_spanned( sig.asyncness, @@ -123,6 +120,7 @@ impl Parse for FunctionComponent { props_type: ty, block, arg, + generics: sig.generics, vis, attrs, name: sig.ident, @@ -176,12 +174,15 @@ fn function_component_impl( block, props_type, arg, + generics, vis, attrs, name: function_name, return_type, } = component; + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + if function_name == component_name { return Err(syn::Error::new_spanned( component_name, @@ -191,21 +192,49 @@ fn function_component_impl( let ret_type = quote_spanned!(return_type.span()=> ::yew::html::Html); - let quoted = quote! { - #[doc(hidden)] - #[allow(non_camel_case_types)] - #vis struct #function_name; + let quoted = if generics.params.len() == 0 { + quote! { + #[doc(hidden)] + #[allow(non_camel_case_types)] + #vis struct #function_name; - impl ::yew_functional::FunctionProvider for #function_name { - type TProps = #props_type; + impl ::yew_functional::FunctionProvider for #function_name { + type TProps = #props_type; - fn run(#arg) -> #ret_type { - #block + fn run(#arg) -> #ret_type { + #block + } } + + #(#attrs)* + #vis type #component_name = ::yew_functional::FunctionComponent<#function_name>; } + } else { + let phantom_generics = generics + .type_params() + .map(|ty_param| ty_param.ident.clone()) // create a new Punctuated sequence without any type bounds + .collect::>(); + + quote! { + #[doc(hidden)] + #[allow(non_camel_case_types)] + #[allow(unused_parens)] + #vis struct #function_name #ty_generics { + _marker: ::std::marker::PhantomData<(#phantom_generics)>, + } + + impl #impl_generics ::yew_functional::FunctionProvider for #function_name #ty_generics #where_clause { + type TProps = #props_type; - #(#attrs)* - #vis type #component_name = ::yew_functional::FunctionComponent<#function_name>; + fn run(#arg) -> #ret_type { + #block + } + } + + #(#attrs)* + #vis type #component_name #ty_generics = ::yew_functional::FunctionComponent<#function_name #ty_generics>; + } }; + Ok(quoted) } diff --git a/packages/yew-functional-macro/tests/function_attr/generic-fail.rs b/packages/yew-functional-macro/tests/function_attr/generic-fail.rs deleted file mode 100644 index 483a32f5a86..00000000000 --- a/packages/yew-functional-macro/tests/function_attr/generic-fail.rs +++ /dev/null @@ -1,18 +0,0 @@ -use yew::prelude::*; -use yew_functional::function_component; - -#[derive(Clone, Properties, PartialEq)] -struct Props { - a: usize, -} - -#[function_component(Comp)] -const fn comp(props: &P) -> Html { - html! { -

- { props.a } -

- } -} - -fn main() {} diff --git a/packages/yew-functional-macro/tests/function_attr/generic-fail.stderr b/packages/yew-functional-macro/tests/function_attr/generic-fail.stderr deleted file mode 100644 index 23bd5608388..00000000000 --- a/packages/yew-functional-macro/tests/function_attr/generic-fail.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: function components can't contain generics - --> $DIR/generic-fail.rs:10:14 - | -10 | const fn comp(props: &P) -> Html { - | ^^^^^^^^^^^^^^^ diff --git a/packages/yew-functional-macro/tests/function_attr/generic-pass.rs b/packages/yew-functional-macro/tests/function_attr/generic-pass.rs new file mode 100644 index 00000000000..29649a85042 --- /dev/null +++ b/packages/yew-functional-macro/tests/function_attr/generic-pass.rs @@ -0,0 +1,33 @@ +use yew::prelude::*; +use yew_functional::function_component; + +#[derive(Clone, Properties, PartialEq)] +struct Props { + a: usize, +} + +#[function_component(Comp)] +fn comp

(_props: &P) -> Html +where + P: Properties + PartialEq, +{ + html! { +

+ } +} + +#[function_component(Comp2)] +fn comp2(_props: &P) -> Html { + html! { +

+ } +} + +#[function_component(Comp3)] +fn comp3(_props: &()) -> Html { + html! { +

+ } +} + +fn main() {} From 8a765cec9cb4718a4c46a894ba8078e8cf0fe752 Mon Sep 17 00:00:00 2001 From: Luke Chu <37006668+lukechu10@users.noreply.github.com> Date: Mon, 22 Feb 2021 19:16:43 -0800 Subject: [PATCH 02/18] Add some tests --- .../function_attr/generic-lifetime-fail.rs | 19 +++++++++++++++++ .../generic-lifetime-fail.stderr | 7 +++++++ .../tests/function_attr/generic-pass.rs | 16 ++++++++++---- .../tests/function_attr/generic-props-fail.rs | 21 +++++++++++++++++++ .../function_attr/generic-props-fail.stderr | 8 +++++++ 5 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 packages/yew-functional-macro/tests/function_attr/generic-lifetime-fail.rs create mode 100644 packages/yew-functional-macro/tests/function_attr/generic-lifetime-fail.stderr create mode 100644 packages/yew-functional-macro/tests/function_attr/generic-props-fail.rs create mode 100644 packages/yew-functional-macro/tests/function_attr/generic-props-fail.stderr diff --git a/packages/yew-functional-macro/tests/function_attr/generic-lifetime-fail.rs b/packages/yew-functional-macro/tests/function_attr/generic-lifetime-fail.rs new file mode 100644 index 00000000000..9ac4796b819 --- /dev/null +++ b/packages/yew-functional-macro/tests/function_attr/generic-lifetime-fail.rs @@ -0,0 +1,19 @@ +use yew::prelude::*; +use yew_functional::function_component; + +#[derive(Clone, Properties, PartialEq)] +struct Props { + a: usize, +} + +// TODO: improve error message +#[function_component(Comp)] +fn comp<'a>(props: &Props) -> Html { + html! { +

+ { props.a } +

+ } +} + +fn main() {} diff --git a/packages/yew-functional-macro/tests/function_attr/generic-lifetime-fail.stderr b/packages/yew-functional-macro/tests/function_attr/generic-lifetime-fail.stderr new file mode 100644 index 00000000000..a2f27c6e78c --- /dev/null +++ b/packages/yew-functional-macro/tests/function_attr/generic-lifetime-fail.stderr @@ -0,0 +1,7 @@ +error[E0392]: parameter `'a` is never used + --> $DIR/generic-lifetime-fail.rs:11:9 + | +11 | fn comp<'a>(props: &Props) -> Html { + | ^^ unused parameter + | + = help: consider removing `'a`, referring to it in a field, or using a marker such as `std::marker::PhantomData` diff --git a/packages/yew-functional-macro/tests/function_attr/generic-pass.rs b/packages/yew-functional-macro/tests/function_attr/generic-pass.rs index 29649a85042..6e3676335e3 100644 --- a/packages/yew-functional-macro/tests/function_attr/generic-pass.rs +++ b/packages/yew-functional-macro/tests/function_attr/generic-pass.rs @@ -16,18 +16,26 @@ where } } -#[function_component(Comp2)] -fn comp2(_props: &P) -> Html { +#[function_component(Comp1)] +fn comp1(_props: &P) -> Html { html! {

} } -#[function_component(Comp3)] -fn comp3(_props: &()) -> Html { +#[function_component(Comp2)] +fn comp2(_props: &()) -> Html { html! {

} } +// TODO: add test for const generics + +fn compile_pass() { + html! { a=10 /> }; + html! { a=10 /> }; + html! { /> }; +} + fn main() {} diff --git a/packages/yew-functional-macro/tests/function_attr/generic-props-fail.rs b/packages/yew-functional-macro/tests/function_attr/generic-props-fail.rs new file mode 100644 index 00000000000..fc0d6c2828a --- /dev/null +++ b/packages/yew-functional-macro/tests/function_attr/generic-props-fail.rs @@ -0,0 +1,21 @@ +use yew::prelude::*; +use yew_functional::function_component; + +#[derive(Clone, Properties, PartialEq)] +struct Props { + a: usize, +} + +// TODO: improve error message +#[function_component(Comp)] +fn comp(_props: &P) -> Html { + html! { +

+ } +} + +fn compile_pass() { + html! { /> }; // missing prop 'a' +} + +fn main() {} diff --git a/packages/yew-functional-macro/tests/function_attr/generic-props-fail.stderr b/packages/yew-functional-macro/tests/function_attr/generic-props-fail.stderr new file mode 100644 index 00000000000..4a74b89cb00 --- /dev/null +++ b/packages/yew-functional-macro/tests/function_attr/generic-props-fail.stderr @@ -0,0 +1,8 @@ +error[E0599]: no method named `build` found for struct `PropsBuilder` in the current scope + --> $DIR/generic-props-fail.rs:17:14 + | +4 | #[derive(Clone, Properties, PartialEq)] + | ---------- method `build` not found for this +... +17 | html! { /> }; + | ^^^^ method not found in `PropsBuilder` From 129b424eaafd2c9178b568d68d99e92899d55c72 Mon Sep 17 00:00:00 2001 From: Luke Chu <37006668+lukechu10@users.noreply.github.com> Date: Mon, 22 Feb 2021 19:20:09 -0800 Subject: [PATCH 03/18] Clippy --- packages/yew-functional-macro/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/yew-functional-macro/src/lib.rs b/packages/yew-functional-macro/src/lib.rs index 53509e71513..c76dba8dd9b 100644 --- a/packages/yew-functional-macro/src/lib.rs +++ b/packages/yew-functional-macro/src/lib.rs @@ -192,7 +192,7 @@ fn function_component_impl( let ret_type = quote_spanned!(return_type.span()=> ::yew::html::Html); - let quoted = if generics.params.len() == 0 { + let quoted = if generics.params.is_empty() { quote! { #[doc(hidden)] #[allow(non_camel_case_types)] From 5147b19b729d2c786f6c898007aa9fef7cbbfd2b Mon Sep 17 00:00:00 2001 From: Luke Chu <37006668+lukechu10@users.noreply.github.com> Date: Mon, 22 Feb 2021 19:27:38 -0800 Subject: [PATCH 04/18] Fix generic-props-fail snapshot --- .../tests/function_attr/generic-props-fail.stderr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/yew-functional-macro/tests/function_attr/generic-props-fail.stderr b/packages/yew-functional-macro/tests/function_attr/generic-props-fail.stderr index 4a74b89cb00..ba038b0df47 100644 --- a/packages/yew-functional-macro/tests/function_attr/generic-props-fail.stderr +++ b/packages/yew-functional-macro/tests/function_attr/generic-props-fail.stderr @@ -1,8 +1,8 @@ error[E0599]: no method named `build` found for struct `PropsBuilder` in the current scope - --> $DIR/generic-props-fail.rs:17:14 + --> $DIR/generic-props-fail.rs:18:14 | 4 | #[derive(Clone, Properties, PartialEq)] | ---------- method `build` not found for this ... -17 | html! { /> }; +18 | html! { /> }; // missing prop 'a' | ^^^^ method not found in `PropsBuilder` From 29202483446521bece5128ffc8c72faf84eade39 Mon Sep 17 00:00:00 2001 From: Luke Chu <37006668+lukechu10@users.noreply.github.com> Date: Mon, 22 Feb 2021 19:34:05 -0800 Subject: [PATCH 05/18] Add docs for generic function components --- .../concepts/function-components/attribute.md | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/docs/concepts/function-components/attribute.md b/docs/concepts/function-components/attribute.md index 4dd99078eae..989d718a0c2 100644 --- a/docs/concepts/function-components/attribute.md +++ b/docs/concepts/function-components/attribute.md @@ -3,14 +3,14 @@ title: #[function_component] description: The #[function_component] attribute --- - -The `#[function_component(_)]` turns a normal Rust function into a function component. -Functions with the attribute have to return `Html` and may take a single parameter for the type of props the component should accept. +`#[function_component(_)]` turns a normal Rust function into a function component. +Functions with the attribute have to return `Html` and may take a single parameter for the type of props the component should accept. The parameter type needs to be a reference to a type which implements `Properties` and `PartialEq` (ex. `props: &MyProps`). If the function doesn't have any parameters the resulting component doesn't accept any props. The attribute doesn't replace your original function with a component. You need to provide a name as an input to the attribute which will be the identifier of the component. Assuming you have a function called `chat_container` and you add the attribute `#[function_component(ChatContainer)]` you can use the component like this: + ```rust html! { } ``` @@ -19,6 +19,7 @@ html! { } + ```rust #[derive(Properties, Clone, PartialEq)] pub struct RenderedAtProps { @@ -37,6 +38,7 @@ pub fn rendered_at(props: &RenderedAtProps) -> Html { ``` + ```rust #[function_component(App)] fn app() -> Html { @@ -46,7 +48,7 @@ fn app() -> Html { let counter = Rc::clone(&counter); Callback::from(move |_| set_counter(*counter + 1)) }; - + html! {
@@ -58,4 +60,26 @@ fn app() -> Html { } } ``` + + +## Generic function components + +The `#[function_component(_)]` attribute also works with generic functions for creating generic components. + +```rust +pub struct Props { + data: T, +} + +#[function_component(MyGenericComponent)] +pub fn my_generic_component(props: Props) -> Html + where T: Display +{ + html! { +

+ { props.data } +

+ } +} +``` From 4a933934779444b38916d711280ace0ec13182ce Mon Sep 17 00:00:00 2001 From: Luke Chu <37006668+lukechu10@users.noreply.github.com> Date: Mon, 22 Feb 2021 19:35:37 -0800 Subject: [PATCH 06/18] Add some more docs --- docs/concepts/function-components/attribute.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/concepts/function-components/attribute.md b/docs/concepts/function-components/attribute.md index 989d718a0c2..baffa5f7932 100644 --- a/docs/concepts/function-components/attribute.md +++ b/docs/concepts/function-components/attribute.md @@ -82,4 +82,13 @@ pub fn my_generic_component(props: Props) -> Html

} } + +// used like this +html! { + data=123 /> +} +// or +html! { + data=foo /> +} ``` From 915dfecf2995f9eedb72de322f0e655207cb4bca Mon Sep 17 00:00:00 2001 From: Luke Chu <37006668+lukechu10@users.noreply.github.com> Date: Mon, 22 Feb 2021 19:53:57 -0800 Subject: [PATCH 07/18] Fix trait bounds in docs --- docs/concepts/function-components/attribute.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/concepts/function-components/attribute.md b/docs/concepts/function-components/attribute.md index baffa5f7932..bbfc21583b4 100644 --- a/docs/concepts/function-components/attribute.md +++ b/docs/concepts/function-components/attribute.md @@ -68,13 +68,16 @@ fn app() -> Html { The `#[function_component(_)]` attribute also works with generic functions for creating generic components. ```rust -pub struct Props { +#[derive(Properties, Clone, PartialEq)] +pub struct Props + where T: Clone + PartialEq +{ data: T, } #[function_component(MyGenericComponent)] pub fn my_generic_component(props: Props) -> Html - where T: Display + where T: Clone + PartialEq + Display { html! {

From a570b963a44135c9a0c1f72644bc84c07c631bd2 Mon Sep 17 00:00:00 2001 From: Luke Chu <37006668+lukechu10@users.noreply.github.com> Date: Mon, 22 Feb 2021 19:54:20 -0800 Subject: [PATCH 08/18] Fix docs --- docs/concepts/function-components/attribute.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/concepts/function-components/attribute.md b/docs/concepts/function-components/attribute.md index bbfc21583b4..aa7262df48b 100644 --- a/docs/concepts/function-components/attribute.md +++ b/docs/concepts/function-components/attribute.md @@ -76,7 +76,7 @@ pub struct Props } #[function_component(MyGenericComponent)] -pub fn my_generic_component(props: Props) -> Html +pub fn my_generic_component(props: &Props) -> Html where T: Clone + PartialEq + Display { html! { From 134035b9edfca06fc8e00da2f0d04bcdf0a43c56 Mon Sep 17 00:00:00 2001 From: Luke Chu <37006668+lukechu10@users.noreply.github.com> Date: Tue, 23 Feb 2021 09:21:26 -0800 Subject: [PATCH 09/18] Improve lifetime error messages --- packages/yew-functional-macro/src/lib.rs | 7 +++++++ .../tests/function_attr/generic-lifetime-fail.rs | 1 - .../tests/function_attr/generic-lifetime-fail.stderr | 10 ++++------ 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/yew-functional-macro/src/lib.rs b/packages/yew-functional-macro/src/lib.rs index c76dba8dd9b..8874f1ea312 100644 --- a/packages/yew-functional-macro/src/lib.rs +++ b/packages/yew-functional-macro/src/lib.rs @@ -33,6 +33,13 @@ impl Parse for FunctionComponent { block, } = func; + if sig.generics.lifetimes().next().is_some() { + return Err(syn::Error::new_spanned( + sig.generics, + "function components can't have lifetime generics", + )); + } + if sig.asyncness.is_some() { return Err(syn::Error::new_spanned( sig.asyncness, diff --git a/packages/yew-functional-macro/tests/function_attr/generic-lifetime-fail.rs b/packages/yew-functional-macro/tests/function_attr/generic-lifetime-fail.rs index 9ac4796b819..4eec272b87e 100644 --- a/packages/yew-functional-macro/tests/function_attr/generic-lifetime-fail.rs +++ b/packages/yew-functional-macro/tests/function_attr/generic-lifetime-fail.rs @@ -6,7 +6,6 @@ struct Props { a: usize, } -// TODO: improve error message #[function_component(Comp)] fn comp<'a>(props: &Props) -> Html { html! { diff --git a/packages/yew-functional-macro/tests/function_attr/generic-lifetime-fail.stderr b/packages/yew-functional-macro/tests/function_attr/generic-lifetime-fail.stderr index a2f27c6e78c..e4b7c3d8a30 100644 --- a/packages/yew-functional-macro/tests/function_attr/generic-lifetime-fail.stderr +++ b/packages/yew-functional-macro/tests/function_attr/generic-lifetime-fail.stderr @@ -1,7 +1,5 @@ -error[E0392]: parameter `'a` is never used - --> $DIR/generic-lifetime-fail.rs:11:9 +error: function components can't have lifetime generics + --> $DIR/generic-lifetime-fail.rs:10:8 | -11 | fn comp<'a>(props: &Props) -> Html { - | ^^ unused parameter - | - = help: consider removing `'a`, referring to it in a field, or using a marker such as `std::marker::PhantomData` +10 | fn comp<'a>(props: &Props) -> Html { + | ^^^^ From e9e9416f6a658ce4ebeecb8f4c2c4230983a3321 Mon Sep 17 00:00:00 2001 From: Luke Chu <37006668+lukechu10@users.noreply.github.com> Date: Tue, 23 Feb 2021 10:21:24 -0800 Subject: [PATCH 10/18] Fix parsing for const generics --- packages/yew-functional-macro/src/lib.rs | 4 ++-- .../tests/function_attr/generic-pass.rs | 10 +++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/yew-functional-macro/src/lib.rs b/packages/yew-functional-macro/src/lib.rs index 8874f1ea312..55a31b821ce 100644 --- a/packages/yew-functional-macro/src/lib.rs +++ b/packages/yew-functional-macro/src/lib.rs @@ -226,7 +226,7 @@ fn function_component_impl( #[doc(hidden)] #[allow(non_camel_case_types)] #[allow(unused_parens)] - #vis struct #function_name #ty_generics { + #vis struct #function_name #impl_generics { _marker: ::std::marker::PhantomData<(#phantom_generics)>, } @@ -239,7 +239,7 @@ fn function_component_impl( } #(#attrs)* - #vis type #component_name #ty_generics = ::yew_functional::FunctionComponent<#function_name #ty_generics>; + #vis type #component_name #impl_generics = ::yew_functional::FunctionComponent<#function_name #ty_generics>; } }; diff --git a/packages/yew-functional-macro/tests/function_attr/generic-pass.rs b/packages/yew-functional-macro/tests/function_attr/generic-pass.rs index 6e3676335e3..75f150f2d94 100644 --- a/packages/yew-functional-macro/tests/function_attr/generic-pass.rs +++ b/packages/yew-functional-macro/tests/function_attr/generic-pass.rs @@ -30,7 +30,15 @@ fn comp2(_props: &()) -> Html { } } -// TODO: add test for const generics +// TODO: uncomment when min_const_generics are in stable and MSRV is updated to support it +// #[function_component(ConstGenerics)] +// fn const_generics() -> Html { +// html! { +//

+// { N } +//
+// } +// } fn compile_pass() { html! { a=10 /> }; From 87a866b3d2e216248cdf8cee0e30cdad123ee3d5 Mon Sep 17 00:00:00 2001 From: Luke Chu <37006668+lukechu10@users.noreply.github.com> Date: Tue, 23 Feb 2021 10:29:08 -0800 Subject: [PATCH 11/18] Use fully qualified path for pass test --- .../tests/function_attr/generic-pass.rs | 41 ++++++++----------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/packages/yew-functional-macro/tests/function_attr/generic-pass.rs b/packages/yew-functional-macro/tests/function_attr/generic-pass.rs index 75f150f2d94..34eee7aa777 100644 --- a/packages/yew-functional-macro/tests/function_attr/generic-pass.rs +++ b/packages/yew-functional-macro/tests/function_attr/generic-pass.rs @@ -1,39 +1,29 @@ -use yew::prelude::*; -use yew_functional::function_component; - -#[derive(Clone, Properties, PartialEq)] +#[derive(Clone, ::yew::Properties, PartialEq)] struct Props { a: usize, } -#[function_component(Comp)] -fn comp

(_props: &P) -> Html +#[::yew_functional::function_component(Comp)] +fn comp

(_props: &P) -> ::yew::Html where - P: Properties + PartialEq, + P: ::yew::Properties + PartialEq, { - html! { -

- } -} - -#[function_component(Comp1)] -fn comp1(_props: &P) -> Html { - html! { + ::yew::html! {

} } -#[function_component(Comp2)] -fn comp2(_props: &()) -> Html { - html! { +#[::yew_functional::function_component(Comp1)] +fn comp1(_props: &()) -> ::yew::Html { + ::yew::html! {

} } -// TODO: uncomment when min_const_generics are in stable and MSRV is updated to support it -// #[function_component(ConstGenerics)] -// fn const_generics() -> Html { -// html! { +// TODO: uncomment when min_const_generics are in stable and Rust version in CI is bumped +// #[::yew_functional::function_component(ConstGenerics)] +// fn const_generics() -> ::yew::Html { +// ::yew::html! { //
// { N } //
@@ -41,9 +31,10 @@ fn comp2(_props: &()) -> Html { // } fn compile_pass() { - html! { a=10 /> }; - html! { a=10 /> }; - html! { /> }; + ::yew::html! { a=10 /> }; + ::yew::html! { /> }; + + // ::yew::html! { }; } fn main() {} From af1eb2db6f23fa50bb9c74a45a1f090fc1db163c Mon Sep 17 00:00:00 2001 From: Luke Chu <37006668+lukechu10@users.noreply.github.com> Date: Tue, 23 Feb 2021 10:43:55 -0800 Subject: [PATCH 12/18] Remove TODO message --- .../tests/function_attr/generic-props-fail.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/yew-functional-macro/tests/function_attr/generic-props-fail.rs b/packages/yew-functional-macro/tests/function_attr/generic-props-fail.rs index fc0d6c2828a..e829cf663d2 100644 --- a/packages/yew-functional-macro/tests/function_attr/generic-props-fail.rs +++ b/packages/yew-functional-macro/tests/function_attr/generic-props-fail.rs @@ -6,7 +6,6 @@ struct Props { a: usize, } -// TODO: improve error message #[function_component(Comp)] fn comp(_props: &P) -> Html { html! { From 1202fc218b1a466d50b50dddfb7cf79643e706c3 Mon Sep 17 00:00:00 2001 From: Luke Chu <37006668+lukechu10@users.noreply.github.com> Date: Tue, 23 Feb 2021 14:19:03 -0800 Subject: [PATCH 13/18] Suggested change Co-authored-by: Simon --- packages/yew-functional-macro/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/yew-functional-macro/src/lib.rs b/packages/yew-functional-macro/src/lib.rs index 55a31b821ce..84eaccc5b33 100644 --- a/packages/yew-functional-macro/src/lib.rs +++ b/packages/yew-functional-macro/src/lib.rs @@ -36,7 +36,7 @@ impl Parse for FunctionComponent { if sig.generics.lifetimes().next().is_some() { return Err(syn::Error::new_spanned( sig.generics, - "function components can't have lifetime generics", + "function components can't have generic lifetime parameters", )); } From 69d4015f364e0c945472e795b1655bb13b3c2e94 Mon Sep 17 00:00:00 2001 From: Luke Chu <37006668+lukechu10@users.noreply.github.com> Date: Tue, 23 Feb 2021 14:31:00 -0800 Subject: [PATCH 14/18] Update test Co-authored-by: Simon --- .../tests/function_attr/generic-lifetime-fail.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/yew-functional-macro/tests/function_attr/generic-lifetime-fail.rs b/packages/yew-functional-macro/tests/function_attr/generic-lifetime-fail.rs index 4eec272b87e..cbf2921254f 100644 --- a/packages/yew-functional-macro/tests/function_attr/generic-lifetime-fail.rs +++ b/packages/yew-functional-macro/tests/function_attr/generic-lifetime-fail.rs @@ -7,7 +7,8 @@ struct Props { } #[function_component(Comp)] -fn comp<'a>(props: &Props) -> Html { +fn comp<'a>(props: &'a Props) -> Html { + html! {

{ props.a } From fd7bcc9505b782ca6a03e9a9e1af4bc0dec03a71 Mon Sep 17 00:00:00 2001 From: Luke Chu <37006668+lukechu10@users.noreply.github.com> Date: Tue, 23 Feb 2021 14:31:09 -0800 Subject: [PATCH 15/18] Update test Co-authored-by: Simon --- .../tests/function_attr/generic-props-fail.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/yew-functional-macro/tests/function_attr/generic-props-fail.rs b/packages/yew-functional-macro/tests/function_attr/generic-props-fail.rs index e829cf663d2..b96456bd75f 100644 --- a/packages/yew-functional-macro/tests/function_attr/generic-props-fail.rs +++ b/packages/yew-functional-macro/tests/function_attr/generic-props-fail.rs @@ -13,8 +13,18 @@ fn comp(_props: &P) -> Html { } } -fn compile_pass() { - html! { /> }; // missing prop 'a' +fn compile_fail() { + // missing prop 'a' + html! { /> }; + + // invalid type parameter + html! { /> }; + // parameter doesn't match bounds + html! { /> }; + + // missing type param + html! { }; } + fn main() {} From ff97110ac7262fd76dc2a2481ac019ed2d11bcb7 Mon Sep 17 00:00:00 2001 From: Luke Chu <37006668+lukechu10@users.noreply.github.com> Date: Tue, 23 Feb 2021 14:48:16 -0800 Subject: [PATCH 16/18] Update stderr snapshots --- .../generic-lifetime-fail.stderr | 4 +- .../tests/function_attr/generic-props-fail.rs | 12 +++-- .../function_attr/generic-props-fail.stderr | 54 ++++++++++++++++++- 3 files changed, 62 insertions(+), 8 deletions(-) diff --git a/packages/yew-functional-macro/tests/function_attr/generic-lifetime-fail.stderr b/packages/yew-functional-macro/tests/function_attr/generic-lifetime-fail.stderr index e4b7c3d8a30..e934e01fe69 100644 --- a/packages/yew-functional-macro/tests/function_attr/generic-lifetime-fail.stderr +++ b/packages/yew-functional-macro/tests/function_attr/generic-lifetime-fail.stderr @@ -1,5 +1,5 @@ -error: function components can't have lifetime generics +error: function components can't have generic lifetime parameters --> $DIR/generic-lifetime-fail.rs:10:8 | -10 | fn comp<'a>(props: &Props) -> Html { +10 | fn comp<'a>(props: &'a Props) -> Html { | ^^^^ diff --git a/packages/yew-functional-macro/tests/function_attr/generic-props-fail.rs b/packages/yew-functional-macro/tests/function_attr/generic-props-fail.rs index b96456bd75f..96b7c0bcb46 100644 --- a/packages/yew-functional-macro/tests/function_attr/generic-props-fail.rs +++ b/packages/yew-functional-macro/tests/function_attr/generic-props-fail.rs @@ -7,24 +7,28 @@ struct Props { } #[function_component(Comp)] -fn comp(_props: &P) -> Html { +fn comp

(_props: &P) -> Html +where + P: Properties + PartialEq, +{ html! {

} } +struct MissingTypeBounds; + fn compile_fail() { // missing prop 'a' html! { /> }; - + // invalid type parameter html! { /> }; // parameter doesn't match bounds - html! { /> }; + html! { /> }; // missing type param html! { }; } - fn main() {} diff --git a/packages/yew-functional-macro/tests/function_attr/generic-props-fail.stderr b/packages/yew-functional-macro/tests/function_attr/generic-props-fail.stderr index ba038b0df47..1d1bfcd3dc1 100644 --- a/packages/yew-functional-macro/tests/function_attr/generic-props-fail.stderr +++ b/packages/yew-functional-macro/tests/function_attr/generic-props-fail.stderr @@ -1,8 +1,58 @@ +error[E0412]: cannot find type `INVALID` in this scope + --> $DIR/generic-props-fail.rs:26:19 + | +21 | fn compile_fail() { + | - help: you might be missing a type parameter: `` +... +26 | html! { /> }; + | ^^^^^^^ not found in this scope + error[E0599]: no method named `build` found for struct `PropsBuilder` in the current scope - --> $DIR/generic-props-fail.rs:18:14 + --> $DIR/generic-props-fail.rs:23:14 | 4 | #[derive(Clone, Properties, PartialEq)] | ---------- method `build` not found for this ... -18 | html! { /> }; // missing prop 'a' +23 | html! { /> }; | ^^^^ method not found in `PropsBuilder` + | + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following trait defines an item `build`, perhaps you need to implement it: + candidate #1: `proc_macro::bridge::server::TokenStreamBuilder` + +error[E0599]: no function or associated item named `new` found for struct `yew::virtual_dom::vcomp::VChild>>` in the current scope + --> $DIR/generic-props-fail.rs:28:14 + | +28 | html! { /> }; + | ^^^^ function or associated item not found in `yew::virtual_dom::vcomp::VChild>>` + | + ::: $WORKSPACE/packages/yew-functional/src/lib.rs + | + | pub struct FunctionComponent { + | ----------------------------------------------------------- doesn't satisfy `_: yew::html::component::Component` + | + = note: the method `new` exists but the following trait bounds were not satisfied: + `yew_functional::FunctionComponent>: yew::html::component::Component` + +error[E0277]: the trait bound `MissingTypeBounds: yew::html::component::properties::Properties` is not satisfied + --> $DIR/generic-props-fail.rs:28:14 + | +28 | html! { /> }; + | ^^^^ the trait `yew::html::component::properties::Properties` is not implemented for `MissingTypeBounds` + | + = note: required because of the requirements on the impl of `yew_functional::FunctionProvider` for `comp` + +error[E0277]: can't compare `MissingTypeBounds` with `MissingTypeBounds` + --> $DIR/generic-props-fail.rs:28:14 + | +28 | html! { /> }; + | ^^^^ no implementation for `MissingTypeBounds == MissingTypeBounds` + | + = help: the trait `std::cmp::PartialEq` is not implemented for `MissingTypeBounds` + = note: required because of the requirements on the impl of `yew_functional::FunctionProvider` for `comp` + +error[E0107]: wrong number of type arguments: expected 1, found 0 + --> $DIR/generic-props-fail.rs:31:14 + | +31 | html! { }; + | ^^^^ expected 1 type argument From 35f6ae40db80f4220743fe93bb58b8ad4ea643db Mon Sep 17 00:00:00 2001 From: Luke Chu <37006668+lukechu10@users.noreply.github.com> Date: Tue, 23 Feb 2021 14:50:45 -0800 Subject: [PATCH 17/18] Combine quote implementations --- packages/yew-functional-macro/src/lib.rs | 55 ++++++++---------------- 1 file changed, 18 insertions(+), 37 deletions(-) diff --git a/packages/yew-functional-macro/src/lib.rs b/packages/yew-functional-macro/src/lib.rs index 84eaccc5b33..73d94460a09 100644 --- a/packages/yew-functional-macro/src/lib.rs +++ b/packages/yew-functional-macro/src/lib.rs @@ -199,48 +199,29 @@ fn function_component_impl( let ret_type = quote_spanned!(return_type.span()=> ::yew::html::Html); - let quoted = if generics.params.is_empty() { - quote! { - #[doc(hidden)] - #[allow(non_camel_case_types)] - #vis struct #function_name; - - impl ::yew_functional::FunctionProvider for #function_name { - type TProps = #props_type; - - fn run(#arg) -> #ret_type { - #block - } - } - - #(#attrs)* - #vis type #component_name = ::yew_functional::FunctionComponent<#function_name>; + let phantom_generics = generics + .type_params() + .map(|ty_param| ty_param.ident.clone()) // create a new Punctuated sequence without any type bounds + .collect::>(); + + let quoted = quote! { + #[doc(hidden)] + #[allow(non_camel_case_types)] + #[allow(unused_parens)] + #vis struct #function_name #impl_generics { + _marker: ::std::marker::PhantomData<(#phantom_generics)>, } - } else { - let phantom_generics = generics - .type_params() - .map(|ty_param| ty_param.ident.clone()) // create a new Punctuated sequence without any type bounds - .collect::>(); - - quote! { - #[doc(hidden)] - #[allow(non_camel_case_types)] - #[allow(unused_parens)] - #vis struct #function_name #impl_generics { - _marker: ::std::marker::PhantomData<(#phantom_generics)>, - } - impl #impl_generics ::yew_functional::FunctionProvider for #function_name #ty_generics #where_clause { - type TProps = #props_type; + impl #impl_generics ::yew_functional::FunctionProvider for #function_name #ty_generics #where_clause { + type TProps = #props_type; - fn run(#arg) -> #ret_type { - #block - } + fn run(#arg) -> #ret_type { + #block } - - #(#attrs)* - #vis type #component_name #impl_generics = ::yew_functional::FunctionComponent<#function_name #ty_generics>; } + + #(#attrs)* + #vis type #component_name #impl_generics = ::yew_functional::FunctionComponent<#function_name #ty_generics>; }; Ok(quoted) From 014ccecd783e7cc788966fd104276e4d474ef707 Mon Sep 17 00:00:00 2001 From: Luke Chu <37006668+lukechu10@users.noreply.github.com> Date: Tue, 23 Feb 2021 15:10:25 -0800 Subject: [PATCH 18/18] Fix warning about type alias bounds --- packages/yew-functional-macro/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/yew-functional-macro/src/lib.rs b/packages/yew-functional-macro/src/lib.rs index 73d94460a09..902ba1f135a 100644 --- a/packages/yew-functional-macro/src/lib.rs +++ b/packages/yew-functional-macro/src/lib.rs @@ -221,6 +221,7 @@ fn function_component_impl( } #(#attrs)* + #[allow(type_alias_bounds)] #vis type #component_name #impl_generics = ::yew_functional::FunctionComponent<#function_name #ty_generics>; };