|
1 |
| -use proc_macro::TokenStream; |
2 | 1 | use proc_macro2::TokenStream as TokenStream2;
|
3 | 2 |
|
4 | 3 | use syn::spanned::Spanned;
|
@@ -48,7 +47,7 @@ pub(crate) fn impl_empty_nativeclass(derive_input: &DeriveInput) -> TokenStream2
|
48 | 47 | }
|
49 | 48 | }
|
50 | 49 |
|
51 |
| -pub(crate) fn derive_native_class(derive_input: &DeriveInput) -> Result<TokenStream, syn::Error> { |
| 50 | +pub(crate) fn derive_native_class(derive_input: &DeriveInput) -> Result<TokenStream2, syn::Error> { |
52 | 51 | let derived = crate::automatically_derived();
|
53 | 52 | let data = parse_derive_input(derive_input)?;
|
54 | 53 |
|
@@ -93,21 +92,27 @@ pub(crate) fn derive_native_class(derive_input: &DeriveInput) -> Result<TokenStr
|
93 | 92 | .map(|ty| quote!(::<#ty>)),
|
94 | 93 | _ => None,
|
95 | 94 | };
|
96 |
| - // #[property] is not attached on `Property<T>` |
97 |
| - if property_ty.is_none() |
98 |
| - // custom getter used |
99 |
| - && config.get.as_ref().map(|get| !matches!(get, PropertyGet::Default)).unwrap_or(false) |
100 |
| - // custom setter used |
101 |
| - && config.set.as_ref().map(|set| !matches!(set, PropertySet::Default)).unwrap_or(false) |
| 95 | + |
| 96 | + // Attribute is #[property] (or has other arguments which are not relevant here) |
| 97 | + let is_standalone_attribute = config.get.is_none() && config.set.is_none(); |
| 98 | + // Attribute is #[property(get)] or #[property(get, set="path")] |
| 99 | + let has_default_getter = matches!(config.get, Some(PropertyGet::Default)); |
| 100 | + // Attribute is #[property(set)] or #[property(get="path", set)] |
| 101 | + let has_default_setter = matches!(config.set, Some(PropertySet::Default)); |
| 102 | + |
| 103 | + // Field type is `Property<T>` |
| 104 | + if property_ty.is_some() |
| 105 | + && (is_standalone_attribute || has_default_getter || has_default_setter) |
102 | 106 | {
|
103 | 107 | return Err(syn::Error::new(
|
104 | 108 | ident.span(),
|
105 |
| - "The `#[property]` attribute can only be used on a field of type `Property`, \ |
106 |
| - if a path is provided for both get/set method(s)." |
| 109 | + "The `#[property]` attribute requires explicit paths for `get` and `set` argument; \ |
| 110 | + the defaults #[property], #[property(get)] and #[property(set)] are not allowed." |
107 | 111 | ));
|
108 | 112 | }
|
| 113 | + |
109 | 114 | // if both of them are not set, i.e. `#[property]`. implicitly use both getter/setter
|
110 |
| - let (get, set) = if config.get.is_none() && config.set.is_none() { |
| 115 | + let (get, set) = if is_standalone_attribute { |
111 | 116 | (Some(PropertyGet::Default), Some(PropertySet::Default))
|
112 | 117 | } else {
|
113 | 118 | (config.get, config.set)
|
@@ -206,7 +211,7 @@ pub(crate) fn derive_native_class(derive_input: &DeriveInput) -> Result<TokenStr
|
206 | 211 | };
|
207 | 212 |
|
208 | 213 | // create output token stream
|
209 |
| - Ok(trait_impl.into()) |
| 214 | + Ok(trait_impl) |
210 | 215 | }
|
211 | 216 |
|
212 | 217 | fn parse_derive_input(input: &DeriveInput) -> Result<DeriveData, syn::Error> {
|
@@ -443,21 +448,71 @@ mod tests {
|
443 | 448 | }
|
444 | 449 |
|
445 | 450 | #[test]
|
446 |
| - fn derive_property_require_to_be_used_on_property_without_default_accessor() { |
447 |
| - let input: TokenStream2 = syn::parse_str( |
448 |
| - r#" |
449 |
| - #[inherit(Node)] |
450 |
| - struct Foo { |
451 |
| - #[property(get = "Self::get_bar", set = "Self::set_bar")] |
452 |
| - bar: i64, |
453 |
| - }"#, |
454 |
| - ) |
455 |
| - .unwrap(); |
456 |
| - let input: DeriveInput = syn::parse2(input).unwrap(); |
457 |
| - assert_eq!( |
458 |
| - derive_native_class(&input).unwrap_err().to_string(), |
459 |
| - "The `#[property]` attribute can only be used on a field of type `Property`, \ |
460 |
| - if a path is provided for both get/set method(s).", |
461 |
| - ); |
| 451 | + fn derive_property_combinations() { |
| 452 | + let attr_none = quote! { #[property] }; |
| 453 | + let attr_get = quote! { #[property(get )] }; |
| 454 | + let attr_getp = quote! { #[property(get="path" )] }; |
| 455 | + let attr_set = quote! { #[property( set )] }; |
| 456 | + let attr_setp = quote! { #[property( set="path")] }; |
| 457 | + let attr_get_set = quote! { #[property(get, set )] }; |
| 458 | + let attr_get_setp = quote! { #[property(get, set="path")] }; |
| 459 | + let attr_getp_set = quote! { #[property(get="path", set )] }; |
| 460 | + let attr_getp_setp = quote! { #[property(get="path", set="path")] }; |
| 461 | + |
| 462 | + // See documentation of Property<T> for this table |
| 463 | + // Columns: #[property] attributes | i32 style fields | Property<i32> style fields |
| 464 | + let combinations = [ |
| 465 | + (attr_none, true, false), |
| 466 | + (attr_get, true, false), |
| 467 | + (attr_getp, true, true), |
| 468 | + (attr_set, true, false), |
| 469 | + (attr_setp, true, true), |
| 470 | + (attr_get_set, true, false), |
| 471 | + (attr_get_setp, true, false), |
| 472 | + (attr_getp_set, true, false), |
| 473 | + (attr_getp_setp, true, true), |
| 474 | + ]; |
| 475 | + |
| 476 | + for (attr, allowed_bare, allowed_property) in &combinations { |
| 477 | + check_property_combination(attr, quote! { i32 }, *allowed_bare); |
| 478 | + check_property_combination(attr, quote! { Property<i32> }, *allowed_property); |
| 479 | + } |
| 480 | + } |
| 481 | + |
| 482 | + /// Tests whether a certain combination of a `#[property]` attribute (attr) and a field type |
| 483 | + /// (bare i32 or Property<i32>) should compile successfully |
| 484 | + fn check_property_combination( |
| 485 | + attr: &TokenStream2, |
| 486 | + field_type: TokenStream2, |
| 487 | + should_succeed: bool, |
| 488 | + ) { |
| 489 | + // Lazy because of formatting in error message |
| 490 | + let input = || { |
| 491 | + quote! { |
| 492 | + #[inherit(Node)] |
| 493 | + struct Foo { |
| 494 | + #attr |
| 495 | + field: #field_type |
| 496 | + } |
| 497 | + } |
| 498 | + }; |
| 499 | + |
| 500 | + let derive_input: DeriveInput = syn::parse2(input()).unwrap(); |
| 501 | + let derived = derive_native_class(&derive_input); |
| 502 | + |
| 503 | + if should_succeed { |
| 504 | + assert!( |
| 505 | + derived.is_ok(), |
| 506 | + "Valid derive expression fails to compile:\n{}", |
| 507 | + input().to_string() |
| 508 | + ); |
| 509 | + } else { |
| 510 | + assert_eq!( |
| 511 | + derived.unwrap_err().to_string(), |
| 512 | + "The `#[property]` attribute requires explicit paths for `get` and `set` argument; \ |
| 513 | + the defaults #[property], #[property(get)] and #[property(set)] are not allowed.", |
| 514 | + "Invalid derive expression compiles by mistake:\n{}", input().to_string() |
| 515 | + ); |
| 516 | + } |
462 | 517 | }
|
463 | 518 | }
|
0 commit comments