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() {}