From ce24cdde3d7239a62a1b67a16dc2ed5d0da794c8 Mon Sep 17 00:00:00 2001 From: Wester de Cocq Date: Wed, 6 Jul 2022 14:19:32 +0200 Subject: [PATCH 1/4] Use IntoPropValue for node refs in html component --- .../yew-macro/src/html_tree/html_component.rs | 59 +++++++++++-------- .../tests/html_macro/component-fail.stderr | 7 ++- .../tests/html_macro/component-pass.rs | 8 ++- 3 files changed, 46 insertions(+), 28 deletions(-) diff --git a/packages/yew-macro/src/html_tree/html_component.rs b/packages/yew-macro/src/html_tree/html_component.rs index 015267acef3..da66883bf36 100644 --- a/packages/yew-macro/src/html_tree/html_component.rs +++ b/packages/yew-macro/src/html_tree/html_component.rs @@ -12,6 +12,7 @@ use syn::{ use super::{HtmlChildrenTree, TagTokens}; use crate::props::ComponentProps; +use crate::stringify::Stringify; use crate::PeekValue; pub struct HtmlComponent { @@ -108,30 +109,40 @@ impl ToTokens for HtmlComponent { let build_props = props.build_properties_tokens(&props_ty, children_renderer); let special_props = props.special(); - let node_ref = if let Some(node_ref) = &special_props.node_ref { - let value = &node_ref.value; - quote! { #value } - } else { - quote! { <::yew::html::NodeRef as ::std::default::Default>::default() } - }; - - let key = if let Some(key) = &special_props.key { - let value = &key.value; - quote_spanned! {value.span().resolved_at(Span::call_site())=> - #[allow(clippy::useless_conversion)] - Some(::std::convert::Into::<::yew::virtual_dom::Key>::into(#value)) - } - } else { - quote! { ::std::option::Option::None } - }; - let use_close_tag = if let Some(close) = close { - let close_ty = &close.ty; - quote_spanned! {close_ty.span()=> - let _ = |_:#close_ty| {}; - } - } else { - Default::default() - }; + let node_ref = special_props + .node_ref + .as_ref() + .map(|attr| { + let value = &attr.value; + quote_spanned! {value.span().resolved_at(Span::call_site())=> + ::yew::html::IntoPropValue::<::yew::html::NodeRef> + ::into_prop_value(#value) + } + }) + .unwrap_or(quote! { ::std::default::Default::default() }); + + let key = special_props + .key + .as_ref() + .map(|attr| { + let value = attr.value.optimize_literals(); + quote_spanned! {value.span().resolved_at(Span::call_site())=> + ::std::option::Option::Some( + ::std::convert::Into::<::yew::virtual_dom::Key>::into(#value) + ) + } + }) + .unwrap_or(quote! { ::std::option::Option::None }); + + let use_close_tag = close + .as_ref() + .map(|close| { + let close_ty = &close.ty; + quote_spanned! {close_ty.span()=> + let _ = |_:#close_ty| {}; + } + }) + .unwrap_or_default(); tokens.extend(quote_spanned! {ty_span=> { diff --git a/packages/yew-macro/tests/html_macro/component-fail.stderr b/packages/yew-macro/tests/html_macro/component-fail.stderr index 3774f0a0182..2e068c63b21 100644 --- a/packages/yew-macro/tests/html_macro/component-fail.stderr +++ b/packages/yew-macro/tests/html_macro/component-fail.stderr @@ -386,11 +386,14 @@ note: required by a bound in `ChildPropertiesBuilder::string` | ------ required by a bound in this = note: this error originates in the derive macro `Properties` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0308]: mismatched types +error[E0277]: the trait bound `(): IntoPropValue` is not satisfied --> tests/html_macro/component-fail.rs:80:31 | 80 | html! { }; - | ^^ expected struct `NodeRef`, found `()` + | ^^ + | | + | the trait `IntoPropValue` is not implemented for `()` + | required by a bound introduced by this call error[E0277]: the trait bound `u32: IntoPropValue` is not satisfied --> tests/html_macro/component-fail.rs:82:24 diff --git a/packages/yew-macro/tests/html_macro/component-pass.rs b/packages/yew-macro/tests/html_macro/component-pass.rs index 707c08f5f7c..bc703f9a5f3 100644 --- a/packages/yew-macro/tests/html_macro/component-pass.rs +++ b/packages/yew-macro/tests/html_macro/component-pass.rs @@ -55,6 +55,7 @@ impl ::yew::Component for Container { fn create(_ctx: &::yew::Context) -> Self { ::std::unimplemented!() } + fn view(&self, _ctx: &::yew::Context) -> ::yew::Html { ::std::unimplemented!() } @@ -116,6 +117,7 @@ impl ::yew::Component for Child { fn create(_ctx: &::yew::Context) -> Self { ::std::unimplemented!() } + fn view(&self, _ctx: &::yew::Context) -> ::yew::Html { ::std::unimplemented!() } @@ -129,6 +131,7 @@ impl ::yew::Component for AltChild { fn create(_ctx: &::yew::Context) -> Self { ::std::unimplemented!() } + fn view(&self, _ctx: &::yew::Context) -> ::yew::Html { ::std::unimplemented!() } @@ -151,14 +154,14 @@ impl ::yew::Component for ChildContainer { fn create(_ctx: &::yew::Context) -> Self { ::std::unimplemented!() } + fn view(&self, _ctx: &::yew::Context) -> ::yew::Html { ::std::unimplemented!() } } mod scoped { - pub use super::Child; - pub use super::Container; + pub use super::{Child, Container}; } fn compile_pass() { @@ -181,6 +184,7 @@ fn compile_pass() { + ::Properties as ::std::default::Default>::default() /> ::Properties as ::std::default::Default>::default() /> }; From 5a94b874a1d84efdc52bae8121652a8866e74d73 Mon Sep 17 00:00:00 2001 From: Wester de Cocq Date: Wed, 6 Jul 2022 14:26:44 +0200 Subject: [PATCH 2/4] Add NodeRef ImplicitClone test for html element --- packages/yew-macro/tests/html_macro/html-element-pass.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/yew-macro/tests/html_macro/html-element-pass.rs b/packages/yew-macro/tests/html_macro/html-element-pass.rs index c85989c96c9..3729c3ec362 100644 --- a/packages/yew-macro/tests/html_macro/html-element-pass.rs +++ b/packages/yew-macro/tests/html_macro/html-element-pass.rs @@ -51,6 +51,7 @@ fn compile_pass() { ::yew::html! {
+
From a5d4a983341ccbcba3a1532b17e17055076068d9 Mon Sep 17 00:00:00 2001 From: Wester de Cocq Date: Wed, 6 Jul 2022 14:46:28 +0200 Subject: [PATCH 3/4] Change node_refs example to use ImplicitClone --- examples/node_refs/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/node_refs/src/main.rs b/examples/node_refs/src/main.rs index f39bc399058..abba846e1dd 100644 --- a/examples/node_refs/src/main.rs +++ b/examples/node_refs/src/main.rs @@ -79,7 +79,7 @@ impl Component for App { From cb29913c3d5673a506abdfcdaced87ce0361bb02 Mon Sep 17 00:00:00 2001 From: Wester de Cocq Date: Wed, 6 Jul 2022 17:29:32 +0200 Subject: [PATCH 4/4] Reuse key and ref attribute wrapping --- .../yew-macro/src/html_tree/html_component.rs | 30 +------------- .../yew-macro/src/html_tree/html_element.rs | 40 +++---------------- packages/yew-macro/src/props/element.rs | 9 ++--- packages/yew-macro/src/props/prop.rs | 32 ++++++++++++++- 4 files changed, 42 insertions(+), 69 deletions(-) diff --git a/packages/yew-macro/src/html_tree/html_component.rs b/packages/yew-macro/src/html_tree/html_component.rs index da66883bf36..3ec0ac4ba2c 100644 --- a/packages/yew-macro/src/html_tree/html_component.rs +++ b/packages/yew-macro/src/html_tree/html_component.rs @@ -12,7 +12,6 @@ use syn::{ use super::{HtmlChildrenTree, TagTokens}; use crate::props::ComponentProps; -use crate::stringify::Stringify; use crate::PeekValue; pub struct HtmlComponent { @@ -107,33 +106,8 @@ impl ToTokens for HtmlComponent { Some(quote! { ::yew::html::ChildrenRenderer::new(#children) }) }; let build_props = props.build_properties_tokens(&props_ty, children_renderer); - - let special_props = props.special(); - let node_ref = special_props - .node_ref - .as_ref() - .map(|attr| { - let value = &attr.value; - quote_spanned! {value.span().resolved_at(Span::call_site())=> - ::yew::html::IntoPropValue::<::yew::html::NodeRef> - ::into_prop_value(#value) - } - }) - .unwrap_or(quote! { ::std::default::Default::default() }); - - let key = special_props - .key - .as_ref() - .map(|attr| { - let value = attr.value.optimize_literals(); - quote_spanned! {value.span().resolved_at(Span::call_site())=> - ::std::option::Option::Some( - ::std::convert::Into::<::yew::virtual_dom::Key>::into(#value) - ) - } - }) - .unwrap_or(quote! { ::std::option::Option::None }); - + let node_ref = props.special().wrap_node_ref_attr(); + let key = props.special().wrap_key_attr(); let use_close_tag = close .as_ref() .map(|close| { diff --git a/packages/yew-macro/src/html_tree/html_element.rs b/packages/yew-macro/src/html_tree/html_element.rs index 8b981a10d1c..db4dfae77fb 100644 --- a/packages/yew-macro/src/html_tree/html_element.rs +++ b/packages/yew-macro/src/html_tree/html_element.rs @@ -112,37 +112,17 @@ impl ToTokens for HtmlElement { booleans, value, checked, - node_ref, - key, listeners, + special, } = &props; // attributes with special treatment - let node_ref = node_ref - .as_ref() - .map(|attr| { - let value = &attr.value; - quote_spanned! {value.span().resolved_at(Span::call_site())=> - ::yew::html::IntoPropValue::<::yew::html::NodeRef> - ::into_prop_value(#value) - } - }) - .unwrap_or(quote! { ::std::default::Default::default() }); - let key = key - .as_ref() - .map(|attr| { - let value = attr.value.optimize_literals(); - quote_spanned! {value.span().resolved_at(Span::call_site())=> - ::std::option::Option::Some( - ::std::convert::Into::<::yew::virtual_dom::Key>::into(#value) - ) - } - }) - .unwrap_or(quote! { ::std::option::Option::None }); + let node_ref = special.wrap_node_ref_attr(); + let key = special.wrap_key_attr(); let value = value .as_ref() - .map(wrap_attr_prop) + .map(|prop| wrap_attr_value(prop.value.optimize_literals())) .unwrap_or(quote! { ::std::option::Option::None }); let checked = checked .as_ref() @@ -262,14 +242,7 @@ impl ToTokens for HtmlElement { .collect::>(); try_into_static(&attrs).unwrap_or_else(|| { let keys = attrs.iter().map(|(k, _)| quote! { #k }); - let values = attrs.iter().map(|(_, v)| { - quote_spanned! {v.span()=> - ::yew::html::IntoPropValue::< - ::std::option::Option::<::yew::virtual_dom::AttrValue> - > - ::into_prop_value(#v) - } - }); + let values = attrs.iter().map(|(_, v)| wrap_attr_value(v)); quote! { ::yew::virtual_dom::Attributes::Dynamic{ keys: &[#(#keys),*], @@ -473,8 +446,7 @@ impl ToTokens for HtmlElement { } } -fn wrap_attr_prop(prop: &Prop) -> TokenStream { - let value = prop.value.optimize_literals(); +fn wrap_attr_value(value: T) -> TokenStream { quote_spanned! {value.span()=> ::yew::html::IntoPropValue::< ::std::option::Option< diff --git a/packages/yew-macro/src/props/element.rs b/packages/yew-macro/src/props/element.rs index f80af1c2adb..50bc57bf201 100644 --- a/packages/yew-macro/src/props/element.rs +++ b/packages/yew-macro/src/props/element.rs @@ -26,8 +26,7 @@ pub struct ElementProps { pub booleans: Vec, pub value: Option, pub checked: Option, - pub node_ref: Option, - pub key: Option, + pub special: SpecialProps, } impl Parse for ElementProps { @@ -48,8 +47,7 @@ impl Parse for ElementProps { .map(|prop| ClassesForm::from_expr(prop.value)); let value = props.pop("value"); let checked = props.pop("checked"); - - let SpecialProps { node_ref, key } = props.special; + let special = props.special; Ok(Self { attributes: props.prop_list.into_vec(), @@ -58,8 +56,7 @@ impl Parse for ElementProps { checked, booleans: booleans.into_vec(), value, - node_ref, - key, + special, }) } } diff --git a/packages/yew-macro/src/props/prop.rs b/packages/yew-macro/src/props/prop.rs index 6fb4d7522fd..8dbd568efc2 100644 --- a/packages/yew-macro/src/props/prop.rs +++ b/packages/yew-macro/src/props/prop.rs @@ -2,13 +2,16 @@ use std::cmp::Ordering; use std::convert::TryFrom; use std::ops::{Deref, DerefMut}; -use proc_macro2::{Spacing, TokenTree}; +use proc_macro2::{Spacing, Span, TokenStream, TokenTree}; +use quote::{quote, quote_spanned}; use syn::parse::{Parse, ParseBuffer, ParseStream}; +use syn::spanned::Spanned; use syn::token::Brace; use syn::{braced, Block, Expr, ExprBlock, ExprPath, ExprRange, Stmt, Token}; use super::CHILDREN_LABEL; use crate::html_tree::HtmlDashedName; +use crate::stringify::Stringify; pub struct Prop { pub label: HtmlDashedName, @@ -325,6 +328,33 @@ impl SpecialProps { pub fn check_all(&self, f: impl FnMut(&Prop) -> syn::Result<()>) -> syn::Result<()> { crate::join_errors(self.iter().map(f).filter_map(Result::err)) } + + pub fn wrap_node_ref_attr(&self) -> TokenStream { + self.node_ref + .as_ref() + .map(|attr| { + let value = &attr.value; + quote_spanned! {value.span().resolved_at(Span::call_site())=> + ::yew::html::IntoPropValue::<::yew::html::NodeRef> + ::into_prop_value(#value) + } + }) + .unwrap_or(quote! { ::std::default::Default::default() }) + } + + pub fn wrap_key_attr(&self) -> TokenStream { + self.key + .as_ref() + .map(|attr| { + let value = attr.value.optimize_literals(); + quote_spanned! {value.span().resolved_at(Span::call_site())=> + ::std::option::Option::Some( + ::std::convert::Into::<::yew::virtual_dom::Key>::into(#value) + ) + } + }) + .unwrap_or(quote! { ::std::option::Option::None }) + } } pub struct Props {