From ad28ca96de50f3cb62b72d2bbff5f014b91fa7e6 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Tue, 31 Oct 2023 18:37:26 +0100 Subject: [PATCH 01/12] XCM builder pattern --- .../xcm/procedural/src/builder_pattern.rs | 93 +++++++++++++++++++ polkadot/xcm/procedural/src/lib.rs | 13 +++ polkadot/xcm/src/v3/mod.rs | 2 +- polkadot/xcm/xcm-simulator/example/src/lib.rs | 16 ++++ 4 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 polkadot/xcm/procedural/src/builder_pattern.rs diff --git a/polkadot/xcm/procedural/src/builder_pattern.rs b/polkadot/xcm/procedural/src/builder_pattern.rs new file mode 100644 index 000000000000..25a305292d30 --- /dev/null +++ b/polkadot/xcm/procedural/src/builder_pattern.rs @@ -0,0 +1,93 @@ +use proc_macro::TokenStream; +use proc_macro2::TokenStream as TokenStream2; +use syn::{parse_macro_input, DeriveInput, Data, Error, Fields}; +use quote::{quote, format_ident}; + +pub fn derive(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let builder_impl = match &input.data { + Data::Enum(data_enum) => generate_methods_for_enum(input.ident, data_enum), + _ => return Error::new_spanned(&input, "Expected the `Instruction` enum") + .to_compile_error().into(), + }; + let output = quote! { + pub struct XcmBuilder(Vec>); + impl Xcm { + pub fn builder() -> XcmBuilder { + XcmBuilder::(Vec::new()) + } + } + #builder_impl + }; + output.into() +} + +fn generate_methods_for_enum(name: syn::Ident, data_enum: &syn::DataEnum) -> TokenStream2 { + let methods = data_enum.variants.iter().map(|variant| { + let variant_name = &variant.ident; + let method_name_string = to_snake_case(&variant_name.to_string()); + let method_name = syn::Ident::new(&method_name_string, variant_name.span()); + match &variant.fields { + Fields::Unit => { + quote! { + pub fn #method_name(mut self) -> Self { + self.0.push(#name::::#variant_name); + self + } + } + }, + Fields::Unnamed(fields) => { + let arg_names: Vec<_> = fields.unnamed.iter().enumerate() + .map(|(index, _)| format_ident!("arg{}", index)) + .collect(); + let arg_types: Vec<_> = fields.unnamed.iter().map(|field| &field.ty) + .collect(); + quote! { + pub fn #method_name(mut self, #(#arg_names: #arg_types),*) -> Self { + self.0.push(#name::::#variant_name(#(#arg_names),*)); + self + } + } + }, + Fields::Named(fields) => { + let arg_names: Vec<_> = fields.named.iter().map(|field| &field.ident).collect(); + let arg_types: Vec<_> = fields.named.iter().map(|field| &field.ty).collect(); + quote! { + pub fn #method_name(mut self, #(#arg_names: #arg_types),*) -> Self { + self.0.push(#name::::#variant_name { #(#arg_names),* }); + self + } + } + }, + } + }); + let output = quote! { + impl XcmBuilder { + #(#methods)* + + pub fn build(self) -> Xcm { + Xcm(self.0) + } + } + }; + output +} + +fn to_snake_case(s: &str) -> String { + let mut snake = String::with_capacity(s.len()); + let mut chars = s.chars().peekable(); + + while let Some(ch) = chars.next() { + // If it's an uppercase character and not the start + if ch.is_uppercase() && !snake.is_empty() { + // If the next character is lowercase, prepend an underscore + if chars.peek().map_or(false, |next| next.is_lowercase()) { + snake.push('_'); + } + } + + snake.extend(ch.to_lowercase()); + } + + snake +} diff --git a/polkadot/xcm/procedural/src/lib.rs b/polkadot/xcm/procedural/src/lib.rs index 2ebccadf50be..addebd6638bd 100644 --- a/polkadot/xcm/procedural/src/lib.rs +++ b/polkadot/xcm/procedural/src/lib.rs @@ -21,6 +21,7 @@ use proc_macro::TokenStream; mod v2; mod v3; mod weight_info; +mod builder_pattern; #[proc_macro] pub fn impl_conversion_functions_for_multilocation_v2(input: TokenStream) -> TokenStream { @@ -47,3 +48,15 @@ pub fn impl_conversion_functions_for_junctions_v3(input: TokenStream) -> TokenSt .unwrap_or_else(syn::Error::into_compile_error) .into() } + +/// This is called on the `Instruction` enum, not on the `Xcm` struct, +/// and allows for the following syntax for building XCMs: +/// let message = Xcm::builder() +/// .withdraw_asset(assets) +/// .buy_execution(fees, weight_limit) +/// .deposit_asset(assets, beneficiary) +/// .build(); +#[proc_macro_derive(Builder)] +pub fn derive_builder(input: TokenStream) -> TokenStream { + builder_pattern::derive(input) +} diff --git a/polkadot/xcm/src/v3/mod.rs b/polkadot/xcm/src/v3/mod.rs index f9f31b752a96..4279bef80c2d 100644 --- a/polkadot/xcm/src/v3/mod.rs +++ b/polkadot/xcm/src/v3/mod.rs @@ -401,7 +401,7 @@ impl XcmContext { /// /// This is the inner XCM format and is version-sensitive. Messages are typically passed using the /// outer XCM format, known as `VersionedXcm`. -#[derive(Derivative, Encode, Decode, TypeInfo, xcm_procedural::XcmWeightInfoTrait)] +#[derive(Derivative, Encode, Decode, TypeInfo, xcm_procedural::XcmWeightInfoTrait, xcm_procedural::Builder)] #[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))] #[codec(encode_bound())] #[codec(decode_bound())] diff --git a/polkadot/xcm/xcm-simulator/example/src/lib.rs b/polkadot/xcm/xcm-simulator/example/src/lib.rs index 85b8ad1c5cb7..d0ea47ac6910 100644 --- a/polkadot/xcm/xcm-simulator/example/src/lib.rs +++ b/polkadot/xcm/xcm-simulator/example/src/lib.rs @@ -649,4 +649,20 @@ mod tests { ); }); } + + #[test] + fn builder_pattern_works() { + let asset: MultiAsset = (Here, 100u128).into(); + let beneficiary: MultiLocation = AccountId32 { id: [0u8; 32], network: None }.into(); + let message: Xcm<()> = Xcm::builder() + .withdraw_asset(asset.clone().into()) + .buy_execution(asset.clone(), Unlimited) + .deposit_asset(asset.clone().into(), beneficiary) + .build(); + assert_eq!(message, Xcm(vec![ + WithdrawAsset(asset.clone().into()), + BuyExecution { fees: asset.clone(), weight_limit: Unlimited }, + DepositAsset { assets: asset.into(), beneficiary }, + ])); + } } From 47da310df60bfaea5d983cab738281f5fb34b35b Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Wed, 1 Nov 2023 17:34:05 +0100 Subject: [PATCH 02/12] Use the inflector crate for snake casing methods --- .../xcm/procedural/src/builder_pattern.rs | 22 ++----------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/polkadot/xcm/procedural/src/builder_pattern.rs b/polkadot/xcm/procedural/src/builder_pattern.rs index 25a305292d30..206249e60f32 100644 --- a/polkadot/xcm/procedural/src/builder_pattern.rs +++ b/polkadot/xcm/procedural/src/builder_pattern.rs @@ -2,6 +2,7 @@ use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; use syn::{parse_macro_input, DeriveInput, Data, Error, Fields}; use quote::{quote, format_ident}; +use inflector::Inflector; pub fn derive(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); @@ -25,7 +26,7 @@ pub fn derive(input: TokenStream) -> TokenStream { fn generate_methods_for_enum(name: syn::Ident, data_enum: &syn::DataEnum) -> TokenStream2 { let methods = data_enum.variants.iter().map(|variant| { let variant_name = &variant.ident; - let method_name_string = to_snake_case(&variant_name.to_string()); + let method_name_string = &variant_name.to_string().to_snake_case(); let method_name = syn::Ident::new(&method_name_string, variant_name.span()); match &variant.fields { Fields::Unit => { @@ -72,22 +73,3 @@ fn generate_methods_for_enum(name: syn::Ident, data_enum: &syn::DataEnum) -> Tok }; output } - -fn to_snake_case(s: &str) -> String { - let mut snake = String::with_capacity(s.len()); - let mut chars = s.chars().peekable(); - - while let Some(ch) = chars.next() { - // If it's an uppercase character and not the start - if ch.is_uppercase() && !snake.is_empty() { - // If the next character is lowercase, prepend an underscore - if chars.peek().map_or(false, |next| next.is_lowercase()) { - snake.push('_'); - } - } - - snake.extend(ch.to_lowercase()); - } - - snake -} From 86842085fdc961a7fd0062c2d71c3cf267f4c740 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Wed, 1 Nov 2023 18:06:02 +0100 Subject: [PATCH 03/12] Put instruction doc comments on top of builder methods --- .../xcm/procedural/src/builder_pattern.rs | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/polkadot/xcm/procedural/src/builder_pattern.rs b/polkadot/xcm/procedural/src/builder_pattern.rs index 206249e60f32..58bd9161ce00 100644 --- a/polkadot/xcm/procedural/src/builder_pattern.rs +++ b/polkadot/xcm/procedural/src/builder_pattern.rs @@ -1,6 +1,6 @@ use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; -use syn::{parse_macro_input, DeriveInput, Data, Error, Fields}; +use syn::{parse_macro_input, DeriveInput, Data, Error, Fields, Meta, Expr, Lit}; use quote::{quote, format_ident}; use inflector::Inflector; @@ -28,7 +28,23 @@ fn generate_methods_for_enum(name: syn::Ident, data_enum: &syn::DataEnum) -> Tok let variant_name = &variant.ident; let method_name_string = &variant_name.to_string().to_snake_case(); let method_name = syn::Ident::new(&method_name_string, variant_name.span()); - match &variant.fields { + let docs: Vec<_> = variant.attrs.iter().filter_map(|attr| { + if attr.path().is_ident("doc") { + match &attr.meta { + Meta::NameValue(pair) => match &pair.value { + Expr::Lit(inner) => match &inner.lit { + Lit::Str(string_literal) => Some(string_literal.value()), + _ => None, + }, + _ => None, + }, + _ => None, + } + } else { + None + } + }).map(|doc| syn::parse_str::(&format!("/// {}", doc)).unwrap()).collect(); + let method = match &variant.fields { Fields::Unit => { quote! { pub fn #method_name(mut self) -> Self { @@ -60,6 +76,10 @@ fn generate_methods_for_enum(name: syn::Ident, data_enum: &syn::DataEnum) -> Tok } } }, + }; + quote! { + #(#docs)* + #method } }); let output = quote! { From 4fd472d78f65ab207dea2f662e54863519027dd7 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Thu, 2 Nov 2023 11:14:57 +0100 Subject: [PATCH 04/12] Update polkadot/xcm/procedural/src/builder_pattern.rs Co-authored-by: Keith Yeung --- polkadot/xcm/procedural/src/builder_pattern.rs | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/polkadot/xcm/procedural/src/builder_pattern.rs b/polkadot/xcm/procedural/src/builder_pattern.rs index 58bd9161ce00..1a02cc8f9b7e 100644 --- a/polkadot/xcm/procedural/src/builder_pattern.rs +++ b/polkadot/xcm/procedural/src/builder_pattern.rs @@ -29,19 +29,9 @@ fn generate_methods_for_enum(name: syn::Ident, data_enum: &syn::DataEnum) -> Tok let method_name_string = &variant_name.to_string().to_snake_case(); let method_name = syn::Ident::new(&method_name_string, variant_name.span()); let docs: Vec<_> = variant.attrs.iter().filter_map(|attr| { - if attr.path().is_ident("doc") { - match &attr.meta { - Meta::NameValue(pair) => match &pair.value { - Expr::Lit(inner) => match &inner.lit { - Lit::Str(string_literal) => Some(string_literal.value()), - _ => None, - }, - _ => None, - }, - _ => None, - } - } else { - None + match &attr.meta { + Meta::NameValue(MetaNameValue { value: Expr::Lit(ExprLit { lit: Lit::Str(literal), .. ), .. }) if attr.path().is_ident("doc") => Some(literal.value()), + _ => None, } }).map(|doc| syn::parse_str::(&format!("/// {}", doc)).unwrap()).collect(); let method = match &variant.fields { From 81041241355d1e43a6df53573a0ce3f230b0c8bf Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Thu, 2 Nov 2023 11:22:08 +0100 Subject: [PATCH 05/12] Simplify match --- polkadot/xcm/procedural/src/builder_pattern.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/polkadot/xcm/procedural/src/builder_pattern.rs b/polkadot/xcm/procedural/src/builder_pattern.rs index 1a02cc8f9b7e..5d4816b01a2c 100644 --- a/polkadot/xcm/procedural/src/builder_pattern.rs +++ b/polkadot/xcm/procedural/src/builder_pattern.rs @@ -1,6 +1,6 @@ use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; -use syn::{parse_macro_input, DeriveInput, Data, Error, Fields, Meta, Expr, Lit}; +use syn::{parse_macro_input, DeriveInput, Data, Error, Fields, Meta, Expr, Lit, MetaNameValue, ExprLit}; use quote::{quote, format_ident}; use inflector::Inflector; @@ -30,7 +30,13 @@ fn generate_methods_for_enum(name: syn::Ident, data_enum: &syn::DataEnum) -> Tok let method_name = syn::Ident::new(&method_name_string, variant_name.span()); let docs: Vec<_> = variant.attrs.iter().filter_map(|attr| { match &attr.meta { - Meta::NameValue(MetaNameValue { value: Expr::Lit(ExprLit { lit: Lit::Str(literal), .. ), .. }) if attr.path().is_ident("doc") => Some(literal.value()), + Meta::NameValue(MetaNameValue { + value: Expr::Lit(ExprLit { + lit: Lit::Str(literal), + .. + }), + .. + }) if attr.path().is_ident("doc") => Some(literal.value()), _ => None, } }).map(|doc| syn::parse_str::(&format!("/// {}", doc)).unwrap()).collect(); From 5f82cc739df5eb546492e2f9f7baefaa2260a098 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Thu, 2 Nov 2023 11:49:51 +0100 Subject: [PATCH 06/12] Add UI tests --- Cargo.lock | 1 + polkadot/xcm/procedural/Cargo.toml | 1 + polkadot/xcm/procedural/tests/ui.rs | 30 +++++++++++++++++++ .../procedural/tests/ui/builder_pattern.rs | 9 ++++++ .../tests/ui/builder_pattern.stderr | 5 ++++ 5 files changed, 46 insertions(+) create mode 100644 polkadot/xcm/procedural/tests/ui.rs create mode 100644 polkadot/xcm/procedural/tests/ui/builder_pattern.rs create mode 100644 polkadot/xcm/procedural/tests/ui/builder_pattern.stderr diff --git a/Cargo.lock b/Cargo.lock index a292f0cb1ab3..0a14b34e326a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20846,6 +20846,7 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.38", + "trybuild", ] [[package]] diff --git a/polkadot/xcm/procedural/Cargo.toml b/polkadot/xcm/procedural/Cargo.toml index 56df0d94f586..759b6a2a1fcc 100644 --- a/polkadot/xcm/procedural/Cargo.toml +++ b/polkadot/xcm/procedural/Cargo.toml @@ -13,3 +13,4 @@ proc-macro2 = "1.0.56" quote = "1.0.28" syn = "2.0.38" Inflector = "0.11.4" +trybuild = { version = "1.0.74", features = ["diff"] } diff --git a/polkadot/xcm/procedural/tests/ui.rs b/polkadot/xcm/procedural/tests/ui.rs new file mode 100644 index 000000000000..822ade982d2e --- /dev/null +++ b/polkadot/xcm/procedural/tests/ui.rs @@ -0,0 +1,30 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +#[cfg(not(feature = "disable-ui-tests"))] +#[test] +fn ui() { + // Only run the ui tests when `RUN_UI_TESTS` is set. + if std::env::var("RUN_UI_TESTS").is_err() { + return; + } + + // As trybuild is using `cargo check`, we don't need the real WASM binaries. + std::env::set_var("SKIP_WASM_BUILD", "1"); + + let t = trybuild::TestCases::new(); + t.compile_fail("tests/ui/*.rs"); +} diff --git a/polkadot/xcm/procedural/tests/ui/builder_pattern.rs b/polkadot/xcm/procedural/tests/ui/builder_pattern.rs new file mode 100644 index 000000000000..4793350c7403 --- /dev/null +++ b/polkadot/xcm/procedural/tests/ui/builder_pattern.rs @@ -0,0 +1,9 @@ +//! Test error when attaching the derive builder macro to something +//! other than the XCM `Instruction` enum. + +use xcm_procedural::Builder; + +#[derive(Builder)] +struct SomeStruct; + +fn main() {} diff --git a/polkadot/xcm/procedural/tests/ui/builder_pattern.stderr b/polkadot/xcm/procedural/tests/ui/builder_pattern.stderr new file mode 100644 index 000000000000..4058283f46df --- /dev/null +++ b/polkadot/xcm/procedural/tests/ui/builder_pattern.stderr @@ -0,0 +1,5 @@ +error: Expected the `Instruction` enum + --> tests/ui/builder_pattern.rs:7:1 + | +7 | struct SomeStruct; + | ^^^^^^^^^^^^^^^^^^ From eca0cba2b28dcaca1d15b0d236cbb48f3cf9d4e0 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Thu, 2 Nov 2023 11:51:20 +0100 Subject: [PATCH 07/12] Add missing licenses --- polkadot/xcm/procedural/src/builder_pattern.rs | 18 ++++++++++++++++++ polkadot/xcm/procedural/tests/ui.rs | 4 ++-- .../xcm/procedural/tests/ui/builder_pattern.rs | 16 ++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/polkadot/xcm/procedural/src/builder_pattern.rs b/polkadot/xcm/procedural/src/builder_pattern.rs index 5d4816b01a2c..51b06b9393eb 100644 --- a/polkadot/xcm/procedural/src/builder_pattern.rs +++ b/polkadot/xcm/procedural/src/builder_pattern.rs @@ -1,3 +1,21 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +//! UI tests for XCM procedural macros + use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; use syn::{parse_macro_input, DeriveInput, Data, Error, Fields, Meta, Expr, Lit, MetaNameValue, ExprLit}; diff --git a/polkadot/xcm/procedural/tests/ui.rs b/polkadot/xcm/procedural/tests/ui.rs index 822ade982d2e..473ae1932c4d 100644 --- a/polkadot/xcm/procedural/tests/ui.rs +++ b/polkadot/xcm/procedural/tests/ui.rs @@ -1,12 +1,12 @@ // Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Polkadot. -// Substrate is free software: you can redistribute it and/or modify +// Polkadot is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// Substrate is distributed in the hope that it will be useful, +// Polkadot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. diff --git a/polkadot/xcm/procedural/tests/ui/builder_pattern.rs b/polkadot/xcm/procedural/tests/ui/builder_pattern.rs index 4793350c7403..e4bcda572ad7 100644 --- a/polkadot/xcm/procedural/tests/ui/builder_pattern.rs +++ b/polkadot/xcm/procedural/tests/ui/builder_pattern.rs @@ -1,3 +1,19 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + //! Test error when attaching the derive builder macro to something //! other than the XCM `Instruction` enum. From a84dad4a2ec7baa10970585ba84beb3287d85842 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Thu, 2 Nov 2023 12:00:23 +0100 Subject: [PATCH 08/12] Add prdoc --- prdoc/pr_2107.prdoc | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 prdoc/pr_2107.prdoc diff --git a/prdoc/pr_2107.prdoc b/prdoc/pr_2107.prdoc new file mode 100644 index 000000000000..0e33680555ac --- /dev/null +++ b/prdoc/pr_2107.prdoc @@ -0,0 +1,24 @@ +# Schema: Parity PR Documentation Schema (prdoc) +# See doc at https://github.com/paritytech/prdoc + +title: Add a builder pattern to create XCM programs + +doc: + - audience: Core Dev + description: | + XCMs can now be built using a builder pattern like so: + Xcm::builder() + .withdraw_asset(assets) + .buy_execution(fees, weight_limit) + .deposit_asset(assets, beneficiary) + .build(); + +migrations: + db: [] + + runtime: [] + +crates: + - name: xcm + +host_functions: [] From 48285f207906cf0fed1f474749a4be4e8f38e2d5 Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Thu, 2 Nov 2023 12:01:12 +0100 Subject: [PATCH 09/12] Move module descriptions --- polkadot/xcm/procedural/src/builder_pattern.rs | 2 +- polkadot/xcm/procedural/tests/ui.rs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/polkadot/xcm/procedural/src/builder_pattern.rs b/polkadot/xcm/procedural/src/builder_pattern.rs index 51b06b9393eb..fb8e1a6b8608 100644 --- a/polkadot/xcm/procedural/src/builder_pattern.rs +++ b/polkadot/xcm/procedural/src/builder_pattern.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -//! UI tests for XCM procedural macros +//! Derive macro for creating XCMs with a builder pattern use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; diff --git a/polkadot/xcm/procedural/tests/ui.rs b/polkadot/xcm/procedural/tests/ui.rs index 473ae1932c4d..90cb94e0706d 100644 --- a/polkadot/xcm/procedural/tests/ui.rs +++ b/polkadot/xcm/procedural/tests/ui.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . +//! UI tests for XCM procedural macros + #[cfg(not(feature = "disable-ui-tests"))] #[test] fn ui() { From 4668aa215ecab171a374830e0bb136ab773142be Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Thu, 2 Nov 2023 12:07:25 +0100 Subject: [PATCH 10/12] Update xcm-procedural Cargo.toml --- polkadot/xcm/procedural/Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/polkadot/xcm/procedural/Cargo.toml b/polkadot/xcm/procedural/Cargo.toml index 759b6a2a1fcc..0f714e581979 100644 --- a/polkadot/xcm/procedural/Cargo.toml +++ b/polkadot/xcm/procedural/Cargo.toml @@ -1,9 +1,11 @@ [package] name = "xcm-procedural" +description = "Procedural macros for XCM" authors.workspace = true edition.workspace = true license.workspace = true version = "1.0.0" +publish = true [lib] proc-macro = true From d4c1b554f8e9e30c159d8c5e54bbfeca35d4f279 Mon Sep 17 00:00:00 2001 From: command-bot <> Date: Thu, 2 Nov 2023 11:14:21 +0000 Subject: [PATCH 11/12] ".git/.scripts/commands/fmt/fmt.sh" --- .../xcm/procedural/src/builder_pattern.rs | 44 +++++++++++-------- polkadot/xcm/procedural/src/lib.rs | 2 +- polkadot/xcm/procedural/tests/ui.rs | 6 +-- polkadot/xcm/src/v3/mod.rs | 9 +++- polkadot/xcm/xcm-simulator/example/src/lib.rs | 13 +++--- 5 files changed, 45 insertions(+), 29 deletions(-) diff --git a/polkadot/xcm/procedural/src/builder_pattern.rs b/polkadot/xcm/procedural/src/builder_pattern.rs index fb8e1a6b8608..ebad54e972b6 100644 --- a/polkadot/xcm/procedural/src/builder_pattern.rs +++ b/polkadot/xcm/procedural/src/builder_pattern.rs @@ -16,18 +16,22 @@ //! Derive macro for creating XCMs with a builder pattern +use inflector::Inflector; use proc_macro::TokenStream; use proc_macro2::TokenStream as TokenStream2; -use syn::{parse_macro_input, DeriveInput, Data, Error, Fields, Meta, Expr, Lit, MetaNameValue, ExprLit}; -use quote::{quote, format_ident}; -use inflector::Inflector; +use quote::{format_ident, quote}; +use syn::{ + parse_macro_input, Data, DeriveInput, Error, Expr, ExprLit, Fields, Lit, Meta, MetaNameValue, +}; pub fn derive(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as DeriveInput); + let input = parse_macro_input!(input as DeriveInput); let builder_impl = match &input.data { Data::Enum(data_enum) => generate_methods_for_enum(input.ident, data_enum), - _ => return Error::new_spanned(&input, "Expected the `Instruction` enum") - .to_compile_error().into(), + _ => + return Error::new_spanned(&input, "Expected the `Instruction` enum") + .to_compile_error() + .into(), }; let output = quote! { pub struct XcmBuilder(Vec>); @@ -46,18 +50,18 @@ fn generate_methods_for_enum(name: syn::Ident, data_enum: &syn::DataEnum) -> Tok let variant_name = &variant.ident; let method_name_string = &variant_name.to_string().to_snake_case(); let method_name = syn::Ident::new(&method_name_string, variant_name.span()); - let docs: Vec<_> = variant.attrs.iter().filter_map(|attr| { - match &attr.meta { + let docs: Vec<_> = variant + .attrs + .iter() + .filter_map(|attr| match &attr.meta { Meta::NameValue(MetaNameValue { - value: Expr::Lit(ExprLit { - lit: Lit::Str(literal), - .. - }), + value: Expr::Lit(ExprLit { lit: Lit::Str(literal), .. }), .. }) if attr.path().is_ident("doc") => Some(literal.value()), _ => None, - } - }).map(|doc| syn::parse_str::(&format!("/// {}", doc)).unwrap()).collect(); + }) + .map(|doc| syn::parse_str::(&format!("/// {}", doc)).unwrap()) + .collect(); let method = match &variant.fields { Fields::Unit => { quote! { @@ -68,11 +72,13 @@ fn generate_methods_for_enum(name: syn::Ident, data_enum: &syn::DataEnum) -> Tok } }, Fields::Unnamed(fields) => { - let arg_names: Vec<_> = fields.unnamed.iter().enumerate() + let arg_names: Vec<_> = fields + .unnamed + .iter() + .enumerate() .map(|(index, _)| format_ident!("arg{}", index)) .collect(); - let arg_types: Vec<_> = fields.unnamed.iter().map(|field| &field.ty) - .collect(); + let arg_types: Vec<_> = fields.unnamed.iter().map(|field| &field.ty).collect(); quote! { pub fn #method_name(mut self, #(#arg_names: #arg_types),*) -> Self { self.0.push(#name::::#variant_name(#(#arg_names),*)); @@ -81,8 +87,8 @@ fn generate_methods_for_enum(name: syn::Ident, data_enum: &syn::DataEnum) -> Tok } }, Fields::Named(fields) => { - let arg_names: Vec<_> = fields.named.iter().map(|field| &field.ident).collect(); - let arg_types: Vec<_> = fields.named.iter().map(|field| &field.ty).collect(); + let arg_names: Vec<_> = fields.named.iter().map(|field| &field.ident).collect(); + let arg_types: Vec<_> = fields.named.iter().map(|field| &field.ty).collect(); quote! { pub fn #method_name(mut self, #(#arg_names: #arg_types),*) -> Self { self.0.push(#name::::#variant_name { #(#arg_names),* }); diff --git a/polkadot/xcm/procedural/src/lib.rs b/polkadot/xcm/procedural/src/lib.rs index addebd6638bd..83cc6cdf98ff 100644 --- a/polkadot/xcm/procedural/src/lib.rs +++ b/polkadot/xcm/procedural/src/lib.rs @@ -18,10 +18,10 @@ use proc_macro::TokenStream; +mod builder_pattern; mod v2; mod v3; mod weight_info; -mod builder_pattern; #[proc_macro] pub fn impl_conversion_functions_for_multilocation_v2(input: TokenStream) -> TokenStream { diff --git a/polkadot/xcm/procedural/tests/ui.rs b/polkadot/xcm/procedural/tests/ui.rs index 90cb94e0706d..a6ec35d0862a 100644 --- a/polkadot/xcm/procedural/tests/ui.rs +++ b/polkadot/xcm/procedural/tests/ui.rs @@ -20,9 +20,9 @@ #[test] fn ui() { // Only run the ui tests when `RUN_UI_TESTS` is set. - if std::env::var("RUN_UI_TESTS").is_err() { - return; - } + if std::env::var("RUN_UI_TESTS").is_err() { + return; + } // As trybuild is using `cargo check`, we don't need the real WASM binaries. std::env::set_var("SKIP_WASM_BUILD", "1"); diff --git a/polkadot/xcm/src/v3/mod.rs b/polkadot/xcm/src/v3/mod.rs index 4279bef80c2d..c4ba7dbe555d 100644 --- a/polkadot/xcm/src/v3/mod.rs +++ b/polkadot/xcm/src/v3/mod.rs @@ -401,7 +401,14 @@ impl XcmContext { /// /// This is the inner XCM format and is version-sensitive. Messages are typically passed using the /// outer XCM format, known as `VersionedXcm`. -#[derive(Derivative, Encode, Decode, TypeInfo, xcm_procedural::XcmWeightInfoTrait, xcm_procedural::Builder)] +#[derive( + Derivative, + Encode, + Decode, + TypeInfo, + xcm_procedural::XcmWeightInfoTrait, + xcm_procedural::Builder, +)] #[derivative(Clone(bound = ""), Eq(bound = ""), PartialEq(bound = ""), Debug(bound = ""))] #[codec(encode_bound())] #[codec(decode_bound())] diff --git a/polkadot/xcm/xcm-simulator/example/src/lib.rs b/polkadot/xcm/xcm-simulator/example/src/lib.rs index d0ea47ac6910..03e7c19a9148 100644 --- a/polkadot/xcm/xcm-simulator/example/src/lib.rs +++ b/polkadot/xcm/xcm-simulator/example/src/lib.rs @@ -659,10 +659,13 @@ mod tests { .buy_execution(asset.clone(), Unlimited) .deposit_asset(asset.clone().into(), beneficiary) .build(); - assert_eq!(message, Xcm(vec![ - WithdrawAsset(asset.clone().into()), - BuyExecution { fees: asset.clone(), weight_limit: Unlimited }, - DepositAsset { assets: asset.into(), beneficiary }, - ])); + assert_eq!( + message, + Xcm(vec![ + WithdrawAsset(asset.clone().into()), + BuyExecution { fees: asset.clone(), weight_limit: Unlimited }, + DepositAsset { assets: asset.into(), beneficiary }, + ]) + ); } } From 88b0d336559b91abbc6f850dc6905b4ede7baf5e Mon Sep 17 00:00:00 2001 From: Francisco Aguirre Date: Fri, 3 Nov 2023 13:02:06 +0100 Subject: [PATCH 12/12] Move trybuild to dev dependencies --- polkadot/xcm/procedural/Cargo.toml | 2 ++ polkadot/xcm/procedural/tests/ui/builder_pattern.stderr | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/polkadot/xcm/procedural/Cargo.toml b/polkadot/xcm/procedural/Cargo.toml index 0f714e581979..33c2a94be0e4 100644 --- a/polkadot/xcm/procedural/Cargo.toml +++ b/polkadot/xcm/procedural/Cargo.toml @@ -15,4 +15,6 @@ proc-macro2 = "1.0.56" quote = "1.0.28" syn = "2.0.38" Inflector = "0.11.4" + +[dev-dependencies] trybuild = { version = "1.0.74", features = ["diff"] } diff --git a/polkadot/xcm/procedural/tests/ui/builder_pattern.stderr b/polkadot/xcm/procedural/tests/ui/builder_pattern.stderr index 4058283f46df..439b40f31cae 100644 --- a/polkadot/xcm/procedural/tests/ui/builder_pattern.stderr +++ b/polkadot/xcm/procedural/tests/ui/builder_pattern.stderr @@ -1,5 +1,5 @@ error: Expected the `Instruction` enum - --> tests/ui/builder_pattern.rs:7:1 - | -7 | struct SomeStruct; - | ^^^^^^^^^^^^^^^^^^ + --> tests/ui/builder_pattern.rs:23:1 + | +23 | struct SomeStruct; + | ^^^^^^^^^^^^^^^^^^