Skip to content

Commit

Permalink
Properly support patterns in inline_props (#760)
Browse files Browse the repository at this point in the history
* Improve error messages for unsupported patterns

* Support ident with subpatterns as props

* Improve error messages
  • Loading branch information
lukechu10 authored Nov 17, 2024
1 parent 1e492e2 commit 3b84a8b
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 26 deletions.
54 changes: 36 additions & 18 deletions packages/sycamore-macro/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,23 +280,31 @@ fn inline_props_impl(item: &mut ItemFn, attrs: Punctuated<Meta, Token![,]>) -> R
let props = inputs.clone().into_iter().collect::<Vec<_>>();
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}");
Expand Down Expand Up @@ -342,8 +350,18 @@ fn inline_props_impl(item: &mut ItemFn, attrs: Punctuated<Meta, Token![,]>) -> 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;
Expand Down
16 changes: 16 additions & 0 deletions packages/sycamore-macro/tests/component/inline-props-fail.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}
20 changes: 20 additions & 0 deletions packages/sycamore-macro/tests/component/inline-props-fail.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
38 changes: 30 additions & 8 deletions packages/sycamore-macro/tests/component/inline-props-pass.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -47,7 +49,10 @@ fn PropsWithImplGenerics(foo: impl std::fmt::Display + 'static) -> View {
}

#[component(inline_props)]
fn PropsWithMixedImplGenerics<T: std::fmt::Display + 'static>(foo: T, bar: impl std::fmt::Display + 'static) -> View {
fn PropsWithMixedImplGenerics<T: std::fmt::Display + 'static>(
foo: T,
bar: impl std::fmt::Display + 'static,
) -> View {
view! {
(foo.to_string())
(bar.to_string())
Expand All @@ -57,7 +62,10 @@ fn PropsWithMixedImplGenerics<T: std::fmt::Display + 'static>(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),
Expand All @@ -77,18 +85,17 @@ 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()))
}
}

#[component(inline_props)]
fn PropsWithAttributes(
#[prop(default)]
dummy: String,
) -> View {
fn PropsWithAttributes(#[prop(default)] dummy: String) -> View {
fn call_component() -> View {
view! {
PropsWithAttributes {}
Expand All @@ -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() {}

0 comments on commit 3b84a8b

Please sign in to comment.