mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 22:11:06 +00:00
Allow pallet error enum variants to contain fields (#10242)
* Allow pallet errors to contain at most one field * Update docs on pallet::error * Reword documentation * cargo fmt * Introduce CompactPalletError trait and require #[pallet::error] fields to implement them * cargo fmt * Do not assume tuple variants * Add CompactPalletError derive macro * Check for error type compactness in construct_runtime * cargo fmt * Derive CompactPalletError instead of implementing it directly during macro expansion * Implement CompactPalletError on OptionBool instead of Option<bool> * Check for type idents instead of variant ident * Add doc comments for ErrorCompactnessTest * Add an trait implementation of ErrorCompactnessTest for () * Convert the error field of DispatchError to a 4-element byte array * Add static check for pallet error size * Rename to MAX_PALLET_ERROR_ENCODED_SIZE * Remove ErrorCompactnessTest trait * Remove check_compactness * Return only the most significant byte when constructing a custom InvalidTransaction * Rename CompactPalletError to PalletError * Use counter to generate unique idents for assert macros * Make declarative pallet macros compile with pallet error size checks * Remove unused doc comment * Try and fix build errors * Fix build errors * Add macro_use for some test modules * Test fix * Fix compilation errors * Remove unneeded #[macro_use] * Resolve import ambiguity * Make path to pallet Error enum more specific * Fix test expectation * Disambiguate imports * Fix test expectations * Revert appending pallet module name to path * Rename bags_list::list::Error to BagError * Fixes * Fixes * Fixes * Fix test expectations * Fix test expectation * Add more implementations for PalletError * Lift the 1-field requirement for nested pallet errors * Fix UI test expectation * Remove PalletError impl for OptionBool * Use saturating operations * cargo fmt * Delete obsolete test * Fix test expectation * Try and use assert macro in const context * Pull out the pallet error size check macro * Fix UI test for const assertion * cargo fmt * Apply clippy suggestion * Fix doc comment * Docs for create_tt_return_macro * Ensure TryInto is imported in earlier Rust editions * Apply suggestions from code review Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Fix up comments and names * Implement PalletError for Never * cargo fmt * Don't compile example code * Bump API version for block builder * Factor in codec attributes while derving PalletError * Rename module and fix unit test * Add missing attribute * Check API version and convert ApplyExtrinsicResult accordingly * Rename BagError to ListError Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Use codec crate re-exported from frame support * Add links to types mentioned in doc comments Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * cargo fmt * cargo fmt * Re-add attribute for hidden docs Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
This commit is contained in:
@@ -241,6 +241,7 @@ fn construct_runtime_final_expansion(
|
||||
expand::expand_outer_inherent(&name, &block, &unchecked_extrinsic, &pallets, &scrate);
|
||||
let validate_unsigned = expand::expand_outer_validate_unsigned(&name, &pallets, &scrate);
|
||||
let integrity_test = decl_integrity_test(&scrate);
|
||||
let static_assertions = decl_static_assertions(&name, &pallets, &scrate);
|
||||
|
||||
let res = quote!(
|
||||
#scrate_decl
|
||||
@@ -282,6 +283,8 @@ fn construct_runtime_final_expansion(
|
||||
#validate_unsigned
|
||||
|
||||
#integrity_test
|
||||
|
||||
#static_assertions
|
||||
);
|
||||
|
||||
Ok(res)
|
||||
@@ -471,3 +474,34 @@ fn decl_integrity_test(scrate: &TokenStream2) -> TokenStream2 {
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn decl_static_assertions(
|
||||
runtime: &Ident,
|
||||
pallet_decls: &[Pallet],
|
||||
scrate: &TokenStream2,
|
||||
) -> TokenStream2 {
|
||||
let error_encoded_size_check = pallet_decls.iter().map(|decl| {
|
||||
let path = &decl.path;
|
||||
let assert_message = format!(
|
||||
"The maximum encoded size of the error type in the `{}` pallet exceeds \
|
||||
`MAX_MODULE_ERROR_ENCODED_SIZE`",
|
||||
decl.name,
|
||||
);
|
||||
|
||||
quote! {
|
||||
#scrate::tt_call! {
|
||||
macro = [{ #path::tt_error_token }]
|
||||
frame_support = [{ #scrate }]
|
||||
~~> #scrate::assert_error_encoded_size! {
|
||||
path = [{ #path }]
|
||||
runtime = [{ #runtime }]
|
||||
assert_message = [{ #assert_message }]
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
#(#error_encoded_size_check)*
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,9 +28,11 @@ mod dummy_part_checker;
|
||||
mod key_prefix;
|
||||
mod match_and_insert;
|
||||
mod pallet;
|
||||
mod pallet_error;
|
||||
mod partial_eq_no_bound;
|
||||
mod storage;
|
||||
mod transactional;
|
||||
mod tt_macro;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use std::{cell::RefCell, str::FromStr};
|
||||
@@ -41,9 +43,9 @@ thread_local! {
|
||||
static COUNTER: RefCell<Counter> = RefCell::new(Counter(0));
|
||||
}
|
||||
|
||||
/// Counter to generate a relatively unique identifier for macros querying for the existence of
|
||||
/// pallet parts. This is necessary because declarative macros gets hoisted to the crate root,
|
||||
/// which shares the namespace with other pallets containing the very same query macros.
|
||||
/// Counter to generate a relatively unique identifier for macros. This is necessary because
|
||||
/// declarative macros gets hoisted to the crate root, which shares the namespace with other pallets
|
||||
/// containing the very same macros.
|
||||
struct Counter(u64);
|
||||
|
||||
impl Counter {
|
||||
@@ -562,3 +564,14 @@ pub fn __generate_dummy_part_checker(input: TokenStream) -> TokenStream {
|
||||
pub fn match_and_insert(input: TokenStream) -> TokenStream {
|
||||
match_and_insert::match_and_insert(input)
|
||||
}
|
||||
|
||||
#[proc_macro_derive(PalletError, attributes(codec))]
|
||||
pub fn derive_pallet_error(input: TokenStream) -> TokenStream {
|
||||
pallet_error::derive_pallet_error(input)
|
||||
}
|
||||
|
||||
/// Internal macro used by `frame_support` to create tt-call-compliant macros
|
||||
#[proc_macro]
|
||||
pub fn __create_tt_macro(input: TokenStream) -> TokenStream {
|
||||
tt_macro::create_tt_return_macro(input)
|
||||
}
|
||||
|
||||
@@ -15,20 +15,48 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::pallet::Def;
|
||||
use crate::{
|
||||
pallet::{parse::error::VariantField, Def},
|
||||
COUNTER,
|
||||
};
|
||||
use frame_support_procedural_tools::get_doc_literals;
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
///
|
||||
/// * impl various trait on Error
|
||||
pub fn expand_error(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
let error = if let Some(error) = &def.error { error } else { return Default::default() };
|
||||
let count = COUNTER.with(|counter| counter.borrow_mut().inc());
|
||||
let error_token_unique_id =
|
||||
syn::Ident::new(&format!("__tt_error_token_{}", count), def.item.span());
|
||||
|
||||
let error_ident = &error.error;
|
||||
let frame_support = &def.frame_support;
|
||||
let frame_system = &def.frame_system;
|
||||
let config_where_clause = &def.config.where_clause;
|
||||
|
||||
let error = if let Some(error) = &def.error {
|
||||
error
|
||||
} else {
|
||||
return quote::quote! {
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! #error_token_unique_id {
|
||||
{
|
||||
$caller:tt
|
||||
frame_support = [{ $($frame_support:ident)::* }]
|
||||
} => {
|
||||
$($frame_support::)*tt_return! {
|
||||
$caller
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub use #error_token_unique_id as tt_error_token;
|
||||
}
|
||||
};
|
||||
|
||||
let error_ident = &error.error;
|
||||
let type_impl_gen = &def.type_impl_generics(error.attr_span);
|
||||
let type_use_gen = &def.type_use_generics(error.attr_span);
|
||||
let config_where_clause = &def.config.where_clause;
|
||||
|
||||
let phantom_variant: syn::Variant = syn::parse_quote!(
|
||||
#[doc(hidden)]
|
||||
@@ -39,13 +67,19 @@ pub fn expand_error(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
)
|
||||
);
|
||||
|
||||
let as_u8_matches = error.variants.iter().enumerate().map(
|
||||
|(i, (variant, _))| quote::quote_spanned!(error.attr_span => Self::#variant => #i as u8,),
|
||||
);
|
||||
|
||||
let as_str_matches = error.variants.iter().map(|(variant, _)| {
|
||||
let variant_str = format!("{}", variant);
|
||||
quote::quote_spanned!(error.attr_span => Self::#variant => #variant_str,)
|
||||
let as_str_matches = error.variants.iter().map(|(variant, field_ty, _)| {
|
||||
let variant_str = variant.to_string();
|
||||
match field_ty {
|
||||
Some(VariantField { is_named: true }) => {
|
||||
quote::quote_spanned!(error.attr_span => Self::#variant { .. } => #variant_str,)
|
||||
},
|
||||
Some(VariantField { is_named: false }) => {
|
||||
quote::quote_spanned!(error.attr_span => Self::#variant(..) => #variant_str,)
|
||||
},
|
||||
None => {
|
||||
quote::quote_spanned!(error.attr_span => Self::#variant => #variant_str,)
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
let error_item = {
|
||||
@@ -62,9 +96,14 @@ pub fn expand_error(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
let capture_docs = if cfg!(feature = "no-metadata-docs") { "never" } else { "always" };
|
||||
|
||||
// derive TypeInfo for error metadata
|
||||
error_item
|
||||
.attrs
|
||||
.push(syn::parse_quote!( #[derive(#frame_support::scale_info::TypeInfo)] ));
|
||||
error_item.attrs.push(syn::parse_quote! {
|
||||
#[derive(
|
||||
#frame_support::codec::Encode,
|
||||
#frame_support::codec::Decode,
|
||||
#frame_support::scale_info::TypeInfo,
|
||||
#frame_support::PalletError,
|
||||
)]
|
||||
});
|
||||
error_item.attrs.push(syn::parse_quote!(
|
||||
#[scale_info(skip_type_params(#type_use_gen), capture_docs = #capture_docs)]
|
||||
));
|
||||
@@ -90,14 +129,6 @@ pub fn expand_error(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
}
|
||||
|
||||
impl<#type_impl_gen> #error_ident<#type_use_gen> #config_where_clause {
|
||||
#[doc(hidden)]
|
||||
pub fn as_u8(&self) -> u8 {
|
||||
match &self {
|
||||
Self::__Ignore(_, _) => unreachable!("`__Ignore` can never be constructed"),
|
||||
#( #as_u8_matches )*
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match &self {
|
||||
@@ -120,18 +151,37 @@ pub fn expand_error(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
#config_where_clause
|
||||
{
|
||||
fn from(err: #error_ident<#type_use_gen>) -> Self {
|
||||
use #frame_support::codec::Encode;
|
||||
let index = <
|
||||
<T as #frame_system::Config>::PalletInfo
|
||||
as #frame_support::traits::PalletInfo
|
||||
>::index::<Pallet<#type_use_gen>>()
|
||||
.expect("Every active module has an index in the runtime; qed") as u8;
|
||||
let mut encoded = err.encode();
|
||||
encoded.resize(#frame_support::MAX_MODULE_ERROR_ENCODED_SIZE, 0);
|
||||
|
||||
#frame_support::sp_runtime::DispatchError::Module(#frame_support::sp_runtime::ModuleError {
|
||||
index,
|
||||
error: err.as_u8(),
|
||||
error: core::convert::TryInto::try_into(encoded).expect("encoded error is resized to be equal to the maximum encoded error size; qed"),
|
||||
message: Some(err.as_str()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! #error_token_unique_id {
|
||||
{
|
||||
$caller:tt
|
||||
frame_support = [{ $($frame_support:ident)::* }]
|
||||
} => {
|
||||
$($frame_support::)*tt_return! {
|
||||
$caller
|
||||
error = [{ #error_ident }]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub use #error_token_unique_id as tt_error_token;
|
||||
)
|
||||
}
|
||||
|
||||
@@ -18,20 +18,26 @@
|
||||
use super::helper;
|
||||
use frame_support_procedural_tools::get_doc_literals;
|
||||
use quote::ToTokens;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{spanned::Spanned, Fields};
|
||||
|
||||
/// List of additional token to be used for parsing.
|
||||
mod keyword {
|
||||
syn::custom_keyword!(Error);
|
||||
}
|
||||
|
||||
/// Records information about the error enum variants.
|
||||
pub struct VariantField {
|
||||
/// Whether or not the field is named, i.e. whether it is a tuple variant or struct variant.
|
||||
pub is_named: bool,
|
||||
}
|
||||
|
||||
/// This checks error declaration as a enum declaration with only variants without fields nor
|
||||
/// discriminant.
|
||||
pub struct ErrorDef {
|
||||
/// The index of error item in pallet module.
|
||||
pub index: usize,
|
||||
/// Variants ident and doc literals (ordered as declaration order)
|
||||
pub variants: Vec<(syn::Ident, Vec<syn::Lit>)>,
|
||||
/// Variants ident, optional field and doc literals (ordered as declaration order)
|
||||
pub variants: Vec<(syn::Ident, Option<VariantField>, Vec<syn::Lit>)>,
|
||||
/// A set of usage of instance, must be check for consistency with trait.
|
||||
pub instances: Vec<helper::InstanceUsage>,
|
||||
/// The keyword error used (contains span).
|
||||
@@ -70,18 +76,19 @@ impl ErrorDef {
|
||||
.variants
|
||||
.iter()
|
||||
.map(|variant| {
|
||||
if !matches!(variant.fields, syn::Fields::Unit) {
|
||||
let msg = "Invalid pallet::error, unexpected fields, must be `Unit`";
|
||||
return Err(syn::Error::new(variant.fields.span(), msg))
|
||||
}
|
||||
let field_ty = match &variant.fields {
|
||||
Fields::Unit => None,
|
||||
Fields::Named(_) => Some(VariantField { is_named: true }),
|
||||
Fields::Unnamed(_) => Some(VariantField { is_named: false }),
|
||||
};
|
||||
if variant.discriminant.is_some() {
|
||||
let msg = "Invalid pallet::error, unexpected discriminant, discriminant \
|
||||
let msg = "Invalid pallet::error, unexpected discriminant, discriminants \
|
||||
are not supported";
|
||||
let span = variant.discriminant.as_ref().unwrap().0.span();
|
||||
return Err(syn::Error::new(span, msg))
|
||||
}
|
||||
|
||||
Ok((variant.ident.clone(), get_doc_literals(&variant.attrs)))
|
||||
Ok((variant.ident.clone(), field_ty, get_doc_literals(&variant.attrs)))
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2021 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use frame_support_procedural_tools::generate_crate_access_2018;
|
||||
use quote::ToTokens;
|
||||
use std::str::FromStr;
|
||||
|
||||
// Derive `PalletError`
|
||||
pub fn derive_pallet_error(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let syn::DeriveInput { ident: name, generics, data, .. } = match syn::parse(input) {
|
||||
Ok(input) => input,
|
||||
Err(e) => return e.to_compile_error().into(),
|
||||
};
|
||||
|
||||
let frame_support = match generate_crate_access_2018("frame-support") {
|
||||
Ok(c) => c,
|
||||
Err(e) => return e.into_compile_error().into(),
|
||||
};
|
||||
let frame_support = &frame_support;
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
|
||||
let max_encoded_size = match data {
|
||||
syn::Data::Struct(syn::DataStruct { fields, .. }) => match fields {
|
||||
syn::Fields::Named(syn::FieldsNamed { named: fields, .. }) |
|
||||
syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed: fields, .. }) => {
|
||||
let maybe_field_tys = fields
|
||||
.iter()
|
||||
.map(|f| generate_field_types(f, &frame_support))
|
||||
.collect::<syn::Result<Vec<_>>>();
|
||||
let field_tys = match maybe_field_tys {
|
||||
Ok(tys) => tys.into_iter().flatten(),
|
||||
Err(e) => return e.into_compile_error().into(),
|
||||
};
|
||||
quote::quote! {
|
||||
0_usize
|
||||
#(
|
||||
.saturating_add(<
|
||||
#field_tys as #frame_support::traits::PalletError
|
||||
>::MAX_ENCODED_SIZE)
|
||||
)*
|
||||
}
|
||||
},
|
||||
syn::Fields::Unit => quote::quote!(0),
|
||||
},
|
||||
syn::Data::Enum(syn::DataEnum { variants, .. }) => {
|
||||
let field_tys = variants
|
||||
.iter()
|
||||
.map(|variant| generate_variant_field_types(variant, &frame_support))
|
||||
.collect::<Result<Vec<Option<Vec<proc_macro2::TokenStream>>>, syn::Error>>();
|
||||
|
||||
let field_tys = match field_tys {
|
||||
Ok(tys) => tys.into_iter().flatten().collect::<Vec<_>>(),
|
||||
Err(e) => return e.to_compile_error().into(),
|
||||
};
|
||||
|
||||
// We start with `1`, because the discriminant of an enum is stored as u8
|
||||
if field_tys.is_empty() {
|
||||
quote::quote!(1)
|
||||
} else {
|
||||
let variant_sizes = field_tys.into_iter().map(|variant_field_tys| {
|
||||
quote::quote! {
|
||||
1_usize
|
||||
#(.saturating_add(<
|
||||
#variant_field_tys as #frame_support::traits::PalletError
|
||||
>::MAX_ENCODED_SIZE))*
|
||||
}
|
||||
});
|
||||
|
||||
quote::quote! {{
|
||||
let mut size = 1_usize;
|
||||
let mut tmp = 0_usize;
|
||||
#(
|
||||
tmp = #variant_sizes;
|
||||
size = if tmp > size { tmp } else { size };
|
||||
tmp = 0_usize;
|
||||
)*
|
||||
size
|
||||
}}
|
||||
}
|
||||
},
|
||||
syn::Data::Union(syn::DataUnion { union_token, .. }) => {
|
||||
let msg = "Cannot derive `PalletError` for union; please implement it directly";
|
||||
return syn::Error::new(union_token.span, msg).into_compile_error().into()
|
||||
},
|
||||
};
|
||||
|
||||
quote::quote!(
|
||||
const _: () = {
|
||||
impl #impl_generics #frame_support::traits::PalletError
|
||||
for #name #ty_generics #where_clause
|
||||
{
|
||||
const MAX_ENCODED_SIZE: usize = #max_encoded_size;
|
||||
}
|
||||
};
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
fn generate_field_types(
|
||||
field: &syn::Field,
|
||||
scrate: &syn::Ident,
|
||||
) -> syn::Result<Option<proc_macro2::TokenStream>> {
|
||||
let attrs = &field.attrs;
|
||||
|
||||
for attr in attrs {
|
||||
if attr.path.is_ident("codec") {
|
||||
match attr.parse_meta()? {
|
||||
syn::Meta::List(ref meta_list) if meta_list.nested.len() == 1 => {
|
||||
match meta_list
|
||||
.nested
|
||||
.first()
|
||||
.expect("Just checked that there is one item; qed")
|
||||
{
|
||||
syn::NestedMeta::Meta(syn::Meta::Path(path))
|
||||
if path.get_ident().map_or(false, |i| i == "skip") =>
|
||||
return Ok(None),
|
||||
|
||||
syn::NestedMeta::Meta(syn::Meta::Path(path))
|
||||
if path.get_ident().map_or(false, |i| i == "compact") =>
|
||||
{
|
||||
let field_ty = &field.ty;
|
||||
return Ok(Some(quote::quote!(#scrate::codec::Compact<#field_ty>)))
|
||||
},
|
||||
|
||||
syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
|
||||
path,
|
||||
lit: syn::Lit::Str(lit_str),
|
||||
..
|
||||
})) if path.get_ident().map_or(false, |i| i == "encoded_as") => {
|
||||
let ty = proc_macro2::TokenStream::from_str(&lit_str.value())?;
|
||||
return Ok(Some(ty))
|
||||
},
|
||||
|
||||
_ => (),
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some(field.ty.to_token_stream()))
|
||||
}
|
||||
|
||||
fn generate_variant_field_types(
|
||||
variant: &syn::Variant,
|
||||
scrate: &syn::Ident,
|
||||
) -> syn::Result<Option<Vec<proc_macro2::TokenStream>>> {
|
||||
let attrs = &variant.attrs;
|
||||
|
||||
for attr in attrs {
|
||||
if attr.path.is_ident("codec") {
|
||||
match attr.parse_meta()? {
|
||||
syn::Meta::List(ref meta_list) if meta_list.nested.len() == 1 => {
|
||||
match meta_list
|
||||
.nested
|
||||
.first()
|
||||
.expect("Just checked that there is one item; qed")
|
||||
{
|
||||
syn::NestedMeta::Meta(syn::Meta::Path(path))
|
||||
if path.get_ident().map_or(false, |i| i == "skip") =>
|
||||
return Ok(None),
|
||||
|
||||
_ => (),
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match &variant.fields {
|
||||
syn::Fields::Named(syn::FieldsNamed { named: fields, .. }) |
|
||||
syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed: fields, .. }) => {
|
||||
let field_tys = fields
|
||||
.iter()
|
||||
.map(|field| generate_field_types(field, scrate))
|
||||
.collect::<syn::Result<Vec<_>>>()?;
|
||||
Ok(Some(field_tys.into_iter().flatten().collect()))
|
||||
},
|
||||
syn::Fields::Unit => Ok(None),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2022 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Implementation of the `create_tt_return_macro` macro
|
||||
|
||||
use crate::COUNTER;
|
||||
use frame_support_procedural_tools::generate_crate_access_2018;
|
||||
use proc_macro2::{Ident, TokenStream};
|
||||
use quote::format_ident;
|
||||
|
||||
struct CreateTtReturnMacroDef {
|
||||
name: Ident,
|
||||
args: Vec<(Ident, TokenStream)>,
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for CreateTtReturnMacroDef {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let name = input.parse()?;
|
||||
let _ = input.parse::<syn::Token![,]>()?;
|
||||
|
||||
let mut args = Vec::new();
|
||||
while !input.is_empty() {
|
||||
let mut value;
|
||||
let key: Ident = input.parse()?;
|
||||
let _ = input.parse::<syn::Token![=]>()?;
|
||||
let _: syn::token::Bracket = syn::bracketed!(value in input);
|
||||
let _: syn::token::Brace = syn::braced!(value in value);
|
||||
let value: TokenStream = value.parse()?;
|
||||
|
||||
args.push((key, value))
|
||||
}
|
||||
|
||||
Ok(Self { name, args })
|
||||
}
|
||||
}
|
||||
|
||||
/// A proc macro that accepts a name and any number of key-value pairs, to be used to create a
|
||||
/// declarative macro that follows tt-call conventions and simply calls [`tt_call::tt_return`],
|
||||
/// accepting an optional `frame-support` argument and returning the key-value pairs that were
|
||||
/// supplied to the proc macro.
|
||||
///
|
||||
/// # Example
|
||||
/// ```ignore
|
||||
/// __create_tt_macro! {
|
||||
/// my_tt_macro,
|
||||
/// foo = [{ bar }]
|
||||
/// }
|
||||
///
|
||||
/// // Creates the following declarative macro:
|
||||
///
|
||||
/// macro_rules! my_tt_macro {
|
||||
/// {
|
||||
/// $caller:tt
|
||||
/// $(frame_support = [{ $($frame_support:ident)::* }])?
|
||||
/// } => {
|
||||
/// frame_support::tt_return! {
|
||||
/// $caller
|
||||
/// foo = [{ bar }]
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn create_tt_return_macro(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let CreateTtReturnMacroDef { name, args } =
|
||||
syn::parse_macro_input!(input as CreateTtReturnMacroDef);
|
||||
|
||||
let frame_support = match generate_crate_access_2018("frame-support") {
|
||||
Ok(i) => i,
|
||||
Err(e) => return e.into_compile_error().into(),
|
||||
};
|
||||
let (keys, values): (Vec<_>, Vec<_>) = args.into_iter().unzip();
|
||||
let count = COUNTER.with(|counter| counter.borrow_mut().inc());
|
||||
let unique_name = format_ident!("{}_{}", name, count);
|
||||
|
||||
let decl_macro = quote::quote! {
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! #unique_name {
|
||||
{
|
||||
$caller:tt
|
||||
$(frame_support = [{ $($frame_support:ident)::* }])?
|
||||
} => {
|
||||
#frame_support::tt_return! {
|
||||
$caller
|
||||
#(
|
||||
#keys = [{ #values }]
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub use #unique_name as #name;
|
||||
};
|
||||
|
||||
decl_macro.into()
|
||||
}
|
||||
Reference in New Issue
Block a user