From 4296c956dca361b2badcdf50a50728e45858c7f8 Mon Sep 17 00:00:00 2001 From: Tomasz Kulik Date: Wed, 19 Jun 2024 15:25:26 +0200 Subject: [PATCH 1/3] feat: Attributes forwarding parser implementation --- sylvia-derive/src/message.rs | 10 ++- sylvia-derive/src/parser/attributes/attr.rs | 68 +++++++++++++++++ sylvia-derive/src/parser/attributes/mod.rs | 18 +++++ sylvia-derive/src/parser/attributes/msg.rs | 84 ++++++++++++--------- 4 files changed, 140 insertions(+), 40 deletions(-) create mode 100644 sylvia-derive/src/parser/attributes/attr.rs diff --git a/sylvia-derive/src/message.rs b/sylvia-derive/src/message.rs index 365f1538..37187963 100644 --- a/sylvia-derive/src/message.rs +++ b/sylvia-derive/src/message.rs @@ -77,6 +77,7 @@ impl<'a> StructMessage<'a> { fn parse_struct_message(source: &ItemImpl, ty: MsgType) -> Option<(&ImplItemFn, MsgAttr)> { let mut methods = source.items.iter().filter_map(|item| match item { ImplItem::Fn(method) => { + // TODO tkulik: Implement variant attr forwarding here let attr = ParsedSylviaAttributes::new(method.attrs.iter()).msg_attr?; if attr == ty { Some((method, attr)) @@ -111,9 +112,12 @@ impl<'a> StructMessage<'a> { pub fn emit(&self) -> TokenStream { use MsgAttr::*; + let instantiate_msg = Ident::new("InstantiateMsg", self.function_name.span()); + let migrate_msg = Ident::new("MigrateMsg", self.function_name.span()); + match &self.msg_attr { - Instantiate { name } => self.emit_struct(name), - Migrate { name } => self.emit_struct(name), + Instantiate { .. } => self.emit_struct(&instantiate_msg), + Migrate { .. } => self.emit_struct(&migrate_msg), _ => { emit_error!(Span::mixed_site(), "Invalid message type"); quote! {} @@ -427,7 +431,7 @@ impl<'a> MsgVariant<'a> { let fields = process_fields(sig, generics_checker); let msg_type = msg_attr.msg_type(); - let return_type = if let MsgAttr::Query { resp_type } = msg_attr { + let return_type = if let MsgAttr::Query { resp_type, .. } = msg_attr { match resp_type { Some(resp_type) => { let resp_type = parse_quote! { #resp_type }; diff --git a/sylvia-derive/src/parser/attributes/attr.rs b/sylvia-derive/src/parser/attributes/attr.rs new file mode 100644 index 00000000..f9594b94 --- /dev/null +++ b/sylvia-derive/src/parser/attributes/attr.rs @@ -0,0 +1,68 @@ +use proc_macro2::TokenStream; +use proc_macro_error::emit_error; +use syn::parse::{Error, Parse, ParseStream, Parser}; +use syn::spanned::Spanned; +use syn::{Attribute, Ident, Result, Token}; + +use super::MsgType; + +pub struct VariantAttrForwarding { + pub attrs: TokenStream, +} + +impl VariantAttrForwarding { + pub fn new(attr: &Attribute) -> Result { + attr.meta + .require_list() + .and_then(|meta| VariantAttrForwarding::parse.parse2(meta.tokens.clone())) + .map_err(|err| { + emit_error!(attr.span(), err); + err + }) + } +} + +impl Parse for VariantAttrForwarding { + fn parse(input: ParseStream) -> Result { + let attrs = input.parse()?; + Ok(Self { attrs }) + } +} + +pub struct MsgAttrForwarding { + pub msg_type: MsgType, + pub attrs: TokenStream, +} + +impl MsgAttrForwarding { + pub fn new(attr: &Attribute) -> Result { + attr.meta + .require_list() + .and_then(|meta| MsgAttrForwarding::parse.parse2(meta.tokens.clone())) + .map_err(|err| { + emit_error!(attr.span(), err); + err + }) + } +} + +impl Parse for MsgAttrForwarding { + fn parse(input: ParseStream) -> Result { + let msg_type: Ident = input.parse()?; + let _: Token![,] = input.parse()?; + let attrs = input.parse()?; + let msg_type = match msg_type.to_string().as_str() { + "exec" => MsgType::Exec, + "query" => MsgType::Query, + "instantiate" => MsgType::Instantiate, + "migrate" => MsgType::Migrate, + "reply" => MsgType::Reply, + "sudo" => MsgType::Sudo, + _ => return Err(Error::new( + input.span(), + "Invalid message type, expected one of: `exec`, `query`, `instantiate`, `migrate`, `reply` or `sudo`.", + )) + }; + Ok(Self { msg_type, attrs }) + } +} diff --git a/sylvia-derive/src/parser/attributes/mod.rs b/sylvia-derive/src/parser/attributes/mod.rs index e5f7b8c2..1e4f046d 100644 --- a/sylvia-derive/src/parser/attributes/mod.rs +++ b/sylvia-derive/src/parser/attributes/mod.rs @@ -2,12 +2,14 @@ use proc_macro_error::emit_error; use syn::spanned::Spanned; use syn::{Attribute, PathSegment}; +pub mod attr; pub mod custom; pub mod error; pub mod messages; pub mod msg; pub mod override_entry_point; +pub use attr::{MsgAttrForwarding, VariantAttrForwarding}; pub use custom::Custom; pub use error::ContractErrorAttr; pub use messages::{ContractMessageAttr, Customs}; @@ -23,6 +25,8 @@ pub enum SylviaAttribute { Messages, Msg, OverrideEntryPoint, + VariantAttrs, + MsgAttrs, } impl SylviaAttribute { @@ -42,6 +46,8 @@ impl SylviaAttribute { "messages" => Some(Self::Messages), "msg" => Some(Self::Msg), "override_entry_point" => Some(Self::OverrideEntryPoint), + "attr" => Some(Self::VariantAttrs), + "msg_attr" => Some(Self::MsgAttrs), _ => None, } } @@ -57,6 +63,8 @@ pub struct ParsedSylviaAttributes { pub messages_attrs: Vec, pub msg_attr: Option, pub override_entry_point_attrs: Vec, + pub variant_attrs_forward: Vec, + pub msg_attrs_forward: Vec, } impl ParsedSylviaAttributes { @@ -122,6 +130,16 @@ impl ParsedSylviaAttributes { self.override_entry_point_attrs.push(override_entry_point) } } + SylviaAttribute::VariantAttrs => { + if let Ok(variant_attrs) = VariantAttrForwarding::new(attr) { + self.variant_attrs_forward.push(variant_attrs); + } + } + SylviaAttribute::MsgAttrs => { + if let Ok(message_attrs) = MsgAttrForwarding::new(attr) { + self.msg_attrs_forward.push(message_attrs); + } + } } } } diff --git a/sylvia-derive/src/parser/attributes/msg.rs b/sylvia-derive/src/parser/attributes/msg.rs index 1ff0f00a..2217dcd2 100644 --- a/sylvia-derive/src/parser/attributes/msg.rs +++ b/sylvia-derive/src/parser/attributes/msg.rs @@ -1,7 +1,6 @@ -use proc_macro2::Punct; -use syn::parse::{Error, Parse, ParseBuffer, ParseStream, Parser}; +use syn::parse::{Error, Parse, ParseStream, Parser}; use syn::spanned::Spanned; -use syn::{Attribute, Ident, Result}; +use syn::{Attribute, Ident, Result, Token}; use proc_macro_error::emit_error; @@ -16,13 +15,42 @@ pub enum MsgType { Sudo, } +#[derive(Default)] +struct ArgumentParser { + pub resp_type: Option, +} + +impl Parse for ArgumentParser { + fn parse(input: ParseStream) -> Result { + let mut result = Self::default(); + while input.peek2(Ident) { + let _: Token![,] = input.parse()?; + let arg_type: Ident = input.parse()?; + let _: Token![=] = input.parse()?; + match arg_type.to_string().as_str() { + "resp" => { + let resp_type: Ident = input.parse()?; + result.resp_type = Some(resp_type); + } + _ => { + return Err(Error::new( + input.span(), + "Invalid argument type, expected one of: `resp` or `attr`.", + )) + } + } + } + Ok(result) + } +} + /// Parsed representation of `#[sv::msg(...)]` attribute. #[derive(Clone)] pub enum MsgAttr { Exec, Query { resp_type: Option }, - Instantiate { name: Ident }, - Migrate { name: Ident }, + Instantiate, + Migrate, Reply, Sudo, } @@ -46,27 +74,15 @@ impl PartialEq for MsgAttr { } impl MsgAttr { - fn parse_query(content: &ParseBuffer) -> Result { - if content.peek2(Ident) { - let _: Punct = content.parse()?; - let _: Ident = content.parse()?; - let _: Punct = content.parse()?; - let resp_type: Option = Some(content.parse()?); - Ok(Self::Query { resp_type }) - } else { - Ok(Self::Query { resp_type: None }) - } - } - pub fn msg_type(&self) -> MsgType { MsgType::Exec.as_accessor_name(); match self { - Self::Exec => MsgType::Exec, + Self::Exec { .. } => MsgType::Exec, Self::Query { .. } => MsgType::Query, Self::Instantiate { .. } => MsgType::Instantiate, Self::Migrate { .. } => MsgType::Migrate, - Self::Reply => MsgType::Reply, - Self::Sudo => MsgType::Sudo, + Self::Reply { .. } => MsgType::Reply, + Self::Sudo { .. } => MsgType::Sudo, } } } @@ -74,26 +90,20 @@ impl MsgAttr { impl Parse for MsgAttr { fn parse(input: ParseStream) -> Result { let ty: Ident = input.parse()?; + let ArgumentParser { resp_type } = ArgumentParser::parse(input)?; - if ty == "exec" { - Ok(Self::Exec) - } else if ty == "query" { - Self::parse_query(input) - } else if ty == "instantiate" { - let name = Ident::new("InstantiateMsg", input.span()); - Ok(Self::Instantiate { name }) - } else if ty == "migrate" { - let name = Ident::new("MigrateMsg", input.span()); - Ok(Self::Migrate { name }) - } else if ty == "reply" { - Ok(Self::Reply) - } else if ty == "sudo" { - Ok(Self::Sudo) - } else { - Err(Error::new( + let result = match ty.to_string().as_str() { + "exec" => Self::Exec, + "query" => Self::Query { resp_type }, + "instantiate" => Self::Instantiate, + "migrate" => Self::Migrate, + "reply" => Self::Reply, + "sudo" => Self::Sudo, + _ => return Err(Error::new( input.span(), "Invalid message type, expected one of: `exec`, `query`, `instantiate`, `migrate`, `reply` or `sudo`.", )) - } + }; + Ok(result) } } From 7e3e98fbce129584305a8019c90dfbb773a8eb1d Mon Sep 17 00:00:00 2001 From: Tomasz Kulik Date: Fri, 21 Jun 2024 09:56:01 +0200 Subject: [PATCH 2/3] feat: Attributes forwarding implementation --- sylvia-derive/src/message.rs | 42 +++++- sylvia-derive/src/parser/attributes/attr.rs | 28 +++- sylvia-derive/src/parser/attributes/mod.rs | 15 +++ sylvia-derive/src/parser/attributes/msg.rs | 3 +- sylvia-derive/src/variant_descs.rs | 16 ++- sylvia/tests/messages_generation.rs | 136 ++++++++++++++++++++ 6 files changed, 230 insertions(+), 10 deletions(-) diff --git a/sylvia-derive/src/message.rs b/sylvia-derive/src/message.rs index 37187963..cb70e542 100644 --- a/sylvia-derive/src/message.rs +++ b/sylvia-derive/src/message.rs @@ -2,6 +2,7 @@ use crate::associated_types::{AssociatedTypes, ItemType, EXEC_TYPE, QUERY_TYPE}; use crate::check_generics::{CheckGenerics, GetPath}; use crate::crate_module; use crate::interfaces::Interfaces; +use crate::parser::attributes::{MsgAttrForwarding, VariantAttrForwarding}; use crate::parser::{ parse_associated_custom_type, ContractErrorAttr, Custom, EntryPointArgs, FilteredOverrideEntryPoints, MsgAttr, MsgType, OverrideEntryPoint, ParsedSylviaAttributes, @@ -38,6 +39,7 @@ pub struct StructMessage<'a> { result: &'a ReturnType, msg_attr: MsgAttr, custom: &'a Custom, + msg_attrs_to_forward: Vec, } impl<'a> StructMessage<'a> { @@ -60,6 +62,12 @@ impl<'a> StructMessage<'a> { let (used_generics, unused_generics) = generics_checker.used_unused(); let wheres = filter_wheres(&source.generics.where_clause, generics, &used_generics); + let msg_attrs_to_forward = ParsedSylviaAttributes::new(source.attrs.iter()) + .msg_attrs_forward + .into_iter() + .filter(|attr| attr.msg_type == ty) + .collect(); + Some(Self { contract_type, fields, @@ -71,13 +79,13 @@ impl<'a> StructMessage<'a> { result: &method.sig.output, msg_attr, custom, + msg_attrs_to_forward, }) } fn parse_struct_message(source: &ItemImpl, ty: MsgType) -> Option<(&ImplItemFn, MsgAttr)> { let mut methods = source.items.iter().filter_map(|item| match item { ImplItem::Fn(method) => { - // TODO tkulik: Implement variant attr forwarding here let attr = ParsedSylviaAttributes::new(method.attrs.iter()).msg_attr?; if attr == ty { Some((method, attr)) @@ -139,6 +147,7 @@ impl<'a> StructMessage<'a> { result, msg_attr, custom, + msg_attrs_to_forward, } = self; let ctx_type = msg_attr @@ -155,10 +164,12 @@ impl<'a> StructMessage<'a> { let where_clause = as_where_clause(wheres); let generics = emit_bracketed_generics(generics); let unused_generics = emit_bracketed_generics(unused_generics); + let msg_attrs_to_forward = msg_attrs_to_forward.iter().map(|attr| &attr.attrs); quote! { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(#sylvia ::serde::Serialize, #sylvia ::serde::Deserialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema)] + #( #[ #msg_attrs_to_forward ] )* #[serde(rename_all="snake_case")] pub struct #name #generics { #(pub #fields,)* @@ -188,6 +199,7 @@ pub struct EnumMessage<'a> { msg_ty: MsgType, resp_type: Type, query_type: Type, + msg_attrs_to_forward: Vec, } impl<'a> EnumMessage<'a> { @@ -213,6 +225,12 @@ impl<'a> EnumMessage<'a> { .or(associated_query) .unwrap_or_else(Custom::default_type); + let msg_attrs_to_forward = ParsedSylviaAttributes::new(source.attrs.iter()) + .msg_attrs_forward + .into_iter() + .filter(|attr| attr.msg_type == msg_ty) + .collect(); + Self { source, variants, @@ -220,6 +238,7 @@ impl<'a> EnumMessage<'a> { msg_ty, resp_type, query_type, + msg_attrs_to_forward, } } @@ -231,6 +250,7 @@ impl<'a> EnumMessage<'a> { msg_ty, resp_type, query_type, + msg_attrs_to_forward, } = self; let trait_name = &source.ident; @@ -263,10 +283,12 @@ impl<'a> EnumMessage<'a> { let ep_name = msg_ty.emit_ep_name(); let messages_fn_name = Ident::new(&format!("{}_messages", ep_name), enum_name.span()); let derive_call = msg_ty.emit_derive_call(); + let msg_attrs_to_forward = msg_attrs_to_forward.iter().map(|attr| &attr.attrs); quote! { #[allow(clippy::derive_partial_eq_without_eq)] #derive_call + #( #[ #msg_attrs_to_forward ] )* #[serde(rename_all="snake_case")] pub enum #unique_enum_name #bracketed_used_generics { #(#msg_variants,)* @@ -306,6 +328,7 @@ pub struct ContractEnumMessage<'a> { error: &'a ContractErrorAttr, custom: &'a Custom, where_clause: &'a Option, + msg_attrs_to_forward: Vec, } impl<'a> ContractEnumMessage<'a> { @@ -318,6 +341,11 @@ impl<'a> ContractEnumMessage<'a> { ) -> Self { let where_clause = &source.generics.where_clause; let variants = MsgVariants::new(source.as_variants(), msg_ty, generics, where_clause); + let msg_attrs_to_forward = ParsedSylviaAttributes::new(source.attrs.iter()) + .msg_attrs_forward + .into_iter() + .filter(|attr| attr.msg_type == msg_ty) + .collect(); Self { variants, @@ -326,6 +354,7 @@ impl<'a> ContractEnumMessage<'a> { error, custom, where_clause, + msg_attrs_to_forward, } } @@ -339,6 +368,7 @@ impl<'a> ContractEnumMessage<'a> { error, custom, where_clause, + msg_attrs_to_forward, .. } = self; @@ -373,10 +403,12 @@ impl<'a> ContractEnumMessage<'a> { }, false => quote! {}, }; + let msg_attrs_to_forward = msg_attrs_to_forward.iter().map(|attr| &attr.attrs); quote! { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(#sylvia ::serde::Serialize, #sylvia ::serde::Deserialize, Clone, Debug, PartialEq, #sylvia ::schemars::JsonSchema, #derive_query )] + #( #[ #msg_attrs_to_forward ] )* #[serde(rename_all="snake_case")] pub enum #enum_name #bracketed_used_generics { #(#variants,)* @@ -413,6 +445,7 @@ pub struct MsgVariant<'a> { /// `returns` attribute. return_type: Option, msg_type: MsgType, + attrs_to_forward: Vec, } impl<'a> MsgVariant<'a> { @@ -421,6 +454,7 @@ impl<'a> MsgVariant<'a> { sig: &'a Signature, generics_checker: &mut CheckGenerics, msg_attr: MsgAttr, + attrs_to_forward: Vec, ) -> MsgVariant<'a> where Generic: GetPath + PartialEq, @@ -455,6 +489,7 @@ impl<'a> MsgVariant<'a> { fields, return_type, msg_type, + attrs_to_forward, } } @@ -465,13 +500,16 @@ impl<'a> MsgVariant<'a> { fields, msg_type, return_type, + attrs_to_forward, .. } = self; let fields = fields.iter().map(MsgField::emit); let returns_attribute = msg_type.emit_returns_attribute(return_type); + let attrs_to_forward = attrs_to_forward.iter().map(|attr| &attr.attrs); quote! { #returns_attribute + #( #[ #attrs_to_forward ] )* #name { #(#fields,)* } @@ -574,6 +612,7 @@ where let variants: Vec<_> = source .filter_map(|variant_desc| { let msg_attr: MsgAttr = variant_desc.attr_msg()?; + let attrs_to_forward = variant_desc.attrs_to_forward(); if msg_attr.msg_type() != msg_ty { return None; @@ -583,6 +622,7 @@ where variant_desc.into_sig(), &mut generics_checker, msg_attr, + attrs_to_forward, )) }) .collect(); diff --git a/sylvia-derive/src/parser/attributes/attr.rs b/sylvia-derive/src/parser/attributes/attr.rs index f9594b94..243f799c 100644 --- a/sylvia-derive/src/parser/attributes/attr.rs +++ b/sylvia-derive/src/parser/attributes/attr.rs @@ -1,4 +1,4 @@ -use proc_macro2::TokenStream; +use proc_macro2::{Span, TokenStream}; use proc_macro_error::emit_error; use syn::parse::{Error, Parse, ParseStream, Parser}; use syn::spanned::Spanned; @@ -6,8 +6,10 @@ use syn::{Attribute, Ident, Result, Token}; use super::MsgType; +#[derive(Clone, Debug)] pub struct VariantAttrForwarding { pub attrs: TokenStream, + pub span: Span, } impl VariantAttrForwarding { @@ -15,6 +17,10 @@ impl VariantAttrForwarding { attr.meta .require_list() .and_then(|meta| VariantAttrForwarding::parse.parse2(meta.tokens.clone())) + .map(|mut variant| { + variant.span = attr.span(); + variant + }) .map_err(|err| { emit_error!(attr.span(), err); err @@ -25,10 +31,14 @@ impl VariantAttrForwarding { impl Parse for VariantAttrForwarding { fn parse(input: ParseStream) -> Result { let attrs = input.parse()?; - Ok(Self { attrs }) + Ok(Self { + attrs, + span: input.span(), + }) } } +#[derive(Clone, Debug)] pub struct MsgAttrForwarding { pub msg_type: MsgType, pub attrs: TokenStream, @@ -48,9 +58,17 @@ impl MsgAttrForwarding { impl Parse for MsgAttrForwarding { fn parse(input: ParseStream) -> Result { - let msg_type: Ident = input.parse()?; - let _: Token![,] = input.parse()?; - let attrs = input.parse()?; + let error_msg = + "Expected attribute of the form: `#[sv::msg_attr(msg_type, attribute_to_forward)]`"; + let msg_type: Ident = input + .parse() + .map_err(|err| Error::new(err.span(), error_msg))?; + let _: Token![,] = input + .parse() + .map_err(|err| Error::new(err.span(), error_msg))?; + let attrs = input + .parse() + .map_err(|err| Error::new(err.span(), error_msg))?; let msg_type = match msg_type.to_string().as_str() { "exec" => MsgType::Exec, "query" => MsgType::Query, diff --git a/sylvia-derive/src/parser/attributes/mod.rs b/sylvia-derive/src/parser/attributes/mod.rs index 1e4f046d..803676af 100644 --- a/sylvia-derive/src/parser/attributes/mod.rs +++ b/sylvia-derive/src/parser/attributes/mod.rs @@ -76,6 +76,21 @@ impl ParsedSylviaAttributes { result.match_attribute(&sylvia_attr, attr); } } + + if let Some(attr) = result.variant_attrs_forward.first() { + if let Some(MsgAttr::Instantiate) = result.msg_attr { + emit_error!( + attr.span, "The attribute `sv::attr` is not supported for `instantiate`"; + note = "Message `instantiate` is a structure, use `#[sv::msg_attr] instead`"; + ); + } else if let Some(MsgAttr::Migrate) = result.msg_attr { + emit_error!( + attr.span, "The attribute `sv::attr` is not supported for `migate`"; + note = "Message `migrate` is a structure, use `#[sv::msg_attr] instead`"; + ); + } + } + result } diff --git a/sylvia-derive/src/parser/attributes/msg.rs b/sylvia-derive/src/parser/attributes/msg.rs index 2217dcd2..cbc510cb 100644 --- a/sylvia-derive/src/parser/attributes/msg.rs +++ b/sylvia-derive/src/parser/attributes/msg.rs @@ -35,7 +35,7 @@ impl Parse for ArgumentParser { _ => { return Err(Error::new( input.span(), - "Invalid argument type, expected one of: `resp` or `attr`.", + "Invalid argument type, expected `resp` or no argument.", )) } } @@ -75,7 +75,6 @@ impl PartialEq for MsgAttr { impl MsgAttr { pub fn msg_type(&self) -> MsgType { - MsgType::Exec.as_accessor_name(); match self { Self::Exec { .. } => MsgType::Exec, Self::Query { .. } => MsgType::Query, diff --git a/sylvia-derive/src/variant_descs.rs b/sylvia-derive/src/variant_descs.rs index 4f0a9696..de04dccd 100644 --- a/sylvia-derive/src/variant_descs.rs +++ b/sylvia-derive/src/variant_descs.rs @@ -1,15 +1,23 @@ +use crate::parser::attributes::VariantAttrForwarding; use crate::parser::{MsgAttr, ParsedSylviaAttributes}; use syn::{Attribute, ImplItem, ItemImpl, ItemTrait, Signature, TraitItem}; pub struct VariantDesc<'a> { msg_attr: Option, + attrs_to_forward: Vec, sig: &'a Signature, } impl<'a> VariantDesc<'a> { pub fn new(attrs: &'a [Attribute], sig: &'a Signature) -> Self { - let msg_attr = ParsedSylviaAttributes::new(attrs.iter()).msg_attr; - Self { msg_attr, sig } + let sylvia_params = ParsedSylviaAttributes::new(attrs.iter()); + let attrs_to_forward = sylvia_params.variant_attrs_forward; + let msg_attr = sylvia_params.msg_attr; + Self { + msg_attr, + attrs_to_forward, + sig, + } } pub fn into_sig(self) -> &'a Signature { @@ -19,6 +27,10 @@ impl<'a> VariantDesc<'a> { pub fn attr_msg(&self) -> Option { self.msg_attr.clone() } + + pub fn attrs_to_forward(&self) -> Vec { + self.attrs_to_forward.clone() + } } pub type VariantDescs<'a> = Box> + 'a>; diff --git a/sylvia/tests/messages_generation.rs b/sylvia/tests/messages_generation.rs index dec442de..26feabba 100644 --- a/sylvia/tests/messages_generation.rs +++ b/sylvia/tests/messages_generation.rs @@ -1,3 +1,6 @@ +use std::fmt::Debug; +use std::str::FromStr; + use cosmwasm_std::{Addr, CustomQuery, Decimal}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -16,18 +19,24 @@ pub mod interface { use cosmwasm_std::{Addr, Decimal, Response, StdError}; use sylvia::interface; use sylvia::types::{ExecCtx, QueryCtx, SudoCtx}; + use thiserror::Error; use crate::QueryResult; #[interface] + #[sv::msg_attr(exec, derive(PartialOrd, Error))] + #[sv::msg_attr(query, derive(PartialOrd, Error))] + #[sv::msg_attr(sudo, derive(PartialOrd, Error))] #[sv::custom(msg=cosmwasm_std::Empty, query=cosmwasm_std::Empty)] pub trait Interface { type Error: From; #[sv::msg(exec)] + #[sv::attr(error("NoArgsExecution"))] fn no_args_execution(&self, ctx: ExecCtx) -> Result; #[sv::msg(exec)] + #[sv::attr(error("ArgumentedExecution"))] fn argumented_execution( &self, ctx: ExecCtx, @@ -37,15 +46,19 @@ pub mod interface { ) -> Result; #[sv::msg(query)] + #[sv::attr(error("NoArgsQuery"))] fn no_args_query(&self, ctx: QueryCtx) -> Result; #[sv::msg(query)] + #[sv::attr(error("ArgumentedQuery"))] fn argumented_query(&self, ctx: QueryCtx, user: Addr) -> Result; #[sv::msg(sudo)] + #[sv::attr(error("NoArgsSudo"))] fn no_args_sudo(&self, ctx: SudoCtx) -> Result; #[sv::msg(sudo)] + #[sv::attr(error("ArgumentedSudo"))] fn argumented_sudo(&self, ctx: SudoCtx, user: Addr) -> Result; } } @@ -55,6 +68,7 @@ mod contract { use sylvia::contract; use sylvia::types::{ExecCtx, InstantiateCtx, MigrateCtx, QueryCtx, ReplyCtx, SudoCtx}; use sylvia_derive::entry_points; + use thiserror::Error; use crate::{MyQuery, QueryResult}; @@ -64,6 +78,13 @@ mod contract { #[contract] #[allow(dead_code)] #[sv::custom(query=MyQuery)] + #[sv::msg_attr(exec, derive(PartialOrd, Error))] + #[sv::msg_attr(query, derive(PartialOrd, Error))] + #[sv::msg_attr(sudo, derive(PartialOrd, Error))] + #[sv::msg_attr(instantiate, derive(PartialOrd, Error))] + #[sv::msg_attr(instantiate, error("Instantiate"))] + #[sv::msg_attr(migrate, derive(PartialOrd, Error))] + #[sv::msg_attr(migrate, error("Migrate"))] impl Contract { #[allow(clippy::new_without_default)] #[allow(dead_code)] @@ -82,11 +103,13 @@ mod contract { } #[sv::msg(exec)] + #[sv::attr(error("NoArgsExecution"))] fn no_args_execution(&self, _ctx: ExecCtx) -> StdResult { Ok(Response::new()) } #[sv::msg(exec)] + #[sv::attr(error("ArgumentedExecution"))] fn argumented_execution( &self, _ctx: ExecCtx, @@ -98,11 +121,13 @@ mod contract { } #[sv::msg(query)] + #[sv::attr(error("NoArgsQuery"))] fn no_args_query(&self, _ctx: QueryCtx) -> StdResult { Ok(QueryResult {}) } #[sv::msg(query)] + #[sv::attr(error("ArgumentedQuery"))] fn argumented_query(&self, _ctx: QueryCtx, _user: Addr) -> StdResult { Ok(QueryResult {}) } @@ -113,11 +138,13 @@ mod contract { } #[sv::msg(sudo)] + #[sv::attr(error("NoArgsSudo"))] fn no_args_sudo(&self, _ctx: SudoCtx) -> StdResult { Ok(Response::new()) } #[sv::msg(sudo)] + #[sv::attr(error("ArgumentedSudo"))] fn argumented_sudo(&self, _ctx: SudoCtx, _user: Addr) -> StdResult { Ok(Response::new()) } @@ -207,3 +234,112 @@ fn entry_points_generation() { .with_reply(entry_points::reply) .with_sudo(entry_points::sudo); } + +#[test] +fn attributes_forwarding_for_message_structs_and_enums() { + let exec_msg_contract = contract::sv::ExecMsg::NoArgsExecution {}; + let _partial_ord_implemented = exec_msg_contract < exec_msg_contract; + + let query_msg_contract = contract::sv::QueryMsg::NoArgsQuery {}; + let _partial_ord_implemented = query_msg_contract < query_msg_contract; + + let sudo_msg_contract = contract::sv::SudoMsg::NoArgsSudo {}; + let _partial_ord_implemented = sudo_msg_contract < sudo_msg_contract; + + let instantiate_msg_contract = contract::sv::InstantiateMsg {}; + let _partial_ord_implemented = instantiate_msg_contract < instantiate_msg_contract; + + let migrate_msg_contract = contract::sv::MigrateMsg {}; + let _partial_ord_implemented = migrate_msg_contract < migrate_msg_contract; + + let exec_msg_interface = interface::sv::ExecMsg::NoArgsExecution {}; + let _partial_ord_implemented = exec_msg_interface < exec_msg_interface; + + let query_msg_interface = interface::sv::QueryMsg::NoArgsQuery {}; + let _partial_ord_implemented = query_msg_interface < query_msg_interface; + + let sudo_msg_interface = interface::sv::SudoMsg::NoArgsSudo {}; + let _partial_ord_implemented = sudo_msg_interface < sudo_msg_interface; +} + +#[test] +fn attributes_forwarding_for_message_variants() { + // By adding #[sv::msg_attr(exec, derive(Error))] and #[sv::attr(error("NoArgsExecution"))] + // for execution message and its variants, it should be possible to call `to_string` method + // on each exec variant. Without this derive, the method is not implemented. + // Analogous for the other message enums and structs. + + assert_eq!( + contract::sv::ExecMsg::NoArgsExecution {}.to_string(), + "NoArgsExecution" + ); + assert_eq!( + contract::sv::ExecMsg::ArgumentedExecution { + _addr: Addr::unchecked("input"), + _coef: Decimal::from_str("0").unwrap(), + _desc: "".to_owned(), + } + .to_string(), + "ArgumentedExecution" + ); + assert_eq!( + contract::sv::QueryMsg::NoArgsQuery {}.to_string(), + "NoArgsQuery" + ); + assert_eq!( + contract::sv::QueryMsg::ArgumentedQuery { + _user: Addr::unchecked("input"), + } + .to_string(), + "ArgumentedQuery" + ); + assert_eq!( + contract::sv::SudoMsg::NoArgsSudo {}.to_string(), + "NoArgsSudo" + ); + assert_eq!( + contract::sv::SudoMsg::ArgumentedSudo { + _user: Addr::unchecked("input"), + } + .to_string(), + "ArgumentedSudo" + ); + assert_eq!(contract::sv::InstantiateMsg {}.to_string(), "Instantiate"); + assert_eq!(contract::sv::MigrateMsg {}.to_string(), "Migrate"); + + assert_eq!( + interface::sv::ExecMsg::NoArgsExecution {}.to_string(), + "NoArgsExecution" + ); + assert_eq!( + interface::sv::ExecMsg::ArgumentedExecution { + addr: Addr::unchecked("input"), + coef: Decimal::from_str("0").unwrap(), + desc: "".to_owned(), + } + .to_string(), + "ArgumentedExecution" + ); + assert_eq!( + interface::sv::QueryMsg::NoArgsQuery {}.to_string(), + "NoArgsQuery" + ); + assert_eq!( + interface::sv::QueryMsg::ArgumentedQuery { + user: Addr::unchecked("input"), + } + .to_string(), + "ArgumentedQuery" + ); + assert_eq!( + interface::sv::SudoMsg::NoArgsSudo {}.to_string(), + "NoArgsSudo" + ); + assert_eq!( + interface::sv::SudoMsg::ArgumentedSudo { + user: Addr::unchecked("input"), + } + .to_string(), + "ArgumentedSudo" + ); +} From d1c24eaf4989997951ff19bc5c221e7b934a4cbb Mon Sep 17 00:00:00 2001 From: Tomasz Kulik Date: Wed, 26 Jun 2024 15:21:15 +0200 Subject: [PATCH 3/3] chore: Update after review --- sylvia-derive/src/parser/attributes/attr.rs | 45 +++++++------------ sylvia-derive/src/parser/attributes/custom.rs | 36 +++++++-------- sylvia-derive/src/parser/attributes/error.rs | 12 +++-- .../src/parser/attributes/messages.rs | 30 +++++++------ sylvia-derive/src/parser/attributes/mod.rs | 15 ++++--- sylvia-derive/src/parser/attributes/msg.rs | 19 +++----- .../parser/attributes/override_entry_point.rs | 12 +++-- .../attr_forwarding/invalid_sv_attr.rs | 28 ++++++++++++ .../attr_forwarding/invalid_sv_attr.stderr | 23 ++++++++++ .../attributes/custom/invalid_custom.stderr | 9 +--- .../attributes/messages/invalid_custom.stderr | 1 - .../messages/unexpected_token.stderr | 7 --- 12 files changed, 125 insertions(+), 112 deletions(-) create mode 100644 sylvia/tests/ui/attributes/attr_forwarding/invalid_sv_attr.rs create mode 100644 sylvia/tests/ui/attributes/attr_forwarding/invalid_sv_attr.stderr diff --git a/sylvia-derive/src/parser/attributes/attr.rs b/sylvia-derive/src/parser/attributes/attr.rs index 243f799c..e694119e 100644 --- a/sylvia-derive/src/parser/attributes/attr.rs +++ b/sylvia-derive/src/parser/attributes/attr.rs @@ -2,7 +2,7 @@ use proc_macro2::{Span, TokenStream}; use proc_macro_error::emit_error; use syn::parse::{Error, Parse, ParseStream, Parser}; use syn::spanned::Spanned; -use syn::{Attribute, Ident, Result, Token}; +use syn::{Ident, MetaList, Result, Token}; use super::MsgType; @@ -13,28 +13,11 @@ pub struct VariantAttrForwarding { } impl VariantAttrForwarding { - pub fn new(attr: &Attribute) -> Result { - attr.meta - .require_list() - .and_then(|meta| VariantAttrForwarding::parse.parse2(meta.tokens.clone())) - .map(|mut variant| { - variant.span = attr.span(); - variant - }) - .map_err(|err| { - emit_error!(attr.span(), err); - err - }) - } -} - -impl Parse for VariantAttrForwarding { - fn parse(input: ParseStream) -> Result { - let attrs = input.parse()?; - Ok(Self { - attrs, - span: input.span(), - }) + pub fn new(attr: &MetaList) -> Self { + VariantAttrForwarding { + attrs: attr.tokens.clone(), + span: attr.span(), + } } } @@ -45,12 +28,11 @@ pub struct MsgAttrForwarding { } impl MsgAttrForwarding { - pub fn new(attr: &Attribute) -> Result { - attr.meta - .require_list() - .and_then(|meta| MsgAttrForwarding::parse.parse2(meta.tokens.clone())) + pub fn new(attr: &MetaList) -> Result { + MsgAttrForwarding::parse + .parse2(attr.tokens.clone()) .map_err(|err| { - emit_error!(attr.span(), err); + emit_error!(attr.tokens.span(), err); err }) } @@ -66,9 +48,12 @@ impl Parse for MsgAttrForwarding { let _: Token![,] = input .parse() .map_err(|err| Error::new(err.span(), error_msg))?; - let attrs = input + let attrs: TokenStream = input .parse() .map_err(|err| Error::new(err.span(), error_msg))?; + if attrs.is_empty() { + return Err(Error::new(attrs.span(), error_msg)); + } let msg_type = match msg_type.to_string().as_str() { "exec" => MsgType::Exec, "query" => MsgType::Query, @@ -77,7 +62,7 @@ impl Parse for MsgAttrForwarding { "reply" => MsgType::Reply, "sudo" => MsgType::Sudo, _ => return Err(Error::new( - input.span(), + msg_type.span(), "Invalid message type, expected one of: `exec`, `query`, `instantiate`, `migrate`, `reply` or `sudo`.", )) }; diff --git a/sylvia-derive/src/parser/attributes/custom.rs b/sylvia-derive/src/parser/attributes/custom.rs index 3d48a924..bccd015e 100644 --- a/sylvia-derive/src/parser/attributes/custom.rs +++ b/sylvia-derive/src/parser/attributes/custom.rs @@ -1,7 +1,6 @@ use proc_macro_error::emit_error; use syn::parse::{Parse, ParseStream, Parser}; -use syn::spanned::Spanned; -use syn::{parse_quote, Attribute, Ident, Result, Token, Type}; +use syn::{parse_quote, Error, Ident, MetaList, Result, Token, Type}; use crate::crate_module; @@ -12,14 +11,11 @@ pub struct Custom { } impl Custom { - pub fn new(attr: &Attribute) -> Result { - attr.meta - .require_list() - .and_then(|meta| Custom::parse.parse2(meta.tokens.clone())) - .map_err(|err| { - emit_error!(attr.span(), err); - err - }) + pub fn new(attr: &MetaList) -> Result { + Custom::parse.parse2(attr.tokens.clone()).map_err(|err| { + emit_error!(err.span(), err); + err + }) } pub fn msg_or_default(&self) -> Type { @@ -43,15 +39,17 @@ impl Parse for Custom { while !input.is_empty() { let ty: Ident = input.parse()?; let _: Token![=] = input.parse()?; - if ty == "msg" { - custom.msg = Some(input.parse()?) - } else if ty == "query" { - custom.query = Some(input.parse()?) - } else { - emit_error!(ty.span(), "Invalid custom type."; - note = ty.span() => "Expected `#[sv::custom(msg=SomeMsg, query=SomeQuery)]`" - ); - }; + match ty.to_string().as_str() { + "msg" => custom.msg = Some(input.parse()?), + "query" => custom.query = Some(input.parse()?), + _ => { + return Err(Error::new( + ty.span(), + "Invalid custom type.\n + = note: Expected `#[sv::custom(msg=SomeMsg, query=SomeQuery)]`.\n", + )) + } + } if !input.peek(Token![,]) { break; } diff --git a/sylvia-derive/src/parser/attributes/error.rs b/sylvia-derive/src/parser/attributes/error.rs index 960fe36b..1c3d7f16 100644 --- a/sylvia-derive/src/parser/attributes/error.rs +++ b/sylvia-derive/src/parser/attributes/error.rs @@ -1,7 +1,6 @@ use proc_macro_error::emit_error; use syn::parse::{Parse, ParseStream, Parser}; -use syn::spanned::Spanned; -use syn::{parse_quote, Attribute, Result, Type}; +use syn::{parse_quote, MetaList, Result, Type}; use crate::crate_module; @@ -20,12 +19,11 @@ impl Default for ContractErrorAttr { } impl ContractErrorAttr { - pub fn new(attr: &Attribute) -> Result { - attr.meta - .require_list() - .and_then(|meta| ContractErrorAttr::parse.parse2(meta.tokens.clone())) + pub fn new(attr: &MetaList) -> Result { + ContractErrorAttr::parse + .parse2(attr.tokens.clone()) .map_err(|err| { - emit_error!(attr.span(), err); + emit_error!(err.span(), err); err }) } diff --git a/sylvia-derive/src/parser/attributes/messages.rs b/sylvia-derive/src/parser/attributes/messages.rs index 0b9e8dfa..c88831b0 100644 --- a/sylvia-derive/src/parser/attributes/messages.rs +++ b/sylvia-derive/src/parser/attributes/messages.rs @@ -4,7 +4,7 @@ use syn::fold::Fold; use syn::parse::{Parse, ParseStream, Parser}; use syn::punctuated::Punctuated; use syn::spanned::Spanned; -use syn::{parenthesized, Attribute, GenericArgument, Ident, Path, Result, Token}; +use syn::{parenthesized, Error, GenericArgument, Ident, MetaList, Path, Result, Token}; use proc_macro_error::emit_error; @@ -20,12 +20,11 @@ pub struct ContractMessageAttr { } impl ContractMessageAttr { - pub fn new(attr: &Attribute) -> Result { - attr.meta - .require_list() - .and_then(|meta| ContractMessageAttr::parse.parse2(meta.tokens.clone())) + pub fn new(attr: &MetaList) -> Result { + ContractMessageAttr::parse + .parse2(attr.tokens.clone()) .map_err(|err| { - emit_error!(attr.span(), err); + emit_error!(err.span(), err); err }) } @@ -61,10 +60,13 @@ fn interface_has_custom(content: ParseStream) -> Result { match custom.get_ident() { Some(ident) if ident == "msg" => customs.has_msg = true, Some(ident) if ident == "query" => customs.has_query = true, - _ => emit_error!(custom.span(), - "Invalid custom attribute, expected one or both of: [`msg`, `query`]"; - note = "Expected attribute to be in form `#[sv::messages(interface: custom(msg, query))]`." - ), + _ => { + return Err(Error::new( + custom.span(), + "Invalid custom attribute, expected one or both of: [`msg`, `query`]\n + = note: Expected attribute to be in form `#[sv::messages(interface: custom(msg, query))]`.\n", + )) + } } if !custom_content.peek(Token![,]) { break; @@ -103,10 +105,10 @@ impl Parse for ContractMessageAttr { }; let customs = interface_has_custom(input)?; if !input.is_empty() { - emit_error!(input.span(), - "Unexpected tokens inside `sv::messages` attribtue."; - note = "Maximal supported form of attribute: `#[sv::messages(interface::path as InterfaceName: custom(msg, query))]`." - ) + return Err(Error::new(input.span(), + "Unexpected tokens inside `sv::messages` attribtue.\n + = note: Maximal supported form of attribute: `#[sv::messages(interface::path as InterfaceName: custom(msg, query))]`.\n" + )); } Ok(Self { module, diff --git a/sylvia-derive/src/parser/attributes/mod.rs b/sylvia-derive/src/parser/attributes/mod.rs index 803676af..b8546239 100644 --- a/sylvia-derive/src/parser/attributes/mod.rs +++ b/sylvia-derive/src/parser/attributes/mod.rs @@ -1,6 +1,6 @@ use proc_macro_error::emit_error; use syn::spanned::Spanned; -use syn::{Attribute, PathSegment}; +use syn::{Attribute, MetaList, PathSegment}; pub mod attr; pub mod custom; @@ -72,7 +72,9 @@ impl ParsedSylviaAttributes { let mut result = Self::default(); for attr in attrs { let sylvia_attr = SylviaAttribute::new(attr); - if let Some(sylvia_attr) = sylvia_attr { + let attr_content = attr.meta.require_list(); + + if let (Some(sylvia_attr), Ok(attr)) = (sylvia_attr, &attr_content) { result.match_attribute(&sylvia_attr, attr); } } @@ -85,7 +87,7 @@ impl ParsedSylviaAttributes { ); } else if let Some(MsgAttr::Migrate) = result.msg_attr { emit_error!( - attr.span, "The attribute `sv::attr` is not supported for `migate`"; + attr.span, "The attribute `sv::attr` is not supported for `migrate`"; note = "Message `migrate` is a structure, use `#[sv::msg_attr] instead`"; ); } @@ -94,7 +96,7 @@ impl ParsedSylviaAttributes { result } - fn match_attribute(&mut self, attribute_type: &SylviaAttribute, attr: &Attribute) { + fn match_attribute(&mut self, attribute_type: &SylviaAttribute, attr: &MetaList) { match attribute_type { SylviaAttribute::Custom => { if self.custom_attr.is_none() { @@ -146,9 +148,8 @@ impl ParsedSylviaAttributes { } } SylviaAttribute::VariantAttrs => { - if let Ok(variant_attrs) = VariantAttrForwarding::new(attr) { - self.variant_attrs_forward.push(variant_attrs); - } + self.variant_attrs_forward + .push(VariantAttrForwarding::new(attr)); } SylviaAttribute::MsgAttrs => { if let Ok(message_attrs) = MsgAttrForwarding::new(attr) { diff --git a/sylvia-derive/src/parser/attributes/msg.rs b/sylvia-derive/src/parser/attributes/msg.rs index cbc510cb..857081ed 100644 --- a/sylvia-derive/src/parser/attributes/msg.rs +++ b/sylvia-derive/src/parser/attributes/msg.rs @@ -1,8 +1,6 @@ -use syn::parse::{Error, Parse, ParseStream, Parser}; -use syn::spanned::Spanned; -use syn::{Attribute, Ident, Result, Token}; - use proc_macro_error::emit_error; +use syn::parse::{Error, Parse, ParseStream, Parser}; +use syn::{Ident, MetaList, Result, Token}; /// Type of message to be generated #[derive(PartialEq, Eq, Debug, Clone, Copy)] @@ -56,14 +54,11 @@ pub enum MsgAttr { } impl MsgAttr { - pub fn new(attr: &Attribute) -> Result { - attr.meta - .require_list() - .and_then(|meta| MsgAttr::parse.parse2(meta.tokens.clone())) - .map_err(|err| { - emit_error!(attr.span(), err); - err - }) + pub fn new(attr: &MetaList) -> Result { + MsgAttr::parse.parse2(attr.tokens.clone()).map_err(|err| { + emit_error!(err.span(), err); + err + }) } } diff --git a/sylvia-derive/src/parser/attributes/override_entry_point.rs b/sylvia-derive/src/parser/attributes/override_entry_point.rs index ce40c92e..a085e179 100644 --- a/sylvia-derive/src/parser/attributes/override_entry_point.rs +++ b/sylvia-derive/src/parser/attributes/override_entry_point.rs @@ -2,8 +2,7 @@ use proc_macro2::TokenStream; use proc_macro_error::emit_error; use quote::quote; use syn::parse::{Error, Parse, ParseStream, Parser}; -use syn::spanned::Spanned; -use syn::{parenthesized, Attribute, Ident, Path, Result, Token, Type}; +use syn::{parenthesized, Ident, MetaList, Path, Result, Token, Type}; use crate::crate_module; use crate::parser::MsgType; @@ -16,12 +15,11 @@ pub struct OverrideEntryPoint { } impl OverrideEntryPoint { - pub fn new(attr: &Attribute) -> Result { - attr.meta - .require_list() - .and_then(|meta| OverrideEntryPoint::parse.parse2(meta.tokens.clone())) + pub fn new(attr: &MetaList) -> Result { + OverrideEntryPoint::parse + .parse2(attr.tokens.clone()) .map_err(|err| { - emit_error!(attr.span(), err); + emit_error!(err.span(), err); err }) } diff --git a/sylvia/tests/ui/attributes/attr_forwarding/invalid_sv_attr.rs b/sylvia/tests/ui/attributes/attr_forwarding/invalid_sv_attr.rs new file mode 100644 index 00000000..34db2616 --- /dev/null +++ b/sylvia/tests/ui/attributes/attr_forwarding/invalid_sv_attr.rs @@ -0,0 +1,28 @@ +#![allow(unused_imports)] +use sylvia::cw_std::{Response, StdResult}; +use sylvia::types::{ExecCtx, InstantiateCtx}; + +pub struct Contract; + +#[sylvia::contract] +#[sv::msg_attr(exec,)] +#[sv::msg_attr(random_msg, PartialOrd)] +#[sv::msg_attr(exec PartialOrd)] +#[sv::msg_attr(PartialOrd)] +impl Contract { + pub const fn new() -> Self { + Self + } + + #[sv::msg(instantiate)] + fn instantiate(&self, _ctx: InstantiateCtx) -> StdResult { + Ok(Response::new()) + } + + #[sv::msg(exec)] + fn exec(&self, _ctx: ExecCtx) -> StdResult { + Ok(Response::new()) + } +} + +fn main() {} diff --git a/sylvia/tests/ui/attributes/attr_forwarding/invalid_sv_attr.stderr b/sylvia/tests/ui/attributes/attr_forwarding/invalid_sv_attr.stderr new file mode 100644 index 00000000..6dda7303 --- /dev/null +++ b/sylvia/tests/ui/attributes/attr_forwarding/invalid_sv_attr.stderr @@ -0,0 +1,23 @@ +error: Expected attribute of the form: `#[sv::msg_attr(msg_type, attribute_to_forward)]` + --> tests/ui/attributes/attr_forwarding/invalid_sv_attr.rs:8:16 + | +8 | #[sv::msg_attr(exec,)] + | ^^^^ + +error: Invalid message type, expected one of: `exec`, `query`, `instantiate`, `migrate`, `reply` or `sudo`. + --> tests/ui/attributes/attr_forwarding/invalid_sv_attr.rs:9:16 + | +9 | #[sv::msg_attr(random_msg, PartialOrd)] + | ^^^^^^^^^^ + +error: Expected attribute of the form: `#[sv::msg_attr(msg_type, attribute_to_forward)]` + --> tests/ui/attributes/attr_forwarding/invalid_sv_attr.rs:10:16 + | +10 | #[sv::msg_attr(exec PartialOrd)] + | ^^^^ + +error: Expected attribute of the form: `#[sv::msg_attr(msg_type, attribute_to_forward)]` + --> tests/ui/attributes/attr_forwarding/invalid_sv_attr.rs:11:16 + | +11 | #[sv::msg_attr(PartialOrd)] + | ^^^^^^^^^^ diff --git a/sylvia/tests/ui/attributes/custom/invalid_custom.stderr b/sylvia/tests/ui/attributes/custom/invalid_custom.stderr index 2f7eafc6..83edaa7a 100644 --- a/sylvia/tests/ui/attributes/custom/invalid_custom.stderr +++ b/sylvia/tests/ui/attributes/custom/invalid_custom.stderr @@ -1,14 +1,7 @@ error: Invalid custom type. - = note: Expected `#[sv::custom(msg=SomeMsg, query=SomeQuery)]` - + = note: Expected `#[sv::custom(msg=SomeMsg, query=SomeQuery)]`. --> tests/ui/attributes/custom/invalid_custom.rs:12:14 | 12 | #[sv::custom(mgs=MyMsg)] | ^^^ - -error: unexpected token - --> tests/ui/attributes/custom/invalid_custom.rs:12:1 - | -12 | #[sv::custom(mgs=MyMsg)] - | ^ diff --git a/sylvia/tests/ui/attributes/messages/invalid_custom.stderr b/sylvia/tests/ui/attributes/messages/invalid_custom.stderr index 8fa4f578..3c004261 100644 --- a/sylvia/tests/ui/attributes/messages/invalid_custom.stderr +++ b/sylvia/tests/ui/attributes/messages/invalid_custom.stderr @@ -1,7 +1,6 @@ error: Invalid custom attribute, expected one or both of: [`msg`, `query`] = note: Expected attribute to be in form `#[sv::messages(interface: custom(msg, query))]`. - --> tests/ui/attributes/messages/invalid_custom.rs:21:34 | 21 | #[sv::messages(interface: custom(wrong))] diff --git a/sylvia/tests/ui/attributes/messages/unexpected_token.stderr b/sylvia/tests/ui/attributes/messages/unexpected_token.stderr index 6e1bcc93..e089e0bc 100644 --- a/sylvia/tests/ui/attributes/messages/unexpected_token.stderr +++ b/sylvia/tests/ui/attributes/messages/unexpected_token.stderr @@ -1,14 +1,7 @@ error: Unexpected tokens inside `sv::messages` attribtue. = note: Maximal supported form of attribute: `#[sv::messages(interface::path as InterfaceName: custom(msg, query))]`. - --> tests/ui/attributes/messages/unexpected_token.rs:24:25 | 24 | #[sv::messages(interface(Empty))] | ^ - -error: unexpected token - --> tests/ui/attributes/messages/unexpected_token.rs:24:1 - | -24 | #[sv::messages(interface(Empty))] - | ^