From 3b84a8b63d7e256dea97c910dc80cf39cb43bd26 Mon Sep 17 00:00:00 2001 From: Luke <37006668+lukechu10@users.noreply.github.com> Date: Sun, 17 Nov 2024 14:32:40 +0000 Subject: [PATCH] Properly support patterns in inline_props (#760) * Improve error messages for unsupported patterns * Support ident with subpatterns as props * Improve error messages --- packages/sycamore-macro/src/component.rs | 54 ++++++++++++------- .../tests/component/inline-props-fail.rs | 16 ++++++ .../tests/component/inline-props-fail.stderr | 20 +++++++ .../tests/component/inline-props-pass.rs | 38 ++++++++++--- 4 files changed, 102 insertions(+), 26 deletions(-) diff --git a/packages/sycamore-macro/src/component.rs b/packages/sycamore-macro/src/component.rs index 3fa2e3f9..f4d58b7c 100644 --- a/packages/sycamore-macro/src/component.rs +++ b/packages/sycamore-macro/src/component.rs @@ -280,23 +280,31 @@ fn inline_props_impl(item: &mut ItemFn, attrs: Punctuated) -> R let props = inputs.clone().into_iter().collect::>(); let generics: &mut Generics = &mut item.sig.generics; let mut fields = Vec::new(); - inputs.into_iter().for_each(|arg| match arg { - FnArg::Receiver(_) => { - unreachable!("receiver cannot be a prop") - } - FnArg::Typed(pat_type) => match *pat_type.pat { - Pat::Ident(ident_pat) => super::inline_props::push_field( - &mut fields, - generics, - pat_type.attrs, - ident_pat.clone().ident, - *pat_type.ty, - ), - _ => { - unreachable!("unexpected pattern!") + for arg in inputs { + match arg { + FnArg::Receiver(receiver) => { + return Err(syn::Error::new( + receiver.span(), + "`self` cannot be a property", + )) } - }, - }); + FnArg::Typed(pat_type) => match *pat_type.pat { + Pat::Ident(ident_pat) => super::inline_props::push_field( + &mut fields, + generics, + pat_type.attrs, + ident_pat.clone().ident, + *pat_type.ty, + ), + _ => { + return Err(syn::Error::new( + pat_type.pat.span(), + "pattern must contain an identifier, properties cannot be unnamed", + )) + } + }, + } + } let generics_phantoms = generics.params.iter().enumerate().filter_map(|(i, param)| { let phantom_ident = format_ident!("__phantom{i}"); @@ -342,8 +350,18 @@ fn inline_props_impl(item: &mut ItemFn, attrs: Punctuated) -> R // Get the ident (technically, patterns) of each prop. let props_pats = props.iter().map(|arg| match arg { - FnArg::Receiver(_) => unreachable!("receiver cannot be a prop"), - FnArg::Typed(arg) => arg.pat.clone(), + FnArg::Receiver(_) => unreachable!(), + FnArg::Typed(arg) => match &*arg.pat { + Pat::Ident(pat) => { + if pat.subpat.is_some() { + let ident = &pat.ident; + quote! { #ident: #pat } + } else { + quote! { #pat } + } + } + _ => unreachable!(), + }, }); // Rewrite function signature. let props_struct_generics = generics.split_for_impl().1; diff --git a/packages/sycamore-macro/tests/component/inline-props-fail.rs b/packages/sycamore-macro/tests/component/inline-props-fail.rs index 8e8a5218..0bcf2bdf 100644 --- a/packages/sycamore-macro/tests/component/inline-props-fail.rs +++ b/packages/sycamore-macro/tests/component/inline-props-fail.rs @@ -5,4 +5,20 @@ fn NotInlineProps() -> View { view! {} } +#[component(inline_props)] +fn ReceiverProp(self) -> View { + view! {} +} + +struct Foo { + bar: i32, +} + +#[component(inline_props)] +fn PatternWithoutIdent(Foo { bar }: Foo) -> View { + view! { + (bar) + } +} + fn main() {} diff --git a/packages/sycamore-macro/tests/component/inline-props-fail.stderr b/packages/sycamore-macro/tests/component/inline-props-fail.stderr index 50c3a360..503f5fca 100644 --- a/packages/sycamore-macro/tests/component/inline-props-fail.stderr +++ b/packages/sycamore-macro/tests/component/inline-props-fail.stderr @@ -3,3 +3,23 @@ error: expected `inline_props` | 3 | #[component(not_inline_props)] | ^^^^^^^^^^^^^^^^ + +error: `self` cannot be a property + --> tests/component/inline-props-fail.rs:9:17 + | +9 | fn ReceiverProp(self) -> View { + | ^^^^ + +error: pattern must contain an identifier, properties cannot be unnamed + --> tests/component/inline-props-fail.rs:18:24 + | +18 | fn PatternWithoutIdent(Foo { bar }: Foo) -> View { + | ^^^ + +error: `self` parameter is only allowed in associated functions + --> tests/component/inline-props-fail.rs:9:17 + | +9 | fn ReceiverProp(self) -> View { + | ^^^^ not semantically valid as function parameter + | + = note: associated functions are those in `impl` or `trait` definitions diff --git a/packages/sycamore-macro/tests/component/inline-props-pass.rs b/packages/sycamore-macro/tests/component/inline-props-pass.rs index 3c6a99c0..6147fb6b 100644 --- a/packages/sycamore-macro/tests/component/inline-props-pass.rs +++ b/packages/sycamore-macro/tests/component/inline-props-pass.rs @@ -1,4 +1,6 @@ -use sycamore::prelude::{component, view, Signal, View, Props}; +#![allow(unused_parens)] + +use sycamore::prelude::{component, view, Props, Signal, View}; #[component(inline_props)] fn NoProps() -> View { @@ -47,7 +49,10 @@ fn PropsWithImplGenerics(foo: impl std::fmt::Display + 'static) -> View { } #[component(inline_props)] -fn PropsWithMixedImplGenerics(foo: T, bar: impl std::fmt::Display + 'static) -> View { +fn PropsWithMixedImplGenerics( + foo: T, + bar: impl std::fmt::Display + 'static, +) -> View { view! { (foo.to_string()) (bar.to_string()) @@ -57,7 +62,10 @@ fn PropsWithMixedImplGenerics(foo: T, bar: impl #[component(inline_props)] fn PropsWithVariousImplGenerics( t1: [impl std::fmt::Display + 'static; 10], - t2: (impl std::fmt::Display + 'static, impl std::fmt::Display + 'static), + t2: ( + impl std::fmt::Display + 'static, + impl std::fmt::Display + 'static, + ), t3: (impl std::fmt::Display + 'static), t4: impl std::fmt::Display + 'static, t5: *const (impl std::fmt::Display + 'static), @@ -77,7 +85,9 @@ fn PropsWithVariousImplGenerics( #[component(inline_props, derive(Clone), derive(Debug))] fn AdditionalStructAttributes(dummy: String) -> View { - let props = AdditionalStructAttributes_Props::builder().dummy(dummy).build(); + let props = AdditionalStructAttributes_Props::builder() + .dummy(dummy) + .build(); view! { (format!("{:?}", props.clone())) @@ -85,10 +95,7 @@ fn AdditionalStructAttributes(dummy: String) -> View { } #[component(inline_props)] -fn PropsWithAttributes( - #[prop(default)] - dummy: String, -) -> View { +fn PropsWithAttributes(#[prop(default)] dummy: String) -> View { fn call_component() -> View { view! { PropsWithAttributes {} @@ -99,4 +106,19 @@ fn PropsWithAttributes( } } +#[derive(Debug)] +struct Foo { + bar: u32, +} + +#[component(inline_props)] +fn PropsWithPatterns(mut a: u32, b @ Foo { bar }: Foo) -> View { + let _ = &mut a; + view! { + (a) + (format!("{b:?}")) + (bar) + } +} + fn main() {}