diff --git a/sylvia-derive/src/contract/communication/reply.rs b/sylvia-derive/src/contract/communication/reply.rs index 4ba0ed1f..0a528200 100644 --- a/sylvia-derive/src/contract/communication/reply.rs +++ b/sylvia-derive/src/contract/communication/reply.rs @@ -111,14 +111,7 @@ impl<'a> Reply<'a> { let sylvia = crate_module(); - let methods_declaration = reply_data.iter().map(|data| { - let method_name = &data.handler_name; - - quote! { - fn #method_name (self) -> #sylvia ::cw_std::SubMsg; - } - }); - + let methods_declaration = reply_data.iter().map(ReplyData::emit_submsg_trait_method); let submsg_reply_setters = reply_data.iter().map(ReplyData::emit_submsg_setter); let submsg_converters: Vec<_> = reply_data .iter() @@ -156,9 +149,10 @@ impl<'a> ReplyVariants<'a> for MsgVariants<'a, GenericParam> { let mut reply_data: Vec = vec![]; self.variants() - .flat_map(ReplyVariant::as_reply_data) - .for_each(|(reply_id, handler_name, handler)| { + .flat_map(ReplyVariant::as_variant_handlers_pair) + .for_each(|(handler, handler_id)| { let reply_on = handler.msg_attr().reply_on(); + let reply_id = handler_id.as_reply_id(); match reply_data .iter_mut() .find(|existing_data| existing_data.reply_id == reply_id) @@ -167,22 +161,20 @@ impl<'a> ReplyVariants<'a> for MsgVariants<'a, GenericParam> { if existing_data .handlers .iter() - .any(|existing_handler| existing_handler.msg_attr().reply_on().excludes(&reply_on)) => + .any(|(_, existing_reply_on)| existing_reply_on.excludes(&reply_on)) => { existing_data.handlers.iter().for_each( - |existing_handler| { + |(existing_function_name, existing_reply_on)| { let existing_reply_id = &existing_data.reply_id; - let existing_reply_on = existing_handler.msg_attr().reply_on(); - let existing_function_name= existing_handler.function_name(); emit_error!(reply_id.span(), "Duplicated reply handler."; - note = existing_data.reply_id.span() => format!("Previous definition of handler={} for reply_on={} defined on `fn {}()`", existing_reply_id, existing_reply_on, existing_function_name); + note = existing_data.reply_id.span() => format!("Previous definition of handler=`{}` for reply_on=`{}` defined on `fn {}()`", existing_reply_id, existing_reply_on, existing_function_name); ) }, ) } - Some(existing_data) => existing_data.handlers.push(handler), - None => reply_data.push(ReplyData::new(reply_id, handler, handler_name)), + Some(existing_data) => existing_data.add_second_handler(handler), + None => reply_data.push(ReplyData::new(reply_id, handler, handler_id)), } }); @@ -195,29 +187,66 @@ struct ReplyData<'a> { /// Unique identifier for the reply. pub reply_id: Ident, /// Unique name of the handler from which the [reply_id](ReplyData::reply_id) was constructed. - pub handler_name: &'a Ident, - /// Handler methods for the reply id. - pub handlers: Vec<&'a MsgVariant<'a>>, + pub handler_id: &'a Ident, + /// Methods handling the reply id for the associated reply on. + pub handlers: Vec<(&'a Ident, ReplyOn)>, + /// Payload parameters associated with the handlers. + pub payload: Vec<&'a MsgField<'a>>, } impl<'a> ReplyData<'a> { - pub fn new(reply_id: Ident, variant: &'a MsgVariant<'a>, handler_name: &'a Ident) -> Self { + pub fn new(reply_id: Ident, variant: &'a MsgVariant<'a>, handler_id: &'a Ident) -> Self { + // Skip the first field reserved for the `data`. + let payload = variant.fields().iter().skip(1).collect::>(); + let method_name = variant.function_name(); + let reply_on = variant.msg_attr().reply_on(); + Self { reply_id, - handler_name, - handlers: vec![variant], + handler_id, + handlers: vec![(method_name, reply_on)], + payload, } } + /// Adds second handler to the reply data provdided their payload signature match. + pub fn add_second_handler(&mut self, new_handler: &'a MsgVariant<'a>) { + let (current_method_name, _) = match self.handlers.first() { + Some(handler) => handler, + _ => return, + }; + + if self.payload.len() != new_handler.fields().len() - 1 { + emit_error!(current_method_name.span(), "Mismatched quantity of method parameters."; + note = self.handler_id.span() => format!("Both `{}` handlers should have the same number of parameters.", self.handler_id); + note = new_handler.function_name().span() => format!("Previous definition of {} handler.", self.handler_id) + ); + } + + self.payload + .iter() + .zip(new_handler.fields().iter().skip(1)) + .for_each(|(current_field, new_field)| + { + if current_field.ty() != new_field.ty() { + emit_error!(current_field.name().span(), "Mismatched parameter in reply handlers."; + note = current_field.name().span() => format!("Parameters for the `{}` handler have to be the same.", self.handler_id); + note = new_field.name().span() => format!("Previous parameter defined for the `{}` handler.", self.handler_id) + ) + } + }); + + let new_function_name = new_handler.function_name(); + let new_reply_on = new_handler.msg_attr().reply_on(); + self.handlers.push((new_function_name, new_reply_on)); + } + /// Emits success and failure match arms for a single `ReplyId`. fn emit_match_arms(&self, contract: &Type, generics: &[&GenericParam]) -> TokenStream { - let Self { - reply_id, handlers, .. - } = self; - + let reply_id = &self.reply_id; let contract_turbofish = emit_turbofish(contract, generics); - let success_match_arm = emit_success_match_arm(handlers, &contract_turbofish); - let failure_match_arm = emit_failure_match_arm(handlers, &contract_turbofish); + let success_match_arm = self.emit_success_match_arm(&contract_turbofish); + let failure_match_arm = self.emit_failure_match_arm(&contract_turbofish); quote! { #reply_id => { @@ -229,20 +258,22 @@ impl<'a> ReplyData<'a> { } } + /// Emits [cosmwasm_std::ReplyOn] value to be put in the `cosmwasm_std::SubMsg`. + /// If both `Success` and `Failure` is defined for given `reply_id`, `cosmwasm_std::ReplyOn::Always` is returned. fn emit_cw_reply_on(&self) -> TokenStream { let sylvia = crate_module(); let is_always = self .handlers .iter() - .any(|handler| handler.msg_attr().reply_on() == ReplyOn::Always); + .any(|(_, reply_on)| reply_on == &ReplyOn::Always); let is_success = self .handlers .iter() - .any(|handler| handler.msg_attr().reply_on() == ReplyOn::Success); + .any(|(_, reply_on)| reply_on == &ReplyOn::Success); let is_failure = self .handlers .iter() - .any(|handler| handler.msg_attr().reply_on() == ReplyOn::Failure); + .any(|(_, reply_on)| reply_on == &ReplyOn::Failure); if is_always || (is_success && is_failure) { quote! { #sylvia ::cw_std::ReplyOn::Always } @@ -258,201 +289,235 @@ impl<'a> ReplyData<'a> { } } + /// Emits method setting reply related fields on the `cosmwasm_std::SubMsg`. fn emit_submsg_setter(&self) -> TokenStream { let sylvia = crate_module(); let Self { reply_id, - handler_name, + handler_id, + payload, .. } = self; - let method_name = handler_name; + let method_name = handler_id; let reply_on = self.emit_cw_reply_on(); + let payload_parameters = payload.iter().map(|field| field.emit_method_field()); + let payload_serialization = payload.emit_payload_serialization(); quote! { - fn #method_name (self) -> #sylvia ::cw_std::SubMsg { - #sylvia ::cw_std::SubMsg { + fn #method_name (self, #(#payload_parameters),* ) -> #sylvia ::cw_std::StdResult< #sylvia ::cw_std::SubMsg> { + #payload_serialization + + Ok( #sylvia ::cw_std::SubMsg { reply_on: #reply_on , id: #reply_id , + payload, ..self - } + }) } } } + /// Emits method for converting `WasmMsg` or `CosmosMsg` to `SubMsg`. fn emit_submsg_converter(&self) -> TokenStream { let sylvia = crate_module(); let Self { reply_id, - handler_name, + handler_id, + payload, .. } = self; - let method_name = handler_name; + let method_name = handler_id; let reply_on = self.emit_cw_reply_on(); + let payload_parameters = payload.iter().map(|field| field.emit_method_field()); + let payload_serialization = payload.emit_payload_serialization(); quote! { - fn #method_name (self) -> #sylvia ::cw_std::SubMsg { - #sylvia ::cw_std::SubMsg { + fn #method_name (self, #(#payload_parameters),* ) -> #sylvia ::cw_std::StdResult< #sylvia ::cw_std::SubMsg> { + #payload_serialization + + Ok( #sylvia ::cw_std::SubMsg { reply_on: #reply_on , id: #reply_id , msg: self.into(), - payload: Default::default(), + payload, gas_limit: None, - } + }) } } } -} -/// Emits match arm for [ReplyOn::Success]. -/// In case neither [ReplyOn::Success] nor [ReplyOn::Always] is present, `Response::events` -/// and `Response::data` are forwarded in the `Response` -fn emit_success_match_arm(handlers: &[&MsgVariant], contract_turbofish: &Type) -> TokenStream { - let sylvia = crate_module(); - - match handlers.iter().find(|handler| { - handler.msg_attr().reply_on() == ReplyOn::Success - || handler.msg_attr().reply_on() == ReplyOn::Always - }) { - Some(handler) if handler.msg_attr().reply_on() == ReplyOn::Success => { - let function_name = handler.function_name(); - let payload = handler.emit_payload_parameters(); - let payload_deserialization = handler.emit_payload_deserialization(); + fn emit_submsg_trait_method(&self) -> TokenStream { + let sylvia = crate_module(); + let method_name = &self.handler_id; + let payload_parameters = self.payload.iter().map(|field| field.emit_method_field()); - quote! { - #sylvia ::cw_std::SubMsgResult::Ok(sub_msg_resp) => { - #[allow(deprecated)] - let #sylvia ::cw_std::SubMsgResponse { events, data, msg_responses} = sub_msg_resp; - #payload_deserialization + quote! { + fn #method_name (self, #(#payload_parameters),* ) -> #sylvia ::cw_std::StdResult< #sylvia ::cw_std::SubMsg>; + } + } + + /// Emits match arm for [ReplyOn::Success]. + /// In case neither [ReplyOn::Success] nor [ReplyOn::Always] is present, `Response::events` + /// and `Response::data` are forwarded in the `Response` + fn emit_success_match_arm(&self, contract_turbofish: &Type) -> TokenStream { + let sylvia = crate_module(); - #contract_turbofish ::new(). #function_name ((deps, env, gas_used, events, msg_responses).into(), data, #payload ) + match self + .handlers + .iter() + .find(|(_, reply_on)| reply_on == &ReplyOn::Success || reply_on == &ReplyOn::Always) + { + Some((method_name, reply_on)) if reply_on == &ReplyOn::Success => { + let payload_values = self.payload.iter().map(|field| field.name()); + let payload_deserialization = self.payload.emit_payload_deserialization(); + + quote! { + #sylvia ::cw_std::SubMsgResult::Ok(sub_msg_resp) => { + #[allow(deprecated)] + let #sylvia ::cw_std::SubMsgResponse { events, data, msg_responses} = sub_msg_resp; + #payload_deserialization + + #contract_turbofish ::new(). #method_name ((deps, env, gas_used, events, msg_responses).into(), data, #(#payload_values),* ) + } } } - } - Some(handler) if handler.msg_attr().reply_on() == ReplyOn::Always => { - let function_name = handler.function_name(); - let payload = handler.emit_payload_parameters(); - let payload_deserialization = handler.emit_payload_deserialization(); + Some((method_name, reply_on)) if reply_on == &ReplyOn::Always => { + let payload_values = self.payload.iter().map(|field| field.name()); + let payload_deserialization = self.payload.emit_payload_deserialization(); - quote! { - #sylvia ::cw_std::SubMsgResult::Ok(_) => { - #payload_deserialization + quote! { + #sylvia ::cw_std::SubMsgResult::Ok(_) => { + #payload_deserialization - #contract_turbofish ::new(). #function_name ((deps, env, gas_used, vec![], vec![]).into(), result, #payload ) + #contract_turbofish ::new(). #method_name ((deps, env, gas_used, vec![], vec![]).into(), result, #(#payload_values),* ) + } } } - } - _ => quote! { - #sylvia ::cw_std::SubMsgResult::Ok(sub_msg_resp) => { - let mut resp = sylvia::cw_std::Response::new().add_events(sub_msg_resp.events); + _ => quote! { + #sylvia ::cw_std::SubMsgResult::Ok(sub_msg_resp) => { + let mut resp = sylvia::cw_std::Response::new().add_events(sub_msg_resp.events); - #[allow(deprecated)] - if sub_msg_resp.data.is_some() { - resp = resp.set_data(sub_msg_resp.data.unwrap()); - } + #[allow(deprecated)] + if sub_msg_resp.data.is_some() { + resp = resp.set_data(sub_msg_resp.data.unwrap()); + } - Ok(resp) - } - }, + Ok(resp) + } + }, + } } -} -/// Emits match arm for [ReplyOn::Failure]. -/// In case neither [ReplyOn::Failure] nor [ReplyOn::Always] is present, -/// the error is forwarded. -fn emit_failure_match_arm(handlers: &[&MsgVariant], contract_turbofish: &Type) -> TokenStream { - let sylvia = crate_module(); - - match handlers.iter().find(|handler| { - handler.msg_attr().reply_on() == ReplyOn::Failure - || handler.msg_attr().reply_on() == ReplyOn::Always - }) { - Some(handler) if handler.msg_attr().reply_on() == ReplyOn::Failure => { - let function_name = handler.function_name(); - let payload = handler.emit_payload_parameters(); - let payload_deserialization = handler.emit_payload_deserialization(); + /// Emits match arm for [ReplyOn::Failure]. + /// In case neither [ReplyOn::Failure] nor [ReplyOn::Always] is present, + /// the error is forwarded. + fn emit_failure_match_arm(&self, contract_turbofish: &Type) -> TokenStream { + let sylvia = crate_module(); + + match self + .handlers + .iter() + .find(|(_, reply_on)| reply_on == &ReplyOn::Failure || reply_on == &ReplyOn::Always) + { + Some((method_name, reply_on)) if reply_on == &ReplyOn::Failure => { + let payload_values = self.payload.iter().map(|field| field.name()); + let payload_deserialization = self.payload.emit_payload_deserialization(); - quote! { - #sylvia ::cw_std::SubMsgResult::Err(error) => { - #payload_deserialization + quote! { + #sylvia ::cw_std::SubMsgResult::Err(error) => { + #payload_deserialization - #contract_turbofish ::new(). #function_name ((deps, env, gas_used, vec![], vec![]).into(), error, #payload ) + #contract_turbofish ::new(). #method_name ((deps, env, gas_used, vec![], vec![]).into(), error, #(#payload_values),* ) + } } } - } - Some(handler) if handler.msg_attr().reply_on() == ReplyOn::Always => { - let function_name = handler.function_name(); - let payload = handler.emit_payload_parameters(); - let payload_deserialization = handler.emit_payload_deserialization(); + Some((method_name, reply_on)) if reply_on == &ReplyOn::Always => { + let payload_values = self.payload.iter().map(|field| field.name()); + let payload_deserialization = self.payload.emit_payload_deserialization(); - quote! { - #sylvia ::cw_std::SubMsgResult::Err(_) => { - #payload_deserialization + quote! { + #sylvia ::cw_std::SubMsgResult::Err(_) => { + #payload_deserialization - #contract_turbofish ::new(). #function_name ((deps, env, gas_used, vec![], vec![]).into(), result, #payload ) + #contract_turbofish ::new(). #method_name ((deps, env, gas_used, vec![], vec![]).into(), result, #(#payload_values),* ) + } } } + _ => quote! { + #sylvia ::cw_std::SubMsgResult::Err(error) => { + Err(sylvia::cw_std::StdError::generic_err(error)).map_err(Into::into) + } + }, } - _ => quote! { - #sylvia ::cw_std::SubMsgResult::Err(error) => { - Err(sylvia::cw_std::StdError::generic_err(error)).map_err(Into::into) - } - }, } } trait ReplyVariant<'a> { - fn as_handlers(&'a self) -> Vec<&'a Ident>; - fn as_reply_data(&self) -> Vec<(Ident, &Ident, &MsgVariant)>; - fn emit_payload_parameters(&self) -> TokenStream; - fn emit_payload_deserialization(&self) -> TokenStream; + fn as_variant_handlers_pair(&'a self) -> Vec<(&'a MsgVariant<'a>, &'a Ident)>; } impl<'a> ReplyVariant<'a> for MsgVariant<'a> { - fn as_handlers(&'a self) -> Vec<&'a Ident> { - if self.msg_attr().handlers().is_empty() { - return vec![self.function_name()]; - } - self.msg_attr().handlers().iter().collect() - } - - fn as_reply_data(&self) -> Vec<(Ident, &Ident, &MsgVariant)> { - self.as_handlers() + fn as_variant_handlers_pair(&'a self) -> Vec<(&'a MsgVariant<'a>, &'a Ident)> { + let variant_handler_id_pair: Vec<_> = self + .msg_attr() + .handlers() .iter() - .map(|&handler| (handler.as_reply_id(), handler, self)) - .collect() - } + .map(|handler| (self, handler)) + .collect(); - fn emit_payload_parameters(&self) -> TokenStream { - if self - .fields() - .iter() - .any(|field| field.contains_attribute(SylviaAttribute::Payload)) - { - quote! { payload } - } else { - let deserialized_payload = self.fields().iter().skip(1).map(MsgField::name); - quote! { #(#deserialized_payload),* } + if variant_handler_id_pair.is_empty() { + return vec![(self, self.function_name())]; } + + variant_handler_id_pair } +} + +pub trait PayloadFields { + fn emit_payload_deserialization(&self) -> TokenStream; + fn emit_payload_serialization(&self) -> TokenStream; + fn is_payload_marked(&self) -> bool; +} +impl PayloadFields for Vec<&MsgField<'_>> { fn emit_payload_deserialization(&self) -> TokenStream { let sylvia = crate_module(); + if self.is_payload_marked() { + // Safe to unwrap as we check if the payload exist. + let payload_value = self.first().unwrap().name(); + return quote! { + let #payload_value = payload ; + }; + } - if self - .fields() - .iter() - .any(|field| field.contains_attribute(SylviaAttribute::Payload)) - { - return quote! {}; + let deserialized_payload_names = self.iter().map(|field| field.name()); + quote! { + let ( #(#deserialized_payload_names),* ) = #sylvia ::cw_std::from_json(&payload)?; } + } - let deserialized_names = self.fields().iter().skip(1).map(MsgField::name); + fn emit_payload_serialization(&self) -> TokenStream { + let sylvia = crate_module(); + if self.is_payload_marked() { + // Safe to unwrap as we check if the payload exist. + let payload_value = self.first().unwrap().name(); + return quote! { + let payload = #payload_value ; + }; + } + + let payload_values = self.iter().map(|field| field.name()); quote! { - let ( #(#deserialized_names),* ) = #sylvia ::cw_std::from_json(&payload)?; + let payload = #sylvia ::cw_std::to_json_binary(&( #(#payload_values),* ))?; } } + + fn is_payload_marked(&self) -> bool { + self.iter() + .any(|field| field.contains_attribute(SylviaAttribute::Payload)) + } } /// Maps self to an [Ident] reply id. diff --git a/sylvia-derive/src/types/msg_field.rs b/sylvia-derive/src/types/msg_field.rs index 227732d3..2834ed71 100644 --- a/sylvia-derive/src/types/msg_field.rs +++ b/sylvia-derive/src/types/msg_field.rs @@ -117,6 +117,10 @@ impl<'a> MsgField<'a> { self.name } + pub fn ty(&self) -> &'a Type { + self.ty + } + pub fn contains_attribute(&self, sv_attr: SylviaAttribute) -> bool { self.attrs .iter() diff --git a/sylvia/tests/reply.rs b/sylvia/tests/reply.rs index befae362..4c50daaf 100644 --- a/sylvia/tests/reply.rs +++ b/sylvia/tests/reply.rs @@ -107,8 +107,11 @@ where let sub_msg = InstantiateBuilder::noop_contract(remote_code_id)? .with_label("noop") .build() - .remote_instantiated() - .with_payload(to_json_binary(&payload)?); + .remote_instantiated(to_json_binary(&payload)?)?; + // TODO: Blocked by https://github.com/CosmWasm/cw-multi-test/pull/216. Uncomment when new + // MultiTest version is released. + // Payload is not currently forwarded in the MultiTest. + // .remote_instantiated(payload)?; Ok(Response::new().add_submessage(sub_msg)) } @@ -125,7 +128,7 @@ where .executor() .noop(should_fail)? .build() - .success(); + .success(Binary::default())?; Ok(Response::new().add_submessage(msg)) } @@ -142,7 +145,7 @@ where .executor() .noop(should_fail)? .build() - .failure(); + .failure(Binary::default())?; Ok(Response::new().add_submessage(msg)) } @@ -159,7 +162,7 @@ where .executor() .noop(should_fail)? .build() - .both(); + .both(Binary::default())?; Ok(Response::new().add_submessage(msg)) } @@ -179,8 +182,7 @@ where .executor() .noop(should_fail)? .build() - .always() - .with_payload(payload); + .always(payload)?; Ok(Response::new().add_submessage(msg)) } @@ -214,7 +216,8 @@ where &self, ctx: ReplyCtx, data: Option, - // Blocked by https://github.com/CosmWasm/cw-multi-test/pull/216. + // TODO: Blocked by https://github.com/CosmWasm/cw-multi-test/pull/216. Uncomment when new + // MultiTest version is released. // Payload is not currently forwarded in the MultiTest. // _instantiate_payload: InstantiatePayload, #[sv::payload] _payload: Binary, @@ -275,7 +278,7 @@ where to_address: remote_addr.as_ref().to_string(), amount: vec![], }); - let submsg = cosmos_msg.always(); + let submsg = cosmos_msg.always(Binary::default())?; Ok(Response::new().add_submessage(submsg)) } } diff --git a/sylvia/tests/ui/attributes/msg/overlapping_reply_handlers.stderr b/sylvia/tests/ui/attributes/msg/overlapping_reply_handlers.stderr index 80c211b6..4b592cba 100644 --- a/sylvia/tests/ui/attributes/msg/overlapping_reply_handlers.stderr +++ b/sylvia/tests/ui/attributes/msg/overlapping_reply_handlers.stderr @@ -1,6 +1,6 @@ error: Duplicated reply handler. - = note: Previous definition of handler=HANDLER_1_REPLY_ID for reply_on=always defined on `fn reply_always()` + = note: Previous definition of handler=`HANDLER_1_REPLY_ID` for reply_on=`always` defined on `fn reply_always()` --> tests/ui/attributes/msg/overlapping_reply_handlers.rs:26:32 | @@ -9,7 +9,7 @@ error: Duplicated reply handler. error: Duplicated reply handler. - = note: Previous definition of handler=HANDLER_2_REPLY_ID for reply_on=failure defined on `fn some_reply()` + = note: Previous definition of handler=`HANDLER_2_REPLY_ID` for reply_on=`failure` defined on `fn some_reply()` --> tests/ui/attributes/msg/overlapping_reply_handlers.rs:41:8 | diff --git a/sylvia/tests/ui/method_signature/reply.rs b/sylvia/tests/ui/method_signature/reply.rs new file mode 100644 index 00000000..3ab7925f --- /dev/null +++ b/sylvia/tests/ui/method_signature/reply.rs @@ -0,0 +1,82 @@ +#![allow(unused_imports)] +use sylvia::cw_std::{Binary, Response, StdResult}; +use sylvia::types::{InstantiateCtx, ReplyCtx}; + +pub mod mismatched_params { + use super::*; + + pub struct Contract {} + + #[sylvia::contract] + impl Contract { + pub const fn new() -> Self { + Self {} + } + + #[sv::msg(instantiate)] + fn instantiate(&self, _ctx: InstantiateCtx) -> StdResult { + Ok(Response::new()) + } + + #[sv::msg(reply, handlers=[on_instantiated], reply_on=success)] + fn first_reply( + &self, + _ctx: ReplyCtx, + _data: Option, + param: String, + ) -> StdResult { + Ok(Response::new()) + } + + #[sv::msg(reply, handlers=[on_instantiated], reply_on=failure)] + fn second_reply( + &self, + _ctx: ReplyCtx, + _data: Option, + param: u32, + ) -> StdResult { + Ok(Response::new()) + } + } +} + +pub mod mismatched_param_arity { + use super::*; + + pub struct Contract {} + + #[sylvia::contract] + impl Contract { + pub const fn new() -> Self { + Self {} + } + + #[sv::msg(instantiate)] + fn instantiate(&self, _ctx: InstantiateCtx) -> StdResult { + Ok(Response::new()) + } + + #[sv::msg(reply, handlers=[on_instantiated], reply_on=success)] + fn first_reply( + &self, + _ctx: ReplyCtx, + _data: Option, + param: String, + ) -> StdResult { + Ok(Response::new()) + } + + #[sv::msg(reply, handlers=[on_instantiated], reply_on=failure)] + fn second_reply( + &self, + _ctx: ReplyCtx, + _data: Option, + param: String, + param: u32, + ) -> StdResult { + Ok(Response::new()) + } + } +} + +fn main() {} diff --git a/sylvia/tests/ui/method_signature/reply.stderr b/sylvia/tests/ui/method_signature/reply.stderr new file mode 100644 index 00000000..b908c1f5 --- /dev/null +++ b/sylvia/tests/ui/method_signature/reply.stderr @@ -0,0 +1,19 @@ +error: Mismatched parameter in reply handlers. + + = note: Parameters for the `on_instantiated` handler have to be the same. + = note: Previous parameter defined for the `on_instantiated` handler. + + --> tests/ui/method_signature/reply.rs:26:13 + | +26 | param: String, + | ^^^^^ + +error: Mismatched quantity of method parameters. + + = note: Both `on_instantiated` handlers should have the same number of parameters. + = note: Previous definition of on_instantiated handler. + + --> tests/ui/method_signature/reply.rs:60:12 + | +60 | fn first_reply( + | ^^^^^^^^^^^