Repository: CosmWasm/sylvia (rust)

Issue:
Forwarding serde attributes
In messages, serde attributes should be forwarded for its fields.

Let's consider a function:

```rust
fn foo(&self, _: (DepsMut, Env, MessageInfo), #[serde(default)] field: bool) -> Result<Response, ContractError> {
  todo!()
}
```

Obviously, I want my `field` to have the `#[serde(default)]` attribute so it is optional and `false` by default. Basically, all `serde` attributes should be moved to a new place.

Pull request description:
Forward fields attrs
close #63 

Patch:
diff --git a/sylvia-derive/src/input.rs b/sylvia-derive/src/input.rs
index 75e948c..8fdbe95 100644
--- a/sylvia-derive/src/input.rs
+++ b/sylvia-derive/src/input.rs
@@ -99,16 +99,16 @@ impl<'a> ImplInput<'a> {
 
     pub fn process(&self) -> TokenStream {
         let is_trait = self.item.trait_.is_some();
-        let multitest_helpers =
+
+        let multitest_helpers = if cfg!(feature = "mt") {
             MultitestHelpers::new(self.item, is_trait, &self.attributes.error, &self.generics)
-                .emit();
+                .emit()
+        } else {
+            quote! {}
+        };
 
         if is_trait {
-            if cfg!(feature = "mt") {
-                return multitest_helpers;
-            } else {
-                return quote! {};
-            };
+            return multitest_helpers;
         }
 
         let messages = self.emit_messages();
diff --git a/sylvia-derive/src/message.rs b/sylvia-derive/src/message.rs
index 243ebd2..6b27f53 100644
--- a/sylvia-derive/src/message.rs
+++ b/sylvia-derive/src/message.rs
@@ -14,8 +14,8 @@ use syn::parse::{Parse, Parser};
 use syn::spanned::Spanned;
 use syn::visit::Visit;
 use syn::{
-    parse_quote, GenericParam, Ident, ImplItem, ItemImpl, ItemTrait, Pat, PatType, ReturnType,
-    Signature, TraitItem, Type, WhereClause, WherePredicate,
+    parse_quote, Attribute, GenericParam, Ident, ImplItem, ItemImpl, ItemTrait, Pat, PatType,
+    ReturnType, Signature, TraitItem, Type, WhereClause, WherePredicate,
 };
 
 /// Representation of single struct message
@@ -567,6 +567,7 @@ impl<'a> MsgVariant<'a> {
 pub struct MsgField<'a> {
     name: &'a Ident,
     ty: &'a Type,
+    attrs: &'a Vec<Attribute>,
 }
 
 impl<'a> MsgField<'a> {
@@ -597,16 +598,18 @@ impl<'a> MsgField<'a> {
         }?;
 
         let ty = &item.ty;
+        let attrs = &item.attrs;
         generics_checker.visit_type(ty);
 
-        Some(Self { name, ty })
+        Some(Self { name, ty, attrs })
     }
 
     /// Emits message field
     pub fn emit(&self) -> TokenStream {
-        let Self { name, ty } = self;
+        let Self { name, ty, attrs } = self;
 
         quote! {
+            #(#attrs)*
             #name: #ty
         }
     }
diff --git a/sylvia-derive/src/strip_input.rs b/sylvia-derive/src/strip_input.rs
index 666ed48..6a0e090 100644
--- a/sylvia-derive/src/strip_input.rs
+++ b/sylvia-derive/src/strip_input.rs
@@ -1,23 +1,44 @@
 use syn::fold::{self, Fold};
-use syn::{ImplItemMethod, ItemImpl, TraitItemMethod};
+use syn::punctuated::Punctuated;
+use syn::{FnArg, ImplItemMethod, ItemImpl, PatType, Receiver, Signature, Token, TraitItemMethod};
 
 /// Utility for stripping all attributes from input before it is emitted
 pub struct StripInput;
 
+fn remove_input_attr(inputs: Punctuated<FnArg, Token![,]>) -> Punctuated<FnArg, Token![,]> {
+    inputs
+        .into_iter()
+        .map(|input| match input {
+            syn::FnArg::Receiver(rec) if !rec.attrs.is_empty() => {
+                let rec = Receiver {
+                    attrs: vec![],
+                    ..rec
+                };
+                syn::FnArg::Receiver(rec)
+            }
+            syn::FnArg::Typed(ty) if !ty.attrs.is_empty() => {
+                let ty = PatType {
+                    attrs: vec![],
+                    ..ty
+                };
+                syn::FnArg::Typed(ty)
+            }
+            _ => input,
+        })
+        .collect()
+}
+
 impl Fold for StripInput {
     fn fold_trait_item_method(&mut self, i: TraitItemMethod) -> TraitItemMethod {
         let attrs = i
             .attrs
             .into_iter()
-            .filter(|attr| !attr.path.is_ident("msg"));
-
-        fold::fold_trait_item_method(
-            self,
-            TraitItemMethod {
-                attrs: attrs.collect(),
-                ..i
-            },
-        )
+            .filter(|attr| !attr.path.is_ident("msg"))
+            .collect();
+
+        let inputs = remove_input_attr(i.sig.inputs);
+        let sig = Signature { inputs, ..i.sig };
+        fold::fold_trait_item_method(self, TraitItemMethod { attrs, sig, ..i })
     }
 
     fn fold_impl_item_method(&mut self, i: ImplItemMethod) -> ImplItemMethod {
@@ -27,7 +48,9 @@ impl Fold for StripInput {
             .filter(|attr| !attr.path.is_ident("msg"))
             .collect();
 
-        fold::fold_impl_item_method(self, ImplItemMethod { attrs, ..i })
+        let inputs = remove_input_attr(i.sig.inputs);
+        let sig = Signature { inputs, ..i.sig };
+        fold::fold_impl_item_method(self, ImplItemMethod { attrs, sig, ..i })
     }
 
     fn fold_item_impl(&mut self, i: ItemImpl) -> ItemImpl {


Test patch:
diff --git a/sylvia/tests/messages_generation.rs b/sylvia/tests/messages_generation.rs
index 0eb6360..c6ce86c 100644
--- a/sylvia/tests/messages_generation.rs
+++ b/sylvia/tests/messages_generation.rs
@@ -20,7 +20,7 @@ pub trait Interface {
         ctx: (DepsMut, Env, MessageInfo),
         addr: Addr,
         coef: Decimal,
-        desc: String,
+        #[serde(default)] desc: String,
     ) -> Result<Response, Self::Error>;
 
     #[msg(query)]
@@ -56,14 +56,13 @@ impl Contract {
         Ok(Response::new())
     }
 
-    #[allow(unused_variables)]
     #[msg(exec)]
     fn argumented_execution(
         &self,
         _ctx: (DepsMut, Env, MessageInfo),
-        addr: Addr,
-        coef: Decimal,
-        desc: String,
+        _addr: Addr,
+        #[serde(default)] _coef: Decimal,
+        #[serde(default)] _desc: String,
     ) -> StdResult<Response> {
         Ok(Response::new())
     }
@@ -73,9 +72,8 @@ impl Contract {
         Ok(QueryResult {})
     }
 
-    #[allow(unused_variables)]
     #[msg(query)]
-    fn argumented_query(&self, _ctx: (Deps, Env), user: Addr) -> StdResult<QueryResult> {
+    fn argumented_query(&self, _ctx: (Deps, Env), _user: Addr) -> StdResult<QueryResult> {
         Ok(QueryResult {})
     }
 }
@@ -109,13 +107,13 @@ fn interface_messages_constructible() {
 fn contract_messages_constructible() {
     let no_args_exec = contract::ExecMsg::NoArgsExecution {};
     let _argumented_exec = contract::ExecMsg::ArgumentedExecution {
-        addr: Addr::unchecked("owner"),
-        coef: Decimal::percent(10),
-        desc: "Some description".to_owned(),
+        _addr: Addr::unchecked("owner"),
+        _coef: Decimal::percent(10),
+        _desc: "Some description".to_owned(),
     };
     let no_args_query = contract::QueryMsg::NoArgsQuery {};
     let _argumented_query = contract::QueryMsg::ArgumentedQuery {
-        user: Addr::unchecked("owner"),
+        _user: Addr::unchecked("owner"),
     };
     let _ = contract::InstantiateMsg {};
     let _ = contract::MigrateMsg {};
diff --git a/sylvia/tests/query_returns.rs b/sylvia/tests/query_returns.rs
index 2c3957f..eeffb50 100644
--- a/sylvia/tests/query_returns.rs
+++ b/sylvia/tests/query_returns.rs
@@ -21,7 +21,7 @@ pub trait Interface {
     type Error: From<StdError>;
 
     #[msg(query, resp=QueryResponse)]
-    fn query(&self, ctx: (Deps, Env)) -> QueryResult<Self::Error>;
+    fn query(&self, ctx: (Deps, Env), #[serde(default)] name: String) -> QueryResult<Self::Error>;
 }
 
 pub struct SomeContract {}
@@ -38,7 +38,11 @@ impl SomeContract {
     }
 
     #[msg(query, resp=QueryResponse)]
-    fn contract_query(&self, _ctx: (Deps, Env)) -> QueryResult<ContractError> {
+    fn contract_query(
+        &self,
+        _ctx: (Deps, Env),
+        #[serde(default)] _name: String,
+    ) -> QueryResult<ContractError> {
         Ok(QueryResponse {})
     }
 }
@@ -49,11 +53,15 @@ mod tests {
 
     #[test]
     fn generate_interface_query() {
-        let _ = msg::InterfaceQueryMsg::Query {};
+        let _ = msg::InterfaceQueryMsg::Query {
+            name: "some_name".to_owned(),
+        };
     }
 
     #[test]
     fn generate_contract_query() {
-        let _ = QueryMsg::ContractQuery {};
+        let _ = QueryMsg::ContractQuery {
+            _name: "some_name".to_owned(),
+        };
     }
 }