Add pallet attribute macro to declare pallets (#6877)

* rename system Config to system Trait.

command used:
```
find frame/ bin/ test-utils/ utils/ -name *.rs -exec sed -i 's/system::Trait>::/system::Config>::/g' {} \;
find frame/ bin/ test-utils/ utils/ -name *.rs -exec sed -i 's/impl frame_system::Trait for /impl frame_system::Config for /g' {} \;
find frame/ bin/ test-utils/ utils/ -name *.rs -exec sed -i 's/impl system::Trait for /impl system::Config for /g' {} \;
```
plus some manual ones especially for frame-support tests and frame-system

* make construct_runtime handle Pallet and Module

pallets can now be implemented on struct named Pallet or Module, both
definition are valid.
This is because next macro will generate only Pallet placeholder.

* introduce pallet attribute macro

currently just with tests, frame_system and other example hasn't been
upgraded

* allow to print some upgrade helper from decl_storage

* Improved error msg, typo.

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>

* Improved error msg, typo.

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>

* Improved error message on unexpected attributes + ui test

* add test for transactional

* various typo

* some tips when spans are lost

* allow pallet to depend on other pallet instances

* make event type metadata consistent with call and constant

* error messages

* ignore doc example

* fix pallet upgrade template

* fixup

* fix doc

* fix indentation

* Apply suggestions code formatting

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* some renames + fix compilation

* remove unsupported genesis config type alias

* merge fixup

* fix ui tests

* additional doc

* implement StorageInstance with new syntax

* fix line width

* fix doc: because pallet doc goes below reexport doc

* Update frame/support/procedural/src/pallet/parse/event.rs

Co-authored-by: Andrew Jones <ascjones@gmail.com>

* Update frame/system/src/lib.rs

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>

* Update frame/support/test/tests/pallet_ui.rs

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>

* improve doc as suggested

* revert construct_runtime Pallet part.

This revert the changes on construct_runtime. Now construct_runtime is
unchanged and instead pallet macro create a type alias
`type Module<..> = Pallet<..>` to be used by construct_runtime

* refactor with less intricated code

* fix ui test with new image

* fix ui tests

* add minor tests

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
Co-authored-by: Andrew Jones <ascjones@gmail.com>
Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
This commit is contained in:
Guillaume Thiolliere
2020-12-24 12:33:40 +01:00
committed by GitHub
parent 8e3d8a6a09
commit 6dfad0921b
131 changed files with 9610 additions and 31 deletions
@@ -21,12 +21,14 @@
mod storage;
mod construct_runtime;
mod pallet;
mod pallet_version;
mod transactional;
mod debug_no_bound;
mod clone_no_bound;
mod partial_eq_no_bound;
pub(crate) use storage::INHERENT_INSTANCE_NAME;
use proc_macro::TokenStream;
/// Declares strongly-typed wrappers around codec-compatible types in storage.
@@ -305,6 +307,12 @@ pub fn construct_runtime(input: TokenStream) -> TokenStream {
construct_runtime::construct_runtime(input)
}
/// Macro to define a pallet. Docs are at `frame_support::pallet`.
#[proc_macro_attribute]
pub fn pallet(attr: TokenStream, item: TokenStream) -> TokenStream {
pallet::pallet(attr, item)
}
/// Execute the annotated function in a new storage transaction.
///
/// The return type of the annotated function must be `Result`. All changes to storage performed
@@ -0,0 +1,201 @@
// This file is part of Substrate.
// Copyright (C) 2020 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 crate::pallet::Def;
use frame_support_procedural_tools::clean_type_string;
use syn::spanned::Spanned;
/// * Generate enum call and implement various trait on it.
/// * Implement Callable and call_function on `Pallet`
pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream {
let frame_support = &def.frame_support;
let frame_system = &def.frame_system;
let type_impl_gen = &def.type_impl_generics();
let type_decl_bounded_gen = &def.type_decl_bounded_generics();
let type_use_gen = &def.type_use_generics();
let call_ident = syn::Ident::new("Call", def.call.attr_span.clone());
let pallet_ident = &def.pallet_struct.pallet;
let where_clause = &def.call.where_clause;
let fn_name = def.call.methods.iter().map(|method| &method.name).collect::<Vec<_>>();
let fn_weight = def.call.methods.iter().map(|method| &method.weight);
let fn_doc = def.call.methods.iter().map(|method| &method.docs).collect::<Vec<_>>();
let args_name = def.call.methods.iter()
.map(|method| method.args.iter().map(|(_, name, _)| name.clone()).collect::<Vec<_>>())
.collect::<Vec<_>>();
let args_type = def.call.methods.iter()
.map(|method| method.args.iter().map(|(_, _, type_)| type_.clone()).collect::<Vec<_>>())
.collect::<Vec<_>>();
let args_compact_attr = def.call.methods.iter().map(|method| {
method.args.iter()
.map(|(is_compact, _, type_)| {
if *is_compact {
quote::quote_spanned!(type_.span() => #[codec(compact)] )
} else {
quote::quote!()
}
})
.collect::<Vec<_>>()
});
let args_metadata_type = def.call.methods.iter().map(|method| {
method.args.iter()
.map(|(is_compact, _, type_)| {
let final_type = if *is_compact {
quote::quote!(Compact<#type_>)
} else {
quote::quote!(#type_)
};
clean_type_string(&final_type.to_string())
})
.collect::<Vec<_>>()
});
quote::quote_spanned!(def.call.attr_span =>
#[derive(
#frame_support::RuntimeDebugNoBound,
#frame_support::CloneNoBound,
#frame_support::EqNoBound,
#frame_support::PartialEqNoBound,
#frame_support::codec::Encode,
#frame_support::codec::Decode,
)]
#[allow(non_camel_case_types)]
pub enum #call_ident<#type_decl_bounded_gen> #where_clause {
#[doc(hidden)]
#[codec(skip)]
__Ignore(
#frame_support::sp_std::marker::PhantomData<(#type_use_gen,)>,
#frame_support::Never,
),
#( #fn_name( #( #args_compact_attr #args_type ),* ), )*
}
impl<#type_impl_gen> #frame_support::dispatch::GetDispatchInfo
for #call_ident<#type_use_gen>
#where_clause
{
fn get_dispatch_info(&self) -> #frame_support::dispatch::DispatchInfo {
match *self {
#(
Self::#fn_name ( #( ref #args_name, )* ) => {
let base_weight = #fn_weight;
let weight = <
dyn #frame_support::dispatch::WeighData<( #( & #args_type, )* )>
>::weigh_data(&base_weight, ( #( #args_name, )* ));
let class = <
dyn #frame_support::dispatch::ClassifyDispatch<
( #( & #args_type, )* )
>
>::classify_dispatch(&base_weight, ( #( #args_name, )* ));
let pays_fee = <
dyn #frame_support::dispatch::PaysFee<( #( & #args_type, )* )>
>::pays_fee(&base_weight, ( #( #args_name, )* ));
#frame_support::dispatch::DispatchInfo {
weight,
class,
pays_fee,
}
},
)*
Self::__Ignore(_, _) => unreachable!("__Ignore cannot be used"),
}
}
}
impl<#type_impl_gen> #frame_support::dispatch::GetCallName for #call_ident<#type_use_gen>
#where_clause
{
fn get_call_name(&self) -> &'static str {
match *self {
#( Self::#fn_name(..) => stringify!(#fn_name), )*
Self::__Ignore(_, _) => unreachable!("__PhantomItem cannot be used."),
}
}
fn get_call_names() -> &'static [&'static str] {
&[ #( stringify!(#fn_name), )* ]
}
}
impl<#type_impl_gen> #frame_support::traits::UnfilteredDispatchable
for #call_ident<#type_use_gen>
#where_clause
{
type Origin = #frame_system::pallet_prelude::OriginFor<T>;
fn dispatch_bypass_filter(
self,
origin: Self::Origin
) -> #frame_support::dispatch::DispatchResultWithPostInfo {
match self {
#(
Self::#fn_name( #( #args_name, )* ) =>
<#pallet_ident<#type_use_gen>>::#fn_name(origin, #( #args_name, )* )
.map(Into::into).map_err(Into::into),
)*
Self::__Ignore(_, _) => {
let _ = origin; // Use origin for empty Call enum
unreachable!("__PhantomItem cannot be used.");
},
}
}
}
impl<#type_impl_gen> #frame_support::dispatch::Callable<T> for #pallet_ident<#type_use_gen>
#where_clause
{
type Call = #call_ident<#type_use_gen>;
}
impl<#type_impl_gen> #pallet_ident<#type_use_gen> #where_clause {
#[doc(hidden)]
pub fn call_functions() -> &'static [#frame_support::dispatch::FunctionMetadata] {
&[ #(
#frame_support::dispatch::FunctionMetadata {
name: #frame_support::dispatch::DecodeDifferent::Encode(
stringify!(#fn_name)
),
arguments: #frame_support::dispatch::DecodeDifferent::Encode(
&[ #(
#frame_support::dispatch::FunctionArgumentMetadata {
name: #frame_support::dispatch::DecodeDifferent::Encode(
stringify!(#args_name)
),
ty: #frame_support::dispatch::DecodeDifferent::Encode(
#args_metadata_type
),
},
)* ]
),
documentation: #frame_support::dispatch::DecodeDifferent::Encode(
&[ #( #fn_doc ),* ]
),
},
)* ]
}
}
)
}
@@ -0,0 +1,138 @@
// This file is part of Substrate.
// Copyright (C) 2020 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 crate::pallet::Def;
use frame_support_procedural_tools::clean_type_string;
use quote::ToTokens;
struct ConstDef {
/// Name of the associated type.
pub ident: syn::Ident,
/// The type in Get, e.g. `u32` in `type Foo: Get<u32>;`, but `Self` is replaced by `T`
pub type_: syn::Type,
/// The doc associated
pub doc: Vec<syn::Lit>,
/// default_byte implementation
pub default_byte_impl: proc_macro2::TokenStream,
}
/// * Impl fn module_constant_metadata for pallet.
pub fn expand_constants(def: &mut Def) -> proc_macro2::TokenStream {
let frame_support = &def.frame_support;
let type_impl_gen = &def.type_impl_generics();
let type_decl_gen = &def.type_decl_generics();
let type_use_gen = &def.type_use_generics();
let pallet_ident = &def.pallet_struct.pallet;
let mut where_clauses = vec![&def.config.where_clause];
where_clauses.extend(def.extra_constants.iter().map(|d| &d.where_clause));
let completed_where_clause = super::merge_where_clauses(&where_clauses);
let config_consts = def.config.consts_metadata.iter().map(|const_| {
let ident = &const_.ident;
let const_type = &const_.type_;
ConstDef {
ident: const_.ident.clone(),
type_: const_.type_.clone(),
doc: const_.doc.clone(),
default_byte_impl: quote::quote!(
let value = <T::#ident as #frame_support::traits::Get<#const_type>>::get();
#frame_support::codec::Encode::encode(&value)
),
}
});
let extra_consts = def.extra_constants.iter().flat_map(|d| &d.extra_constants).map(|const_| {
let ident = &const_.ident;
ConstDef {
ident: const_.ident.clone(),
type_: const_.type_.clone(),
doc: const_.doc.clone(),
default_byte_impl: quote::quote!(
let value = <Pallet<#type_use_gen>>::#ident();
#frame_support::codec::Encode::encode(&value)
),
}
});
let consts = config_consts.chain(extra_consts)
.map(|const_| {
let const_type = &const_.type_;
let const_type_str = clean_type_string(&const_type.to_token_stream().to_string());
let ident = &const_.ident;
let ident_str = format!("{}", ident);
let doc = const_.doc.clone().into_iter();
let default_byte_impl = &const_.default_byte_impl;
let default_byte_getter = syn::Ident::new(
&format!("{}DefaultByteGetter", ident),
ident.span()
);
quote::quote!({
#[allow(non_upper_case_types)]
#[allow(non_camel_case_types)]
struct #default_byte_getter<#type_decl_gen>(
#frame_support::sp_std::marker::PhantomData<(#type_use_gen)>
);
impl<#type_impl_gen> #frame_support::dispatch::DefaultByte for
#default_byte_getter<#type_use_gen>
#completed_where_clause
{
fn default_byte(&self) -> #frame_support::sp_std::vec::Vec<u8> {
#default_byte_impl
}
}
unsafe impl<#type_impl_gen> Send for #default_byte_getter<#type_use_gen>
#completed_where_clause
{}
unsafe impl<#type_impl_gen> Sync for #default_byte_getter<#type_use_gen>
#completed_where_clause
{}
#frame_support::dispatch::ModuleConstantMetadata {
name: #frame_support::dispatch::DecodeDifferent::Encode(#ident_str),
ty: #frame_support::dispatch::DecodeDifferent::Encode(#const_type_str),
value: #frame_support::dispatch::DecodeDifferent::Encode(
#frame_support::dispatch::DefaultByteGetter(
&#default_byte_getter::<#type_use_gen>(
#frame_support::sp_std::marker::PhantomData
)
)
),
documentation: #frame_support::dispatch::DecodeDifferent::Encode(
&[ #( #doc ),* ]
),
}
})
});
quote::quote!(
impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause{
#[doc(hidden)]
pub fn module_constants_metadata()
-> &'static [#frame_support::dispatch::ModuleConstantMetadata]
{
&[ #( #consts ),* ]
}
}
)
}
@@ -0,0 +1,141 @@
// This file is part of Substrate.
// Copyright (C) 2020 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 crate::pallet::Def;
use syn::spanned::Spanned;
/// * impl various trait on Error
/// * impl ModuleErrorMetadata for 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 error_item_span =
def.item.content.as_mut().expect("Checked by def parser").1[error.index].span();
let error_ident = &error.error;
let frame_support = &def.frame_support;
let frame_system = &def.frame_system;
let type_impl_gen = &def.type_impl_generics();
let type_use_gen = &def.type_use_generics();
let config_where_clause = &def.config.where_clause;
let phantom_variant: syn::Variant = syn::parse_quote!(
#[doc(hidden)]
__Ignore(
#frame_support::sp_std::marker::PhantomData<(#type_use_gen)>,
#frame_support::Never,
)
);
let as_u8_matches = error.variants.iter().enumerate()
.map(|(i, (variant, _))| quote::quote!(Self::#variant => #i as u8,));
let as_str_matches = error.variants.iter()
.map(|(variant, _)| {
let variant_str = format!("{}", variant);
quote::quote!(Self::#variant => #variant_str,)
});
let metadata = error.variants.iter()
.map(|(variant, doc)| {
let variant_str = format!("{}", variant);
quote::quote!(
#frame_support::error::ErrorMetadata {
name: #frame_support::error::DecodeDifferent::Encode(#variant_str),
documentation: #frame_support::error::DecodeDifferent::Encode(&[ #( #doc, )* ]),
},
)
});
let error_item = {
let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[error.index];
if let syn::Item::Enum(item) = item {
item
} else {
unreachable!("Checked by event parser")
}
};
error_item.variants.insert(0, phantom_variant);
quote::quote_spanned!(error_item_span =>
impl<#type_impl_gen> #frame_support::sp_std::fmt::Debug for #error_ident<#type_use_gen>
#config_where_clause
{
fn fmt(&self, f: &mut #frame_support::sp_std::fmt::Formatter<'_>)
-> #frame_support::sp_std::fmt::Result
{
f.write_str(self.as_str())
}
}
impl<#type_impl_gen> #error_ident<#type_use_gen> #config_where_clause {
pub fn as_u8(&self) -> u8 {
match &self {
Self::__Ignore(_, _) => unreachable!("`__Ignore` can never be constructed"),
#( #as_u8_matches )*
}
}
pub fn as_str(&self) -> &'static str {
match &self {
Self::__Ignore(_, _) => unreachable!("`__Ignore` can never be constructed"),
#( #as_str_matches )*
}
}
}
impl<#type_impl_gen> From<#error_ident<#type_use_gen>> for &'static str
#config_where_clause
{
fn from(err: #error_ident<#type_use_gen>) -> &'static str {
err.as_str()
}
}
impl<#type_impl_gen> From<#error_ident<#type_use_gen>>
for #frame_support::sp_runtime::DispatchError
#config_where_clause
{
fn from(err: #error_ident<#type_use_gen>) -> Self {
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;
#frame_support::sp_runtime::DispatchError::Module {
index,
error: err.as_u8(),
message: Some(err.as_str()),
}
}
}
impl<#type_impl_gen> #frame_support::error::ModuleErrorMetadata
for #error_ident<#type_use_gen>
#config_where_clause
{
fn metadata() -> &'static [#frame_support::error::ErrorMetadata] {
&[ #( #metadata )* ]
}
}
)
}
@@ -0,0 +1,143 @@
// This file is part of Substrate.
// Copyright (C) 2020 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 crate::pallet::Def;
use syn::spanned::Spanned;
/// * Add __Ignore variant on Event
/// * Impl various trait on Event including metadata
/// * if deposit_event is defined, implement deposit_event on module.
pub fn expand_event(def: &mut Def) -> proc_macro2::TokenStream {
let event = if let Some(event) = &def.event {
event
} else {
return Default::default()
};
let event_where_clause = &event.where_clause;
// NOTE: actually event where clause must be a subset of config where clause because of
// `type Event: From<Event<Self>>`. But we merge either way for potential better error message
let completed_where_clause = super::merge_where_clauses(&[
&event.where_clause,
&def.config.where_clause,
]);
let event_ident = &event.event;
let frame_system = &def.frame_system;
let frame_support = &def.frame_support;
let event_use_gen = &event.gen_kind.type_use_gen();
let event_impl_gen= &event.gen_kind.type_impl_gen();
let metadata = event.metadata.iter()
.map(|(ident, args, docs)| {
let name = format!("{}", ident);
quote::quote!(
#frame_support::event::EventMetadata {
name: #frame_support::event::DecodeDifferent::Encode(#name),
arguments: #frame_support::event::DecodeDifferent::Encode(&[
#( #args, )*
]),
documentation: #frame_support::event::DecodeDifferent::Encode(&[
#( #docs, )*
]),
},
)
});
let event_item_span =
def.item.content.as_mut().expect("Checked by def parser").1[event.index].span();
let event_item = {
let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[event.index];
if let syn::Item::Enum(item) = item {
item
} else {
unreachable!("Checked by event parser")
}
};
// Phantom data is added for generic event.
if event.gen_kind.is_generic() {
let variant = syn::parse_quote!(
#[doc(hidden)]
#[codec(skip)]
__Ignore(
#frame_support::sp_std::marker::PhantomData<(#event_use_gen)>,
#frame_support::Never,
)
);
// Push ignore variant at the end.
event_item.variants.push(variant);
}
// derive some traits because system event require Clone, FullCodec, Eq, PartialEq and Debug
event_item.attrs.push(syn::parse_quote!(
#[derive(
#frame_support::CloneNoBound,
#frame_support::EqNoBound,
#frame_support::PartialEqNoBound,
#frame_support::RuntimeDebugNoBound,
#frame_support::codec::Encode,
#frame_support::codec::Decode,
)]
));
let deposit_event = if let Some((fn_vis, fn_span)) = &event.deposit_event {
let event_use_gen = &event.gen_kind.type_use_gen();
let trait_use_gen = &def.trait_use_generics();
let type_impl_gen = &def.type_impl_generics();
let type_use_gen = &def.type_use_generics();
quote::quote_spanned!(*fn_span =>
impl<#type_impl_gen> Pallet<#type_use_gen> #completed_where_clause {
#fn_vis fn deposit_event(event: Event<#event_use_gen>) {
let event = <
<T as Config#trait_use_gen>::Event as
From<Event<#event_use_gen>>
>::from(event);
let event = <
<T as Config#trait_use_gen>::Event as
Into<<T as #frame_system::Config>::Event>
>::into(event);
<#frame_system::Pallet<T>>::deposit_event(event)
}
}
)
} else {
Default::default()
};
quote::quote_spanned!(event_item_span =>
#deposit_event
impl<#event_impl_gen> From<#event_ident<#event_use_gen>> for () #event_where_clause {
fn from(_: #event_ident<#event_use_gen>) -> () { () }
}
impl<#event_impl_gen> #event_ident<#event_use_gen> #event_where_clause {
#[allow(dead_code)]
#[doc(hidden)]
pub fn metadata() -> &'static [#frame_support::event::EventMetadata] {
&[ #( #metadata )* ]
}
}
)
}
@@ -0,0 +1,72 @@
// This file is part of Substrate.
// Copyright (C) 2020 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 crate::pallet::Def;
use syn::spanned::Spanned;
/// * implement the trait `sp_runtime::BuildModuleGenesisStorage`
/// * add #[cfg(features = "std")] to GenesisBuild implementation.
pub fn expand_genesis_build(def: &mut Def) -> proc_macro2::TokenStream {
let genesis_config = if let Some(genesis_config) = &def.genesis_config {
genesis_config
} else {
return Default::default()
};
let frame_support = &def.frame_support;
let type_impl_gen = &def.type_impl_generics();
let type_use_gen = &def.type_use_generics();
let trait_use_gen = if def.config.has_instance {
quote::quote!(T, I)
} else {
// `__InherentHiddenInstance` used by construct_runtime here is alias for `()`
quote::quote!(T, ())
};
let gen_cfg_ident = &genesis_config.genesis_config;
let gen_cfg_use_gen = genesis_config.gen_kind.type_use_gen();
let genesis_build = def.genesis_build.as_ref().expect("Checked by def parser");
let genesis_build_item = &mut def.item.content.as_mut()
.expect("Checked by def parser").1[genesis_build.index];
let genesis_build_item_impl = if let syn::Item::Impl(impl_) = genesis_build_item {
impl_
} else {
unreachable!("Checked by genesis_build parser")
};
genesis_build_item_impl.attrs.push(syn::parse_quote!( #[cfg(feature = "std")] ));
let where_clause = &genesis_build.where_clause;
quote::quote_spanned!(genesis_build_item.span() =>
#[cfg(feature = "std")]
impl<#type_impl_gen> #frame_support::sp_runtime::BuildModuleGenesisStorage<#trait_use_gen>
for #gen_cfg_ident<#gen_cfg_use_gen> #where_clause
{
fn build_module_genesis_storage(
&self,
storage: &mut #frame_support::sp_runtime::Storage,
) -> std::result::Result<(), std::string::String> {
#frame_support::BasicExternalities::execute_with_storage(storage, || {
<Self as #frame_support::traits::GenesisBuild<#type_use_gen>>::build(self);
Ok(())
})
}
}
)
}
@@ -0,0 +1,49 @@
// This file is part of Substrate.
// Copyright (C) 2020 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 crate::pallet::Def;
/// * add various derive trait on GenesisConfig struct.
pub fn expand_genesis_config(def: &mut Def) -> proc_macro2::TokenStream {
let genesis_config = if let Some(genesis_config) = &def.genesis_config {
genesis_config
} else {
return Default::default()
};
let frame_support = &def.frame_support;
let genesis_config_item = &mut def.item.content.as_mut()
.expect("Checked by def parser").1[genesis_config.index];
match genesis_config_item {
syn::Item::Enum(syn::ItemEnum { attrs, ..}) |
syn::Item::Struct(syn::ItemStruct { attrs, .. }) |
syn::Item::Type(syn::ItemType { attrs, .. }) => {
attrs.push(syn::parse_quote!( #[cfg(feature = "std")] ));
attrs.push(syn::parse_quote!(
#[derive(#frame_support::Serialize, #frame_support::Deserialize)]
));
attrs.push(syn::parse_quote!( #[serde(rename_all = "camelCase")] ));
attrs.push(syn::parse_quote!( #[serde(deny_unknown_fields)] ));
attrs.push(syn::parse_quote!( #[serde(bound(serialize = ""))] ));
attrs.push(syn::parse_quote!( #[serde(bound(deserialize = ""))] ));
},
_ => unreachable!("Checked by genesis_config parser"),
}
Default::default()
}
@@ -0,0 +1,110 @@
// This file is part of Substrate.
// Copyright (C) 2020 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 crate::pallet::Def;
use syn::spanned::Spanned;
/// * implement the individual traits using the Hooks trait
pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream {
let frame_support = &def.frame_support;
let type_impl_gen = &def.type_impl_generics();
let type_use_gen = &def.type_use_generics();
let pallet_ident = &def.pallet_struct.pallet;
let where_clause = &def.hooks.where_clause;
let frame_system = &def.frame_system;
let hooks_item_span = def.item.content.as_mut()
.expect("Checked by def parser").1[def.hooks.index].span();
quote::quote_spanned!(hooks_item_span =>
impl<#type_impl_gen>
#frame_support::traits::OnFinalize<<T as #frame_system::Config>::BlockNumber>
for #pallet_ident<#type_use_gen> #where_clause
{
fn on_finalize(n: <T as #frame_system::Config>::BlockNumber) {
<
Self as #frame_support::traits::Hooks<
<T as #frame_system::Config>::BlockNumber
>
>::on_finalize(n)
}
}
impl<#type_impl_gen>
#frame_support::traits::OnInitialize<<T as #frame_system::Config>::BlockNumber>
for #pallet_ident<#type_use_gen> #where_clause
{
fn on_initialize(
n: <T as #frame_system::Config>::BlockNumber
) -> #frame_support::weights::Weight {
<
Self as #frame_support::traits::Hooks<
<T as #frame_system::Config>::BlockNumber
>
>::on_initialize(n)
}
}
impl<#type_impl_gen>
#frame_support::traits::OnRuntimeUpgrade
for #pallet_ident<#type_use_gen> #where_clause
{
fn on_runtime_upgrade() -> #frame_support::weights::Weight {
let result = <
Self as #frame_support::traits::Hooks<
<T as #frame_system::Config>::BlockNumber
>
>::on_runtime_upgrade();
#frame_support::crate_to_pallet_version!()
.put_into_storage::<<T as #frame_system::Config>::PalletInfo, Self>();
let additional_write = <
<T as #frame_system::Config>::DbWeight as #frame_support::traits::Get<_>
>::get().writes(1);
result.saturating_add(additional_write)
}
}
impl<#type_impl_gen>
#frame_support::traits::OffchainWorker<<T as #frame_system::Config>::BlockNumber>
for #pallet_ident<#type_use_gen> #where_clause
{
fn offchain_worker(n: <T as #frame_system::Config>::BlockNumber) {
<
Self as #frame_support::traits::Hooks<
<T as #frame_system::Config>::BlockNumber
>
>::offchain_worker(n)
}
}
impl<#type_impl_gen>
#frame_support::traits::IntegrityTest
for #pallet_ident<#type_use_gen> #where_clause
{
fn integrity_test() {
<
Self as #frame_support::traits::Hooks<
<T as #frame_system::Config>::BlockNumber
>
>::integrity_test()
}
}
)
}
@@ -0,0 +1,40 @@
// This file is part of Substrate.
// Copyright (C) 2020 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 proc_macro2::Span;
use crate::pallet::Def;
/// * Provide inherent instance to be used by construct_runtime
/// * Provide Instance0 .. Instance16 for instantiable pallet
pub fn expand_instances(def: &mut Def) -> proc_macro2::TokenStream {
let frame_support = &def.frame_support;
let inherent_ident = syn::Ident::new(crate::INHERENT_INSTANCE_NAME, Span::call_site());
let instances = if def.config.has_instance {
(0..16).map(|i| syn::Ident::new(&format!("Instance{}", i), Span::call_site())).collect()
} else {
vec![]
};
quote::quote!(
/// Hidden instance generated to be internally used when module is used without
/// instance.
#[doc(hidden)]
pub type #inherent_ident = ();
#( pub use #frame_support::instances::#instances; )*
)
}
@@ -0,0 +1,81 @@
// This file is part of Substrate.
// Copyright (C) 2020 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.
mod constants;
mod pallet_struct;
mod call;
mod error;
mod event;
mod storage;
mod hooks;
mod store_trait;
mod instances;
mod genesis_build;
mod genesis_config;
mod type_value;
use crate::pallet::Def;
use quote::ToTokens;
/// Merge where clause together, `where` token span is taken from the first not none one.
pub fn merge_where_clauses(clauses: &[&Option<syn::WhereClause>]) -> Option<syn::WhereClause> {
let mut clauses = clauses.iter().filter_map(|f| f.as_ref());
let mut res = clauses.next()?.clone();
for other in clauses {
res.predicates.extend(other.predicates.iter().cloned())
}
Some(res)
}
/// Expand definition, in particular:
/// * add some bounds and variants to type defined,
/// * create some new types,
/// * impl stuff on them.
pub fn expand(mut def: Def) -> proc_macro2::TokenStream {
let constants = constants::expand_constants(&mut def);
let pallet_struct = pallet_struct::expand_pallet_struct(&mut def);
let call = call::expand_call(&mut def);
let error = error::expand_error(&mut def);
let event = event::expand_event(&mut def);
let storages = storage::expand_storages(&mut def);
let instances = instances::expand_instances(&mut def);
let store_trait = store_trait::expand_store_trait(&mut def);
let hooks = hooks::expand_hooks(&mut def);
let genesis_build = genesis_build::expand_genesis_build(&mut def);
let genesis_config = genesis_config::expand_genesis_config(&mut def);
let type_values = type_value::expand_type_values(&mut def);
let new_items = quote::quote!(
#constants
#pallet_struct
#call
#error
#event
#storages
#instances
#store_trait
#hooks
#genesis_build
#genesis_config
#type_values
);
def.item.content.as_mut().expect("This is checked by parsing").1
.push(syn::Item::Verbatim(new_items));
def.item.into_token_stream()
}
@@ -0,0 +1,117 @@
// This file is part of Substrate.
// Copyright (C) 2020 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 crate::pallet::Def;
/// * Add derive trait on Pallet
/// * Implement GetPalletVersion on Pallet
/// * Implement OnGenesis on Pallet
/// * Implement ModuleErrorMetadata on Pallet
/// * declare Module type alias for construct_runtime
pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream {
let frame_support = &def.frame_support;
let frame_system = &def.frame_system;
let type_impl_gen = &def.type_impl_generics();
let type_use_gen = &def.type_use_generics();
let type_decl_gen = &def.type_decl_generics();
let pallet_ident = &def.pallet_struct.pallet;
let config_where_clause = &def.config.where_clause;
let pallet_item = {
let pallet_module_items = &mut def.item.content.as_mut().expect("Checked by def").1;
let item = &mut pallet_module_items[def.pallet_struct.index];
if let syn::Item::Struct(item) = item {
item
} else {
unreachable!("Checked by pallet struct parser")
}
};
pallet_item.attrs.push(syn::parse_quote!(
#[derive(
#frame_support::CloneNoBound,
#frame_support::EqNoBound,
#frame_support::PartialEqNoBound,
#frame_support::RuntimeDebugNoBound,
)]
));
let module_error_metadata = if let Some(error_def) = &def.error {
let error_ident = &error_def.error;
quote::quote!(
impl<#type_impl_gen> #frame_support::error::ModuleErrorMetadata
for #pallet_ident<#type_use_gen>
#config_where_clause
{
fn metadata() -> &'static [#frame_support::error::ErrorMetadata] {
<
#error_ident<#type_use_gen> as #frame_support::error::ModuleErrorMetadata
>::metadata()
}
}
)
} else {
quote::quote!(
impl<#type_impl_gen> #frame_support::error::ModuleErrorMetadata
for #pallet_ident<#type_use_gen>
#config_where_clause
{
fn metadata() -> &'static [#frame_support::error::ErrorMetadata] {
&[]
}
}
)
};
quote::quote!(
#module_error_metadata
/// Type alias to `Pallet`, to be used by `construct_runtime`.
///
/// Generated by `pallet` attribute macro.
pub type Module<#type_decl_gen> = #pallet_ident<#type_use_gen>;
// Implement `GetPalletVersion` for `Pallet`
impl<#type_impl_gen> #frame_support::traits::GetPalletVersion
for #pallet_ident<#type_use_gen>
#config_where_clause
{
fn current_version() -> #frame_support::traits::PalletVersion {
#frame_support::crate_to_pallet_version!()
}
fn storage_version() -> Option<#frame_support::traits::PalletVersion> {
let key = #frame_support::traits::PalletVersion::storage_key::<
<T as #frame_system::Config>::PalletInfo, Self
>().expect("Every active pallet has a name in the runtime; qed");
#frame_support::storage::unhashed::get(&key)
}
}
// Implement `OnGenesis` for `Pallet`
impl<#type_impl_gen> #frame_support::traits::OnGenesis
for #pallet_ident<#type_use_gen>
#config_where_clause
{
fn on_genesis() {
#frame_support::crate_to_pallet_version!()
.put_into_storage::<<T as #frame_system::Config>::PalletInfo, Self>();
}
}
)
}
@@ -0,0 +1,267 @@
// This file is part of Substrate.
// Copyright (C) 2020 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 crate::pallet::Def;
use crate::pallet::parse::storage::{Metadata, QueryKind};
use frame_support_procedural_tools::clean_type_string;
/// Generate the prefix_ident related the the storage.
/// prefix_ident is used for the prefix struct to be given to storage as first generic param.
fn prefix_ident(storage_ident: &syn::Ident) -> syn::Ident {
syn::Ident::new(&format!("_GeneratedPrefixForStorage{}", storage_ident), storage_ident.span())
}
/// * generate StoragePrefix structs (e.g. for a storage `MyStorage` a struct with the name
/// `_GeneratedPrefixForStorage$NameOfStorage` is generated) and implements StorageInstance trait.
/// * replace the first generic `_` by the generated prefix structure
/// * generate metadatas
pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream {
let frame_support = &def.frame_support;
let frame_system = &def.frame_system;
let type_impl_gen = &def.type_impl_generics();
let type_use_gen = &def.type_use_generics();
let pallet_ident = &def.pallet_struct.pallet;
// Replace first arg `_` by the generated prefix structure.
// Add `#[allow(type_alias_bounds)]`
for storage_def in def.storages.iter_mut() {
let item = &mut def.item.content.as_mut().expect("Checked by def").1[storage_def.index];
let typ_item = if let syn::Item::Type(t) = item {
t
} else {
unreachable!("Checked by def");
};
typ_item.attrs.push(syn::parse_quote!(#[allow(type_alias_bounds)]));
let typ_path = if let syn::Type::Path(p) = &mut *typ_item.ty {
p
} else {
unreachable!("Checked by def");
};
let args = if let syn::PathArguments::AngleBracketed(args) =
&mut typ_path.path.segments[0].arguments
{
args
} else {
unreachable!("Checked by def");
};
let prefix_ident = prefix_ident(&storage_def.ident);
args.args[0] = syn::parse_quote!( #prefix_ident<#type_use_gen> );
}
let entries = def.storages.iter()
.map(|storage| {
let docs = &storage.docs;
let ident = &storage.ident;
let gen = &def.type_use_generics();
let full_ident = quote::quote!( #ident<#gen> );
let metadata_trait = match &storage.metadata {
Metadata::Value { .. } =>
quote::quote!(#frame_support::storage::types::StorageValueMetadata),
Metadata::Map { .. } =>
quote::quote!(#frame_support::storage::types::StorageMapMetadata),
Metadata::DoubleMap { .. } =>
quote::quote!(#frame_support::storage::types::StorageDoubleMapMetadata),
};
let ty = match &storage.metadata {
Metadata::Value { value } => {
let value = clean_type_string(&quote::quote!(#value).to_string());
quote::quote!(
#frame_support::metadata::StorageEntryType::Plain(
#frame_support::metadata::DecodeDifferent::Encode(#value)
)
)
},
Metadata::Map { key, value } => {
let value = clean_type_string(&quote::quote!(#value).to_string());
let key = clean_type_string(&quote::quote!(#key).to_string());
quote::quote!(
#frame_support::metadata::StorageEntryType::Map {
hasher: <#full_ident as #metadata_trait>::HASHER,
key: #frame_support::metadata::DecodeDifferent::Encode(#key),
value: #frame_support::metadata::DecodeDifferent::Encode(#value),
unused: false,
}
)
},
Metadata::DoubleMap { key1, key2, value } => {
let value = clean_type_string(&quote::quote!(#value).to_string());
let key1 = clean_type_string(&quote::quote!(#key1).to_string());
let key2 = clean_type_string(&quote::quote!(#key2).to_string());
quote::quote!(
#frame_support::metadata::StorageEntryType::DoubleMap {
hasher: <#full_ident as #metadata_trait>::HASHER1,
key2_hasher: <#full_ident as #metadata_trait>::HASHER2,
key1: #frame_support::metadata::DecodeDifferent::Encode(#key1),
key2: #frame_support::metadata::DecodeDifferent::Encode(#key2),
value: #frame_support::metadata::DecodeDifferent::Encode(#value),
}
)
}
};
quote::quote_spanned!(storage.ident.span() =>
#frame_support::metadata::StorageEntryMetadata {
name: #frame_support::metadata::DecodeDifferent::Encode(
<#full_ident as #metadata_trait>::NAME
),
modifier: <#full_ident as #metadata_trait>::MODIFIER,
ty: #ty,
default: #frame_support::metadata::DecodeDifferent::Encode(
<#full_ident as #metadata_trait>::DEFAULT
),
documentation: #frame_support::metadata::DecodeDifferent::Encode(&[
#( #docs, )*
]),
}
)
});
let getters = def.storages.iter()
.map(|storage| if let Some(getter) = &storage.getter {
let completed_where_clause = super::merge_where_clauses(&[
&storage.where_clause,
&def.config.where_clause,
]);
let docs = storage.docs.iter().map(|d| quote::quote!(#[doc = #d]));
let ident = &storage.ident;
let gen = &def.type_use_generics();
let full_ident = quote::quote!( #ident<#gen> );
match &storage.metadata {
Metadata::Value { value } => {
let query = match storage.query_kind.as_ref().expect("Checked by def") {
QueryKind::OptionQuery => quote::quote!(Option<#value>),
QueryKind::ValueQuery => quote::quote!(#value),
};
quote::quote_spanned!(getter.span() =>
impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause {
#( #docs )*
pub fn #getter() -> #query {
<
#full_ident as #frame_support::storage::StorageValue<#value>
>::get()
}
}
)
},
Metadata::Map { key, value } => {
let query = match storage.query_kind.as_ref().expect("Checked by def") {
QueryKind::OptionQuery => quote::quote!(Option<#value>),
QueryKind::ValueQuery => quote::quote!(#value),
};
quote::quote_spanned!(getter.span() =>
impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause {
#( #docs )*
pub fn #getter<KArg>(k: KArg) -> #query where
KArg: #frame_support::codec::EncodeLike<#key>,
{
<
#full_ident as #frame_support::storage::StorageMap<#key, #value>
>::get(k)
}
}
)
},
Metadata::DoubleMap { key1, key2, value } => {
let query = match storage.query_kind.as_ref().expect("Checked by def") {
QueryKind::OptionQuery => quote::quote!(Option<#value>),
QueryKind::ValueQuery => quote::quote!(#value),
};
quote::quote_spanned!(getter.span() =>
impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause {
#( #docs )*
pub fn #getter<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> #query where
KArg1: #frame_support::codec::EncodeLike<#key1>,
KArg2: #frame_support::codec::EncodeLike<#key2>,
{
<
#full_ident as
#frame_support::storage::StorageDoubleMap<#key1, #key2, #value>
>::get(k1, k2)
}
}
)
},
}
} else {
Default::default()
});
let prefix_structs = def.storages.iter().map(|storage_def| {
let prefix_struct_ident = prefix_ident(&storage_def.ident);
let prefix_struct_vis = &storage_def.vis;
let prefix_struct_const = storage_def.ident.to_string();
let config_where_clause = &def.config.where_clause;
quote::quote_spanned!(storage_def.ident.span() =>
#prefix_struct_vis struct #prefix_struct_ident<#type_use_gen>(
core::marker::PhantomData<(#type_use_gen,)>
);
impl<#type_impl_gen> #frame_support::traits::StorageInstance
for #prefix_struct_ident<#type_use_gen>
#config_where_clause
{
fn pallet_prefix() -> &'static str {
<
<T as #frame_system::Config>::PalletInfo
as #frame_support::traits::PalletInfo
>::name::<Pallet<#type_use_gen>>()
.expect("Every active pallet has a name in the runtime; qed")
}
const STORAGE_PREFIX: &'static str = #prefix_struct_const;
}
)
});
let mut where_clauses = vec![&def.config.where_clause];
where_clauses.extend(def.storages.iter().map(|storage| &storage.where_clause));
let completed_where_clause = super::merge_where_clauses(&where_clauses);
quote::quote!(
impl<#type_impl_gen> #pallet_ident<#type_use_gen>
#completed_where_clause
{
#[doc(hidden)]
pub fn storage_metadata() -> #frame_support::metadata::StorageMetadata {
#frame_support::metadata::StorageMetadata {
prefix: #frame_support::metadata::DecodeDifferent::Encode(
<
<T as #frame_system::Config>::PalletInfo as
#frame_support::traits::PalletInfo
>::name::<#pallet_ident<#type_use_gen>>()
.expect("Every active pallet has a name in the runtime; qed")
),
entries: #frame_support::metadata::DecodeDifferent::Encode(
&[ #( #entries, )* ]
),
}
}
}
#( #getters )*
#( #prefix_structs )*
)
}
@@ -0,0 +1,55 @@
// This file is part of Substrate.
// Copyright (C) 2020 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 crate::pallet::Def;
use syn::spanned::Spanned;
/// If attribute `#[pallet::generate_store(..)]` is defined then:
/// * generate Store trait with all storages,
/// * implement Store trait for Pallet.
pub fn expand_store_trait(def: &mut Def) -> proc_macro2::TokenStream {
let (trait_vis, trait_store) = if let Some(store) = &def.pallet_struct.store {
store
} else {
return Default::default()
};
let type_impl_gen = &def.type_impl_generics();
let type_use_gen = &def.type_use_generics();
let pallet_ident = &def.pallet_struct.pallet;
let mut where_clauses = vec![&def.config.where_clause];
where_clauses.extend(def.storages.iter().map(|storage| &storage.where_clause));
let completed_where_clause = super::merge_where_clauses(&where_clauses);
let storage_names = &def.storages.iter().map(|storage| &storage.ident).collect::<Vec<_>>();
quote::quote_spanned!(trait_store.span() =>
#trait_vis trait #trait_store {
#(
type #storage_names;
)*
}
impl<#type_impl_gen> #trait_store for #pallet_ident<#type_use_gen>
#completed_where_clause
{
#(
type #storage_names = #storage_names<#type_use_gen>;
)*
}
)
}
@@ -0,0 +1,55 @@
// This file is part of Substrate.
// Copyright (C) 2020 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 crate::pallet::Def;
use syn::spanned::Spanned;
/// * Generate the struct
/// * implement the `Get<..>` on it
pub fn expand_type_values(def: &mut Def) -> proc_macro2::TokenStream {
let mut expand = quote::quote!();
let frame_support = &def.frame_support;
for type_value in &def.type_values {
// Remove item from module content
let item = &mut def.item.content.as_mut().expect("Checked by def").1[type_value.index];
let span = item.span();
*item = syn::Item::Verbatim(Default::default());
let vis = &type_value.vis;
let ident = &type_value.ident;
let block = &type_value.block;
let type_ = &type_value.type_;
let where_clause = &type_value.where_clause;
let (struct_impl_gen, struct_use_gen) = if type_value.is_generic {
(def.type_impl_generics(), def.type_use_generics())
} else {
(Default::default(), Default::default())
};
expand.extend(quote::quote_spanned!(span =>
#vis struct #ident<#struct_use_gen>(core::marker::PhantomData<((), #struct_use_gen)>);
impl<#struct_impl_gen> #frame_support::traits::Get<#type_> for #ident<#struct_use_gen>
#where_clause
{
fn get() -> #type_ #block
}
));
}
expand
}
@@ -0,0 +1,50 @@
// This file is part of Substrate.
// Copyright (C) 2020 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 for pallet attribute macro.
//!
//! General workflow:
//! 1 - parse all pallet attributes:
//! This step remove all attributes `#[pallet::*]` from the ItemMod and build the `Def` struct
//! which holds the ItemMod without `#[pallet::*]` and information given by those attributes
//! 2 - expand from the parsed information
//! This step will modify the ItemMod by adding some derive attributes or phantom data variants
//! to user defined types. And also crate new types and implement block.
mod parse;
mod expand;
pub use parse::Def;
use syn::spanned::Spanned;
pub fn pallet(
attr: proc_macro::TokenStream,
item: proc_macro::TokenStream
) -> proc_macro::TokenStream {
if !attr.is_empty() {
let msg = "Invalid pallet macro call: expected no attributes, e.g. macro call must be just \
`#[frame_support::pallet]` or `#[pallet]`";
let span = proc_macro2::TokenStream::from(attr).span();
return syn::Error::new(span, msg).to_compile_error().into();
}
let item = syn::parse_macro_input!(item as syn::ItemMod);
match parse::Def::try_from(item) {
Ok(def) => expand::expand(def).into(),
Err(e) => e.to_compile_error().into(),
}
}
@@ -0,0 +1,235 @@
// This file is part of Substrate.
// Copyright (C) 2020 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 super::helper;
use quote::ToTokens;
use syn::spanned::Spanned;
/// List of additional token to be used for parsing.
mod keyword {
syn::custom_keyword!(DispatchResultWithPostInfo);
syn::custom_keyword!(Call);
syn::custom_keyword!(OriginFor);
syn::custom_keyword!(weight);
syn::custom_keyword!(compact);
syn::custom_keyword!(T);
syn::custom_keyword!(pallet);
}
/// Definition of dispatchables typically `impl<T: Config> Pallet<T> { ... }`
pub struct CallDef {
/// The where_clause used.
pub where_clause: Option<syn::WhereClause>,
/// A set of usage of instance, must be check for consistency with trait.
pub instances: Vec<helper::InstanceUsage>,
/// The index of call item in pallet module.
pub index: usize,
/// Information on methods (used for expansion).
pub methods: Vec<CallVariantDef>,
/// The span of the attribute.
pub attr_span: proc_macro2::Span,
}
/// Definition of dispatchable typically: `#[weight...] fn foo(origin .., param1: ...) -> ..`
pub struct CallVariantDef {
/// Function name.
pub name: syn::Ident,
/// Information on args: `(is_compact, name, type)`
pub args: Vec<(bool, syn::Ident, Box<syn::Type>)>,
/// Weight formula.
pub weight: syn::Expr,
/// Docs, used for metadata.
pub docs: Vec<syn::Lit>,
}
/// Attributes for functions in call impl block.
/// Parse for `#[pallet::weight = expr]`
pub struct FunctionAttr {
weight: syn::Expr,
}
impl syn::parse::Parse for FunctionAttr {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
input.parse::<syn::Token![#]>()?;
let content;
syn::bracketed!(content in input);
content.parse::<keyword::pallet>()?;
content.parse::<syn::Token![::]>()?;
content.parse::<keyword::weight>()?;
let weight_content;
syn::parenthesized!(weight_content in content);
Ok(FunctionAttr {
weight: weight_content.parse::<syn::Expr>()?,
})
}
}
/// Attribute for arguments in function in call impl block.
/// Parse for `#[pallet::compact]|
pub struct ArgAttrIsCompact;
impl syn::parse::Parse for ArgAttrIsCompact {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
input.parse::<syn::Token![#]>()?;
let content;
syn::bracketed!(content in input);
content.parse::<keyword::pallet>()?;
content.parse::<syn::Token![::]>()?;
content.parse::<keyword::compact>()?;
Ok(ArgAttrIsCompact)
}
}
/// Check the syntax is `OriginFor<T>`
pub fn check_dispatchable_first_arg_type(ty: &syn::Type) -> syn::Result<()> {
pub struct CheckDispatchableFirstArg;
impl syn::parse::Parse for CheckDispatchableFirstArg {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
input.parse::<keyword::OriginFor>()?;
input.parse::<syn::Token![<]>()?;
input.parse::<keyword::T>()?;
input.parse::<syn::Token![>]>()?;
Ok(Self)
}
}
syn::parse2::<CheckDispatchableFirstArg>(ty.to_token_stream())
.map_err(|e| {
let msg = "Invalid type: expected `OriginFor<T>`";
let mut err = syn::Error::new(ty.span(), msg);
err.combine(e);
err
})?;
Ok(())
}
impl CallDef {
pub fn try_from(
// Span needed for expansion
attr_span: proc_macro2::Span,
index: usize,
item: &mut syn::Item
) -> syn::Result<Self> {
let item = if let syn::Item::Impl(item) = item {
item
} else {
return Err(syn::Error::new(item.span(), "Invalid pallet::call, expected item impl"));
};
let mut instances = vec![];
instances.push(helper::check_impl_gen(&item.generics, item.impl_token.span())?);
instances.push(helper::check_pallet_struct_usage(&item.self_ty)?);
if let Some((_, _, for_)) = item.trait_ {
let msg = "Invalid pallet::call, expected no trait ident as in \
`impl<..> Pallet<..> { .. }`";
return Err(syn::Error::new(for_.span(), msg))
}
let mut methods = vec![];
for impl_item in &mut item.items {
if let syn::ImplItem::Method(method) = impl_item {
match method.sig.inputs.first() {
None => {
let msg = "Invalid pallet::call, must have at least origin arg";
return Err(syn::Error::new(method.sig.span(), msg));
},
Some(syn::FnArg::Receiver(_)) => {
let msg = "Invalid pallet::call, first argument must be a typed argument, \
e.g. `origin: OriginFor<T>`";
return Err(syn::Error::new(method.sig.span(), msg));
},
Some(syn::FnArg::Typed(arg)) => {
check_dispatchable_first_arg_type(&*arg.ty)?;
},
}
if let syn::ReturnType::Type(_, type_) = &method.sig.output {
syn::parse2::<keyword::DispatchResultWithPostInfo>(type_.to_token_stream())?;
} else {
let msg = "Invalid pallet::call, require return type \
DispatchResultWithPostInfo";
return Err(syn::Error::new(method.sig.span(), msg));
}
let mut call_var_attrs: Vec<FunctionAttr> =
helper::take_item_attrs(&mut method.attrs)?;
if call_var_attrs.len() != 1 {
let msg = if call_var_attrs.len() == 0 {
"Invalid pallet::call, require weight attribute i.e. `#[pallet::weight = $expr]`"
} else {
"Invalid pallet::call, too many weight attributes given"
};
return Err(syn::Error::new(method.sig.span(), msg));
}
let weight = call_var_attrs.pop().unwrap().weight;
let mut args = vec![];
for arg in method.sig.inputs.iter_mut().skip(1) {
let arg = if let syn::FnArg::Typed(arg) = arg {
arg
} else {
unreachable!("Only first argument can be receiver");
};
let arg_attrs: Vec<ArgAttrIsCompact> =
helper::take_item_attrs(&mut arg.attrs)?;
if arg_attrs.len() > 1 {
let msg = "Invalid pallet::call, argument has too many attributes";
return Err(syn::Error::new(arg.span(), msg));
}
let arg_ident = if let syn::Pat::Ident(pat) = &*arg.pat {
pat.ident.clone()
} else {
let msg = "Invalid pallet::call, argument must be ident";
return Err(syn::Error::new(arg.pat.span(), msg));
};
args.push((!arg_attrs.is_empty(), arg_ident, arg.ty.clone()));
}
let docs = helper::get_doc_literals(&method.attrs);
methods.push(CallVariantDef {
name: method.sig.ident.clone(),
weight,
args,
docs,
});
} else {
let msg = "Invalid pallet::call, only method accepted";
return Err(syn::Error::new(impl_item.span(), msg));
}
}
Ok(Self {
index,
attr_span,
instances,
methods,
where_clause: item.generics.where_clause.clone(),
})
}
}
@@ -0,0 +1,384 @@
// This file is part of Substrate.
// Copyright (C) 2020 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 super::helper;
use syn::spanned::Spanned;
use quote::ToTokens;
/// List of additional token to be used for parsing.
mod keyword {
syn::custom_keyword!(Config);
syn::custom_keyword!(From);
syn::custom_keyword!(T);
syn::custom_keyword!(I);
syn::custom_keyword!(Get);
syn::custom_keyword!(config);
syn::custom_keyword!(IsType);
syn::custom_keyword!(Event);
syn::custom_keyword!(constant);
syn::custom_keyword!(frame_system);
syn::custom_keyword!(disable_frame_system_supertrait_check);
}
/// Input definition for the pallet config.
pub struct ConfigDef {
/// The index of item in pallet module.
pub index: usize,
/// Whether the trait has instance (i.e. define with `Config<I = ()>`)
pub has_instance: bool,
/// Const associated type.
pub consts_metadata: Vec<ConstMetadataDef>,
/// Whether the trait has the associated type `Event`, note that those bounds are checked:
/// * `IsType<Self as frame_system::Config>::Event`
/// * `From<Event>` or `From<Event<T>>` or `From<Event<T, I>>`
pub has_event_type: bool,
/// The where clause on trait definition but modified so `Self` is `T`.
pub where_clause: Option<syn::WhereClause>,
}
/// Input definition for a constant in pallet config.
pub struct ConstMetadataDef {
/// Name of the associated type.
pub ident: syn::Ident,
/// The type in Get, e.g. `u32` in `type Foo: Get<u32>;`, but `Self` is replaced by `T`
pub type_: syn::Type,
/// The doc associated
pub doc: Vec<syn::Lit>,
}
impl syn::parse::Parse for ConstMetadataDef {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let doc = helper::get_doc_literals(&syn::Attribute::parse_outer(input)?);
input.parse::<syn::Token![type]>()?;
let ident = input.parse::<syn::Ident>()?;
input.parse::<syn::Token![:]>()?;
input.parse::<keyword::Get>()?;
input.parse::<syn::Token![<]>()?;
let mut type_ = input.parse::<syn::Type>()?;
type_ = syn::parse2::<syn::Type>(replace_self_by_t(type_.to_token_stream()))
.expect("Internal error: replacing `Self` by `T` should result in valid type");
input.parse::<syn::Token![>]>()?;
input.parse::<syn::Token![;]>()?;
Ok(Self { ident, type_, doc })
}
}
/// Parse for `#[pallet::disable_frame_system_supertrait_check]`
pub struct DisableFrameSystemSupertraitCheck;
impl syn::parse::Parse for DisableFrameSystemSupertraitCheck {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
input.parse::<syn::Token![#]>()?;
let content;
syn::bracketed!(content in input);
content.parse::<syn::Ident>()?;
content.parse::<syn::Token![::]>()?;
content.parse::<keyword::disable_frame_system_supertrait_check>()?;
Ok(Self)
}
}
/// Parse for `#[pallet::constant]`
pub struct TypeAttrConst(proc_macro2::Span);
impl Spanned for TypeAttrConst {
fn span(&self) -> proc_macro2::Span {
self.0
}
}
impl syn::parse::Parse for TypeAttrConst {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
input.parse::<syn::Token![#]>()?;
let content;
syn::bracketed!(content in input);
content.parse::<syn::Ident>()?;
content.parse::<syn::Token![::]>()?;
Ok(TypeAttrConst(content.parse::<keyword::constant>()?.span()))
}
}
/// Parse for `$ident::Config`
pub struct ConfigBoundParse(syn::Ident);
impl syn::parse::Parse for ConfigBoundParse {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let ident = input.parse::<syn::Ident>()?;
input.parse::<syn::Token![::]>()?;
input.parse::<keyword::Config>()?;
Ok(Self(ident))
}
}
/// Parse for `IsType<<Sef as $ident::Config>::Event>` and retrieve `$ident`
pub struct IsTypeBoundEventParse(syn::Ident);
impl syn::parse::Parse for IsTypeBoundEventParse {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
input.parse::<keyword::IsType>()?;
input.parse::<syn::Token![<]>()?;
input.parse::<syn::Token![<]>()?;
input.parse::<syn::Token![Self]>()?;
input.parse::<syn::Token![as]>()?;
let ident = input.parse::<syn::Ident>()?;
input.parse::<syn::Token![::]>()?;
input.parse::<keyword::Config>()?;
input.parse::<syn::Token![>]>()?;
input.parse::<syn::Token![::]>()?;
input.parse::<keyword::Event>()?;
input.parse::<syn::Token![>]>()?;
Ok(Self(ident))
}
}
/// Parse for `From<Event>` or `From<Event<Self>>` or `From<Event<Self, I>>`
pub struct FromEventParse {
is_generic: bool,
has_instance: bool,
}
impl syn::parse::Parse for FromEventParse {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let mut is_generic = false;
let mut has_instance = false;
input.parse::<keyword::From>()?;
input.parse::<syn::Token![<]>()?;
input.parse::<keyword::Event>()?;
if input.peek(syn::Token![<]) {
is_generic = true;
input.parse::<syn::Token![<]>()?;
input.parse::<syn::Token![Self]>()?;
if input.peek(syn::Token![,]) {
input.parse::<syn::Token![,]>()?;
input.parse::<keyword::I>()?;
has_instance = true;
}
input.parse::<syn::Token![>]>()?;
}
input.parse::<syn::Token![>]>()?;
Ok(Self { is_generic, has_instance })
}
}
/// Check if trait_item is `type Event`, if so checks its bounds are those expected.
/// (Event type is reserved type)
fn check_event_type(
frame_system: &syn::Ident,
trait_item: &syn::TraitItem,
trait_has_instance: bool
) -> syn::Result<bool> {
if let syn::TraitItem::Type(type_) = trait_item {
if type_.ident == "Event" {
// Check event has no generics
if !type_.generics.params.is_empty() || type_.generics.where_clause.is_some() {
let msg = "Invalid `type Event`, associated type `Event` is reserved and must have\
no generics nor where_clause";
return Err(syn::Error::new(trait_item.span(), msg));
}
// Check bound contains IsType and From
let has_is_type_bound = type_.bounds.iter().any(|s| {
syn::parse2::<IsTypeBoundEventParse>(s.to_token_stream())
.map_or(false, |b| b.0 == *frame_system)
});
if !has_is_type_bound {
let msg = format!(
"Invalid `type Event`, associated type `Event` is reserved and must \
bound: `IsType<<Self as {}::Config>::Event>`",
frame_system,
);
return Err(syn::Error::new(type_.span(), msg));
}
let from_event_bound = type_.bounds.iter().find_map(|s| {
syn::parse2::<FromEventParse>(s.to_token_stream()).ok()
});
let from_event_bound = if let Some(b) = from_event_bound {
b
} else {
let msg = "Invalid `type Event`, associated type `Event` is reserved and must \
bound: `From<Event>` or `From<Event<Self>>` or `From<Event<Self, I>>`";
return Err(syn::Error::new(type_.span(), msg));
};
if from_event_bound.is_generic
&& (from_event_bound.has_instance != trait_has_instance)
{
let msg = "Invalid `type Event`, associated type `Event` bounds inconsistent \
`From<Event..>`. Config and generic Event must be both with instance or \
without instance";
return Err(syn::Error::new(type_.span(), msg));
}
Ok(true)
} else {
Ok(false)
}
} else {
Ok(false)
}
}
/// Replace ident `Self` by `T`
pub fn replace_self_by_t(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
input.into_iter()
.map(|token_tree| match token_tree {
proc_macro2::TokenTree::Group(group) =>
proc_macro2::Group::new(
group.delimiter(),
replace_self_by_t(group.stream())
).into(),
proc_macro2::TokenTree::Ident(ident) if ident == "Self" =>
proc_macro2::Ident::new("T", ident.span()).into(),
other @ _ => other
})
.collect()
}
impl ConfigDef {
pub fn try_from(
frame_system: &syn::Ident,
index: usize,
item: &mut syn::Item
) -> syn::Result<Self> {
let item = if let syn::Item::Trait(item) = item {
item
} else {
let msg = "Invalid pallet::config, expected trait definition";
return Err(syn::Error::new(item.span(), msg));
};
if !matches!(item.vis, syn::Visibility::Public(_)) {
let msg = "Invalid pallet::config, trait must be public";
return Err(syn::Error::new(item.span(), msg));
}
syn::parse2::<keyword::Config>(item.ident.to_token_stream())?;
let where_clause = {
let stream = replace_self_by_t(item.generics.where_clause.to_token_stream());
syn::parse2::<Option<syn::WhereClause>>(stream)
.expect("Internal error: replacing `Self` by `T` should result in valid where
clause")
};
if item.generics.params.len() > 1 {
let msg = "Invalid pallet::config, expected no more than one generic";
return Err(syn::Error::new(item.generics.params[2].span(), msg));
}
let has_instance = if let Some(_) = item.generics.params.first() {
helper::check_config_def_gen(&item.generics, item.ident.span())?;
true
} else {
false
};
let mut has_event_type = false;
let mut consts_metadata = vec![];
for trait_item in &mut item.items {
// Parse for event
has_event_type = has_event_type
|| check_event_type(frame_system, trait_item, has_instance)?;
// Parse for constant
let type_attrs_const: Vec<TypeAttrConst> = helper::take_item_attrs(trait_item)?;
if type_attrs_const.len() > 1 {
let msg = "Invalid attribute in pallet::config, only one attribute is expected";
return Err(syn::Error::new(type_attrs_const[1].span(), msg));
}
if type_attrs_const.len() == 1 {
match trait_item {
syn::TraitItem::Type(type_) => {
let constant = syn::parse2::<ConstMetadataDef>(type_.to_token_stream())
.map_err(|e| {
let error_msg = "Invalid usage of `#[pallet::constant]`, syntax \
must be `type $SomeIdent: Get<$SomeType>;`";
let mut err = syn::Error::new(type_.span(), error_msg);
err.combine(e);
err
})?;
consts_metadata.push(constant);
},
_ => {
let msg = "Invalid pallet::constant in pallet::config, expected type trait \
item";
return Err(syn::Error::new(trait_item.span(), msg));
},
}
}
}
let attr: Option<DisableFrameSystemSupertraitCheck> = helper::take_first_item_attr(
&mut item.attrs
)?;
let disable_system_supertrait_check = attr.is_some();
let has_frame_system_supertrait = item.supertraits.iter().any(|s| {
syn::parse2::<ConfigBoundParse>(s.to_token_stream())
.map_or(false, |b| b.0 == *frame_system)
});
if !has_frame_system_supertrait && !disable_system_supertrait_check {
let found = if item.supertraits.is_empty() {
"none".to_string()
} else {
let mut found = item.supertraits.iter()
.fold(String::new(), |acc, s| {
format!("{}`{}`, ", acc, quote::quote!(#s).to_string())
});
found.pop();
found.pop();
found
};
let msg = format!(
"Invalid pallet::trait, expected explicit `{}::Config` as supertrait, \
found {}. \
(try `pub trait Config: frame_system::Config {{ ...` or \
`pub trait Config<I: 'static>: frame_system::Config {{ ...`). \
To disable this check, use `#[pallet::disable_frame_system_supertrait_check]`",
frame_system,
found,
);
return Err(syn::Error::new(item.span(), msg));
}
Ok(Self {
index,
has_instance,
consts_metadata,
has_event_type,
where_clause,
})
}
}
@@ -0,0 +1,86 @@
// This file is part of Substrate.
// Copyright (C) 2020 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 super::helper;
use syn::spanned::Spanned;
use quote::ToTokens;
/// List of additional token to be used for parsing.
mod keyword {
syn::custom_keyword!(Error);
}
/// 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>)>,
/// A set of usage of instance, must be check for consistency with trait.
pub instances: Vec<helper::InstanceUsage>,
/// The keyword error used (contains span).
pub error: keyword::Error
}
impl ErrorDef {
pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result<Self> {
let item = if let syn::Item::Enum(item) = item {
item
} else {
return Err(syn::Error::new(item.span(), "Invalid pallet::error, expected item enum"));
};
if !matches!(item.vis, syn::Visibility::Public(_)) {
let msg = "Invalid pallet::error, `Error` must be public";
return Err(syn::Error::new(item.span(), msg));
}
let mut instances = vec![];
instances.push(helper::check_type_def_gen_no_bounds(&item.generics, item.ident.span())?);
if item.generics.where_clause.is_some() {
let msg = "Invalid pallet::error, unexpected where clause";
return Err(syn::Error::new(item.generics.where_clause.as_ref().unwrap().span(), msg));
}
let error = syn::parse2::<keyword::Error>(item.ident.to_token_stream())?;
let variants = item.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));
}
if variant.discriminant.is_some() {
let msg = "Invalid pallet::error, unexpected discriminant, discriminant \
are not supported";
let span = variant.discriminant.as_ref().unwrap().0.span();
return Err(syn::Error::new(span, msg));
}
Ok((variant.ident.clone(), helper::get_doc_literals(&variant.attrs)))
})
.collect::<Result<_, _>>()?;
Ok(ErrorDef {
index,
variants,
instances,
error,
})
}
}
@@ -0,0 +1,220 @@
// This file is part of Substrate.
// Copyright (C) 2020 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 super::helper;
use syn::spanned::Spanned;
use quote::ToTokens;
use frame_support_procedural_tools::clean_type_string;
/// List of additional token to be used for parsing.
mod keyword {
syn::custom_keyword!(metadata);
syn::custom_keyword!(Event);
syn::custom_keyword!(pallet);
syn::custom_keyword!(generate_deposit);
syn::custom_keyword!(deposit_event);
}
/// Definition for pallet event enum.
pub struct EventDef {
/// The index of event item in pallet module.
pub index: usize,
/// The keyword Event used (contains span).
pub event: keyword::Event,
/// Event metadatas: `(name, args, docs)`.
pub metadata: Vec<(syn::Ident, Vec<String>, Vec<syn::Lit>)>,
/// A set of usage of instance, must be check for consistency with trait.
pub instances: Vec<helper::InstanceUsage>,
/// The kind of generic the type `Event` has.
pub gen_kind: super::GenericKind,
/// Whether the function `deposit_event` must be generated.
pub deposit_event: Option<(syn::Visibility, proc_macro2::Span)>,
/// Where clause used in event definition.
pub where_clause: Option<syn::WhereClause>,
}
/// Attribute for Event: defines metadata name to use.
///
/// Syntax is:
/// * `#[pallet::metadata(SomeType = MetadataName, ...)]`
/// * `#[pallet::generate_deposit($vis fn deposit_event)]`
enum PalletEventAttr {
Metadata {
metadata: Vec<(syn::Type, String)>,
// Span of the attribute
span: proc_macro2::Span,
},
DepositEvent {
fn_vis: syn::Visibility,
// Span for the keyword deposit_event
fn_span: proc_macro2::Span,
// Span of the attribute
span: proc_macro2::Span,
},
}
impl PalletEventAttr {
fn span(&self) -> proc_macro2::Span {
match self {
Self::Metadata { span, .. } => span.clone(),
Self::DepositEvent { span, .. } => span.clone(),
}
}
}
/// Parse for syntax `$Type = "$SomeString"`.
fn parse_event_metadata_element(
input: syn::parse::ParseStream
) -> syn::Result<(syn::Type, String)> {
let typ = input.parse::<syn::Type>()?;
input.parse::<syn::Token![=]>()?;
let ident = input.parse::<syn::LitStr>()?;
Ok((typ, ident.value()))
}
impl syn::parse::Parse for PalletEventAttr {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
input.parse::<syn::Token![#]>()?;
let content;
syn::bracketed!(content in input);
content.parse::<keyword::pallet>()?;
content.parse::<syn::Token![::]>()?;
let lookahead = content.lookahead1();
if lookahead.peek(keyword::metadata) {
let span = content.parse::<keyword::metadata>()?.span();
let metadata_content;
syn::parenthesized!(metadata_content in content);
let metadata = metadata_content
.parse_terminated::<_, syn::Token![,]>(parse_event_metadata_element)?
.into_pairs()
.map(syn::punctuated::Pair::into_value)
.collect();
Ok(PalletEventAttr::Metadata { metadata, span })
} else if lookahead.peek(keyword::generate_deposit) {
let span = content.parse::<keyword::generate_deposit>()?.span();
let generate_content;
syn::parenthesized!(generate_content in content);
let fn_vis = generate_content.parse::<syn::Visibility>()?;
generate_content.parse::<syn::Token![fn]>()?;
let fn_span = generate_content.parse::<keyword::deposit_event>()?.span();
Ok(PalletEventAttr::DepositEvent { fn_vis, span, fn_span })
} else {
Err(lookahead.error())
}
}
}
struct PalletEventAttrInfo {
metadata: Option<Vec<(syn::Type, String)>>,
deposit_event: Option<(syn::Visibility, proc_macro2::Span)>,
}
impl PalletEventAttrInfo {
fn from_attrs(attrs: Vec<PalletEventAttr>) -> syn::Result<Self> {
let mut metadata = None;
let mut deposit_event = None;
for attr in attrs {
match attr {
PalletEventAttr::Metadata { metadata: m, .. } if metadata.is_none() =>
metadata = Some(m),
PalletEventAttr::DepositEvent { fn_vis, fn_span, .. } if deposit_event.is_none() =>
deposit_event = Some((fn_vis, fn_span)),
attr => {
return Err(syn::Error::new(attr.span(), "Duplicate attribute"));
}
}
}
Ok(PalletEventAttrInfo { metadata, deposit_event })
}
}
impl EventDef {
pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result<Self> {
let item = if let syn::Item::Enum(item) = item {
item
} else {
return Err(syn::Error::new(item.span(), "Invalid pallet::event, expected item enum"))
};
let event_attrs: Vec<PalletEventAttr> = helper::take_item_attrs(&mut item.attrs)?;
let attr_info = PalletEventAttrInfo::from_attrs(event_attrs)?;
let metadata = attr_info.metadata.unwrap_or_else(|| vec![]);
let deposit_event = attr_info.deposit_event;
if !matches!(item.vis, syn::Visibility::Public(_)) {
let msg = "Invalid pallet::event, `Error` must be public";
return Err(syn::Error::new(item.span(), msg));
}
let where_clause = item.generics.where_clause.clone();
let mut instances = vec![];
// NOTE: Event is not allowed to be only generic on I because it is not supported
// by construct_runtime.
if let Some(u) = helper::check_type_def_optional_gen(&item.generics, item.ident.span())? {
instances.push(u);
} else {
// construct_runtime only allow non generic event for non instantiable pallet.
instances.push(helper::InstanceUsage {
has_instance: false,
span: item.ident.span(),
})
}
let has_instance = item.generics.type_params().any(|t| t.ident == "I");
let has_config = item.generics.type_params().any(|t| t.ident == "T");
let gen_kind = super::GenericKind::from_gens(has_config, has_instance)
.expect("Checked by `helper::check_type_def_optional_gen` above");
let event = syn::parse2::<keyword::Event>(item.ident.to_token_stream())?;
let metadata = item.variants.iter()
.map(|variant| {
let name = variant.ident.clone();
let docs = helper::get_doc_literals(&variant.attrs);
let args = variant.fields.iter()
.map(|field| {
metadata.iter().find(|m| m.0 == field.ty)
.map(|m| m.1.clone())
.unwrap_or_else(|| {
clean_type_string(&field.ty.to_token_stream().to_string())
})
})
.collect();
(name, args, docs)
})
.collect();
Ok(EventDef {
index,
metadata,
instances,
deposit_event,
event,
gen_kind,
where_clause,
})
}
}
@@ -0,0 +1,121 @@
// This file is part of Substrate.
// Copyright (C) 2020 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 super::helper;
use syn::spanned::Spanned;
/// List of additional token to be used for parsing.
mod keyword {
syn::custom_keyword!(DispatchResultWithPostInfo);
syn::custom_keyword!(Call);
syn::custom_keyword!(OriginFor);
syn::custom_keyword!(weight);
syn::custom_keyword!(compact);
syn::custom_keyword!(T);
syn::custom_keyword!(pallet);
}
/// Definition of extra constants typically `impl<T: Config> Pallet<T> { ... }`
pub struct ExtraConstantsDef {
/// The where_clause used.
pub where_clause: Option<syn::WhereClause>,
/// A set of usage of instance, must be check for consistency with trait.
pub instances: Vec<helper::InstanceUsage>,
/// The index of call item in pallet module.
pub index: usize,
/// The extra constant defined.
pub extra_constants: Vec<ExtraConstantDef>,
}
/// Input definition for an constant in pallet.
pub struct ExtraConstantDef {
/// Name of the function
pub ident: syn::Ident,
/// The type returned by the function
pub type_: syn::Type,
/// The doc associated
pub doc: Vec<syn::Lit>,
}
impl ExtraConstantsDef {
pub fn try_from(
index: usize,
item: &mut syn::Item
) -> syn::Result<Self> {
let item = if let syn::Item::Impl(item) = item {
item
} else {
return Err(syn::Error::new(item.span(), "Invalid pallet::call, expected item impl"));
};
let mut instances = vec![];
instances.push(helper::check_impl_gen(&item.generics, item.impl_token.span())?);
instances.push(helper::check_pallet_struct_usage(&item.self_ty)?);
if let Some((_, _, for_)) = item.trait_ {
let msg = "Invalid pallet::call, expected no trait ident as in \
`impl<..> Pallet<..> { .. }`";
return Err(syn::Error::new(for_.span(), msg))
}
let mut extra_constants = vec![];
for impl_item in &mut item.items {
let method = if let syn::ImplItem::Method(method) = impl_item {
method
} else {
let msg = "Invalid pallet::call, only method accepted";
return Err(syn::Error::new(impl_item.span(), msg));
};
if method.sig.inputs.len() != 0 {
let msg = "Invalid pallet::extra_constants, method must have 0 args";
return Err(syn::Error::new(method.sig.span(), msg));
}
if method.sig.generics.params.len() != 0 {
let msg = "Invalid pallet::extra_constants, method must have 0 generics";
return Err(syn::Error::new(method.sig.generics.params[0].span(), msg));
}
if method.sig.generics.where_clause.is_some() {
let msg = "Invalid pallet::extra_constants, method must have no where clause";
return Err(syn::Error::new(method.sig.generics.where_clause.span(), msg));
}
let type_ = match &method.sig.output {
syn::ReturnType::Default => {
let msg = "Invalid pallet::extra_constants, method must have a return type";
return Err(syn::Error::new(method.span(), msg));
},
syn::ReturnType::Type(_, type_) => *type_.clone(),
};
extra_constants.push(ExtraConstantDef {
ident: method.sig.ident.clone(),
type_,
doc: helper::get_doc_literals(&method.attrs),
});
}
Ok(Self {
index,
instances,
where_clause: item.generics.where_clause.clone(),
extra_constants,
})
}
}
@@ -0,0 +1,56 @@
// This file is part of Substrate.
// Copyright (C) 2020 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 syn::spanned::Spanned;
use super::helper;
/// Definition for pallet genesis build implementation.
pub struct GenesisBuildDef {
/// The index of item in pallet module.
pub index: usize,
/// A set of usage of instance, must be check for consistency with trait.
pub instances: Vec<helper::InstanceUsage>,
/// The where_clause used.
pub where_clause: Option<syn::WhereClause>,
}
impl GenesisBuildDef {
pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result<Self> {
let item = if let syn::Item::Impl(item) = item {
item
} else {
let msg = "Invalid pallet::genesis_build, expected item impl";
return Err(syn::Error::new(item.span(), msg));
};
let item_trait = &item.trait_.as_ref()
.ok_or_else(|| {
let msg = "Invalid pallet::genesis_build, expected impl<..> GenesisBuild<..> \
for GenesisConfig<..>";
syn::Error::new(item.span(), msg)
})?.1;
let mut instances = vec![];
instances.push(helper::check_genesis_builder_usage(&item_trait)?);
Ok(Self {
index,
instances,
where_clause: item.generics.where_clause.clone(),
})
}
}
@@ -0,0 +1,78 @@
// This file is part of Substrate.
// Copyright (C) 2020 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 syn::spanned::Spanned;
use super::helper;
/// Definition for pallet genesis config type.
///
/// Either:
/// * `struct GenesisConfig`
/// * `enum GenesisConfig`
pub struct GenesisConfigDef {
/// The index of item in pallet module.
pub index: usize,
/// The kind of generic the type `GenesisConfig` has.
pub gen_kind: super::GenericKind,
/// A set of usage of instance, must be check for consistency with trait.
pub instances: Vec<helper::InstanceUsage>,
/// The ident of genesis_config, can be used for span.
pub genesis_config: syn::Ident,
}
impl GenesisConfigDef {
pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result<Self> {
let item_span = item.span();
let (vis, ident, generics) = match &item {
syn::Item::Enum(item) => (&item.vis, &item.ident, &item.generics),
syn::Item::Struct(item) => (&item.vis, &item.ident, &item.generics),
_ => {
let msg = "Invalid pallet::genesis_config, expected enum or struct";
return Err(syn::Error::new(item.span(), msg));
},
};
let mut instances = vec![];
// NOTE: GenesisConfig is not allowed to be only generic on I because it is not supported
// by construct_runtime.
if let Some(u) = helper::check_type_def_optional_gen(&generics, ident.span())? {
instances.push(u);
}
let has_instance = generics.type_params().any(|t| t.ident == "I");
let has_config = generics.type_params().any(|t| t.ident == "T");
let gen_kind = super::GenericKind::from_gens(has_config, has_instance)
.expect("Checked by `helper::check_type_def_optional_gen` above");
if !matches!(vis, syn::Visibility::Public(_)) {
let msg = "Invalid pallet::genesis_config, GenesisConfig must be public";
return Err(syn::Error::new(item_span, msg));
}
if ident != "GenesisConfig" {
let msg = "Invalid pallet::genesis_config, ident must `GenesisConfig`";
return Err(syn::Error::new(ident.span(), msg));
}
Ok(GenesisConfigDef {
index,
genesis_config: ident.clone(),
instances,
gen_kind,
})
}
}
@@ -0,0 +1,600 @@
// This file is part of Substrate.
// Copyright (C) 2020 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 syn::spanned::Spanned;
use quote::ToTokens;
/// List of additional token to be used for parsing.
mod keyword {
syn::custom_keyword!(I);
syn::custom_keyword!(compact);
syn::custom_keyword!(GenesisBuild);
syn::custom_keyword!(Config);
syn::custom_keyword!(T);
syn::custom_keyword!(Pallet);
syn::custom_keyword!(origin);
}
/// A usage of instance, either the trait `Config` has been used with instance or without instance.
/// Used to check for consistency.
#[derive(Clone)]
pub struct InstanceUsage {
pub has_instance: bool,
pub span: proc_macro2::Span,
}
/// Trait implemented for syn items to get mutable references on their attributes.
///
/// NOTE: verbatim variants are not supported.
pub trait MutItemAttrs {
fn mut_item_attrs(&mut self) -> Option<&mut Vec<syn::Attribute>>;
}
/// Take the first pallet attribute (e.g. attribute like `#[pallet..]`) and decode it to `Attr`
pub fn take_first_item_attr<Attr>(item: &mut impl MutItemAttrs) -> syn::Result<Option<Attr>> where
Attr: syn::parse::Parse,
{
let attrs = if let Some(attrs) = item.mut_item_attrs() {
attrs
} else {
return Ok(None)
};
if let Some(index) = attrs.iter()
.position(|attr|
attr.path.segments.first().map_or(false, |segment| segment.ident == "pallet")
)
{
let pallet_attr = attrs.remove(index);
Ok(Some(syn::parse2(pallet_attr.into_token_stream())?))
} else {
Ok(None)
}
}
/// Take all the pallet attributes (e.g. attribute like `#[pallet..]`) and decode them to `Attr`
pub fn take_item_attrs<Attr>(item: &mut impl MutItemAttrs) -> syn::Result<Vec<Attr>> where
Attr: syn::parse::Parse,
{
let mut pallet_attrs = Vec::new();
while let Some(attr) = take_first_item_attr(item)? {
pallet_attrs.push(attr)
}
Ok(pallet_attrs)
}
impl MutItemAttrs for syn::Item {
fn mut_item_attrs(&mut self) -> Option<&mut Vec<syn::Attribute>> {
match self {
Self::Const(item) => Some(item.attrs.as_mut()),
Self::Enum(item) => Some(item.attrs.as_mut()),
Self::ExternCrate(item) => Some(item.attrs.as_mut()),
Self::Fn(item) => Some(item.attrs.as_mut()),
Self::ForeignMod(item) => Some(item.attrs.as_mut()),
Self::Impl(item) => Some(item.attrs.as_mut()),
Self::Macro(item) => Some(item.attrs.as_mut()),
Self::Macro2(item) => Some(item.attrs.as_mut()),
Self::Mod(item) => Some(item.attrs.as_mut()),
Self::Static(item) => Some(item.attrs.as_mut()),
Self::Struct(item) => Some(item.attrs.as_mut()),
Self::Trait(item) => Some(item.attrs.as_mut()),
Self::TraitAlias(item) => Some(item.attrs.as_mut()),
Self::Type(item) => Some(item.attrs.as_mut()),
Self::Union(item) => Some(item.attrs.as_mut()),
Self::Use(item) => Some(item.attrs.as_mut()),
Self::Verbatim(_) => None,
Self::__Nonexhaustive => None,
}
}
}
impl MutItemAttrs for syn::TraitItem {
fn mut_item_attrs(&mut self) -> Option<&mut Vec<syn::Attribute>> {
match self {
Self::Const(item) => Some(item.attrs.as_mut()),
Self::Method(item) => Some(item.attrs.as_mut()),
Self::Type(item) => Some(item.attrs.as_mut()),
Self::Macro(item) => Some(item.attrs.as_mut()),
Self::Verbatim(_) => None,
Self::__Nonexhaustive => None,
}
}
}
impl MutItemAttrs for Vec<syn::Attribute> {
fn mut_item_attrs(&mut self) -> Option<&mut Vec<syn::Attribute>> {
Some(self)
}
}
impl MutItemAttrs for syn::ItemMod {
fn mut_item_attrs(&mut self) -> Option<&mut Vec<syn::Attribute>> {
Some(&mut self.attrs)
}
}
/// Return all doc attributes literals found.
pub fn get_doc_literals(attrs: &Vec<syn::Attribute>) -> Vec<syn::Lit> {
attrs.iter()
.filter_map(|attr| {
if let Ok(syn::Meta::NameValue(meta)) = attr.parse_meta() {
if meta.path.get_ident().map_or(false, |ident| ident == "doc") {
Some(meta.lit.clone())
} else {
None
}
} else {
None
}
})
.collect()
}
/// Parse for `()`
struct Unit;
impl syn::parse::Parse for Unit {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let content;
syn::parenthesized!(content in input);
if !content.is_empty() {
let msg = "unexpected tokens, expected nothing inside parenthesis as `()`";
return Err(syn::Error::new(content.span(), msg));
}
Ok(Self)
}
}
/// Parse for `'static`
struct StaticLifetime;
impl syn::parse::Parse for StaticLifetime {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let lifetime = input.parse::<syn::Lifetime>()?;
if lifetime.ident != "static" {
let msg = "unexpected tokens, expected `static`";
return Err(syn::Error::new(lifetime.ident.span(), msg));
}
Ok(Self)
}
}
/// Check the syntax: `I: 'static = ()`
///
/// `span` is used in case generics is empty (empty generics has span == call_site).
///
/// return the instance if found.
pub fn check_config_def_gen(
gen: &syn::Generics,
span: proc_macro2::Span,
) -> syn::Result<()> {
let expected = "expected `I: 'static = ()`";
pub struct CheckTraitDefGenerics;
impl syn::parse::Parse for CheckTraitDefGenerics {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
input.parse::<keyword::I>()?;
input.parse::<syn::Token![:]>()?;
input.parse::<StaticLifetime>()?;
input.parse::<syn::Token![=]>()?;
input.parse::<Unit>()?;
Ok(Self)
}
}
syn::parse2::<CheckTraitDefGenerics>(gen.params.to_token_stream())
.map_err(|e| {
let msg = format!("Invalid generics: {}", expected);
let mut err = syn::Error::new(span, msg);
err.combine(e);
err
})?;
Ok(())
}
/// Check the syntax:
/// * either `T`
/// * or `T, I = ()`
///
/// `span` is used in case generics is empty (empty generics has span == call_site).
///
/// return the instance if found.
pub fn check_type_def_gen_no_bounds(
gen: &syn::Generics,
span: proc_macro2::Span,
) -> syn::Result<InstanceUsage> {
let expected = "expected `T` or `T, I = ()`";
pub struct Checker(InstanceUsage);
impl syn::parse::Parse for Checker {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let mut instance_usage = InstanceUsage {
has_instance: false,
span: input.span(),
};
input.parse::<keyword::T>()?;
if input.peek(syn::Token![,]) {
instance_usage.has_instance = true;
input.parse::<syn::Token![,]>()?;
input.parse::<keyword::I>()?;
input.parse::<syn::Token![=]>()?;
input.parse::<Unit>()?;
}
Ok(Self(instance_usage))
}
}
let i = syn::parse2::<Checker>(gen.params.to_token_stream())
.map_err(|e| {
let msg = format!("Invalid type def generics: {}", expected);
let mut err = syn::Error::new(span, msg);
err.combine(e);
err
})?.0;
Ok(i)
}
/// Check the syntax:
/// * either `` (no generics
/// * or `T`
/// * or `T: Config`
/// * or `T, I = ()`
/// * or `T: Config<I>, I: 'static = ()`
///
/// `span` is used in case generics is empty (empty generics has span == call_site).
///
/// return some instance usage if there is some generic, or none otherwise.
pub fn check_type_def_optional_gen(
gen: &syn::Generics,
span: proc_macro2::Span,
) -> syn::Result<Option<InstanceUsage>> {
let expected = "expected `` or `T` or `T: Config` or `T, I = ()` or \
`T: Config<I>, I: 'static = ()`";
pub struct Checker(Option<InstanceUsage>);
impl syn::parse::Parse for Checker {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
if input.is_empty() {
return Ok(Self(None))
}
let mut instance_usage = InstanceUsage {
span: input.span(),
has_instance: false,
};
input.parse::<keyword::T>()?;
if input.is_empty() {
return Ok(Self(Some(instance_usage)))
}
let lookahead = input.lookahead1();
if lookahead.peek(syn::Token![,]) {
instance_usage.has_instance = true;
input.parse::<syn::Token![,]>()?;
input.parse::<keyword::I>()?;
input.parse::<syn::Token![=]>()?;
input.parse::<Unit>()?;
Ok(Self(Some(instance_usage)))
} else if lookahead.peek(syn::Token![:]) {
input.parse::<syn::Token![:]>()?;
input.parse::<keyword::Config>()?;
if input.is_empty() {
return Ok(Self(Some(instance_usage)))
}
instance_usage.has_instance = true;
input.parse::<syn::Token![<]>()?;
input.parse::<keyword::I>()?;
input.parse::<syn::Token![>]>()?;
input.parse::<syn::Token![,]>()?;
input.parse::<keyword::I>()?;
input.parse::<syn::Token![:]>()?;
input.parse::<StaticLifetime>()?;
input.parse::<syn::Token![=]>()?;
input.parse::<Unit>()?;
Ok(Self(Some(instance_usage)))
} else {
Err(lookahead.error())
}
}
}
let i = syn::parse2::<Checker>(gen.params.to_token_stream())
.map_err(|e| {
let msg = format!("Invalid type def generics: {}", expected);
let mut err = syn::Error::new(span, msg);
err.combine(e);
err
})?.0
// Span can be call_site if generic is empty. Thus we replace it.
.map(|mut i| { i.span = span; i });
Ok(i)
}
/// Check the syntax:
/// * either `Pallet<T>`
/// * or `Pallet<T, I>`
///
/// return the instance if found.
pub fn check_pallet_struct_usage(type_: &Box<syn::Type>) -> syn::Result<InstanceUsage> {
let expected = "expected `Pallet<T>` or `Pallet<T, I>`";
pub struct Checker(InstanceUsage);
impl syn::parse::Parse for Checker {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let mut instance_usage = InstanceUsage {
span: input.span(),
has_instance: false,
};
input.parse::<keyword::Pallet>()?;
input.parse::<syn::Token![<]>()?;
input.parse::<keyword::T>()?;
if input.peek(syn::Token![,]) {
instance_usage.has_instance = true;
input.parse::<syn::Token![,]>()?;
input.parse::<keyword::I>()?;
}
input.parse::<syn::Token![>]>()?;
Ok(Self(instance_usage))
}
}
let i = syn::parse2::<Checker>(type_.to_token_stream())
.map_err(|e| {
let msg = format!("Invalid pallet struct: {}", expected);
let mut err = syn::Error::new(type_.span(), msg);
err.combine(e);
err
})?.0;
Ok(i)
}
/// Check the generic is:
/// * either `T: Config`
/// * or `T: Config<I>, I: 'static`
///
/// `span` is used in case generics is empty (empty generics has span == call_site).
///
/// return whether it contains instance.
pub fn check_impl_gen(
gen: &syn::Generics,
span: proc_macro2::Span
) -> syn::Result<InstanceUsage> {
let expected = "expected `impl<T: Config>` or `impl<T: Config<I>, I: 'static>`";
pub struct Checker(InstanceUsage);
impl syn::parse::Parse for Checker {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let mut instance_usage = InstanceUsage {
span: input.span(),
has_instance: false,
};
input.parse::<keyword::T>()?;
input.parse::<syn::Token![:]>()?;
input.parse::<keyword::Config>()?;
if input.peek(syn::Token![<]) {
instance_usage.has_instance = true;
input.parse::<syn::Token![<]>()?;
input.parse::<keyword::I>()?;
input.parse::<syn::Token![>]>()?;
input.parse::<syn::Token![,]>()?;
input.parse::<keyword::I>()?;
input.parse::<syn::Token![:]>()?;
input.parse::<StaticLifetime>()?;
}
Ok(Self(instance_usage))
}
}
let i = syn::parse2::<Checker>(gen.params.to_token_stream())
.map_err(|e| {
let mut err = syn::Error::new(span, format!("Invalid generics: {}", expected));
err.combine(e);
err
})?.0;
Ok(i)
}
/// Check the syntax:
/// * or `T`
/// * or `T: Config`
/// * or `T, I = ()`
/// * or `T: Config<I>, I: 'static = ()`
///
/// `span` is used in case generics is empty (empty generics has span == call_site).
///
/// return the instance if found.
pub fn check_type_def_gen(
gen: &syn::Generics,
span: proc_macro2::Span,
) -> syn::Result<InstanceUsage> {
let expected = "expected `T` or `T: Config` or `T, I = ()` or \
`T: Config<I>, I: 'static = ()`";
pub struct Checker(InstanceUsage);
impl syn::parse::Parse for Checker {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let mut instance_usage = InstanceUsage {
span: input.span(),
has_instance: false,
};
input.parse::<keyword::T>()?;
if input.is_empty() {
return Ok(Self(instance_usage))
}
let lookahead = input.lookahead1();
if lookahead.peek(syn::Token![,]) {
instance_usage.has_instance = true;
input.parse::<syn::Token![,]>()?;
input.parse::<keyword::I>()?;
input.parse::<syn::Token![=]>()?;
input.parse::<Unit>()?;
Ok(Self(instance_usage))
} else if lookahead.peek(syn::Token![:]) {
input.parse::<syn::Token![:]>()?;
input.parse::<keyword::Config>()?;
if input.is_empty() {
return Ok(Self(instance_usage))
}
instance_usage.has_instance = true;
input.parse::<syn::Token![<]>()?;
input.parse::<keyword::I>()?;
input.parse::<syn::Token![>]>()?;
input.parse::<syn::Token![,]>()?;
input.parse::<keyword::I>()?;
input.parse::<syn::Token![:]>()?;
input.parse::<StaticLifetime>()?;
input.parse::<syn::Token![=]>()?;
input.parse::<Unit>()?;
Ok(Self(instance_usage))
} else {
Err(lookahead.error())
}
}
}
let mut i = syn::parse2::<Checker>(gen.params.to_token_stream())
.map_err(|e| {
let msg = format!("Invalid type def generics: {}", expected);
let mut err = syn::Error::new(span, msg);
err.combine(e);
err
})?.0;
// Span can be call_site if generic is empty. Thus we replace it.
i.span = span;
Ok(i)
}
/// Check the syntax:
/// * either `GenesisBuild<T>`
/// * or `GenesisBuild<T, I>`
///
/// return the instance if found.
pub fn check_genesis_builder_usage(type_: &syn::Path) -> syn::Result<InstanceUsage> {
let expected = "expected `GenesisBuild<T>` or `GenesisBuild<T, I>`";
pub struct Checker(InstanceUsage);
impl syn::parse::Parse for Checker {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let mut instance_usage = InstanceUsage {
span: input.span(),
has_instance: false,
};
input.parse::<keyword::GenesisBuild>()?;
input.parse::<syn::Token![<]>()?;
input.parse::<keyword::T>()?;
if input.peek(syn::Token![,]) {
instance_usage.has_instance = true;
input.parse::<syn::Token![,]>()?;
input.parse::<keyword::I>()?;
}
input.parse::<syn::Token![>]>()?;
Ok(Self(instance_usage))
}
}
let i = syn::parse2::<Checker>(type_.to_token_stream())
.map_err(|e| {
let msg = format!("Invalid genesis builder: {}", expected);
let mut err = syn::Error::new(type_.span(), msg);
err.combine(e);
err
})?.0;
Ok(i)
}
/// Check the syntax:
/// * either `` (no generics)
/// * or `T: Config`
/// * or `T: Config<I>, I: 'static`
///
/// `span` is used in case generics is empty (empty generics has span == call_site).
///
/// return the instance if found.
pub fn check_type_value_gen(
gen: &syn::Generics,
span: proc_macro2::Span,
) -> syn::Result<Option<InstanceUsage>> {
let expected = "expected `` or `T: Config` or `T: Config<I>, I: 'static`";
pub struct Checker(Option<InstanceUsage>);
impl syn::parse::Parse for Checker {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
if input.is_empty() {
return Ok(Self(None))
}
input.parse::<keyword::T>()?;
input.parse::<syn::Token![:]>()?;
input.parse::<keyword::Config>()?;
let mut instance_usage = InstanceUsage {
span: input.span(),
has_instance: false,
};
if input.is_empty() {
return Ok(Self(Some(instance_usage)))
}
instance_usage.has_instance = true;
input.parse::<syn::Token![<]>()?;
input.parse::<keyword::I>()?;
input.parse::<syn::Token![>]>()?;
input.parse::<syn::Token![,]>()?;
input.parse::<keyword::I>()?;
input.parse::<syn::Token![:]>()?;
input.parse::<StaticLifetime>()?;
Ok(Self(Some(instance_usage)))
}
}
let i = syn::parse2::<Checker>(gen.params.to_token_stream())
.map_err(|e| {
let msg = format!("Invalid type def generics: {}", expected);
let mut err = syn::Error::new(span, msg);
err.combine(e);
err
})?.0
// Span can be call_site if generic is empty. Thus we replace it.
.map(|mut i| { i.span = span; i });
Ok(i)
}
@@ -0,0 +1,69 @@
// This file is part of Substrate.
// Copyright (C) 2020 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 syn::spanned::Spanned;
use super::helper;
/// Implementation of the pallet hooks.
pub struct HooksDef {
/// The index of item in pallet.
pub index: usize,
/// A set of usage of instance, must be check for consistency with trait.
pub instances: Vec<helper::InstanceUsage>,
/// The where_clause used.
pub where_clause: Option<syn::WhereClause>,
}
impl HooksDef {
pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result<Self> {
let item = if let syn::Item::Impl(item) = item {
item
} else {
let msg = "Invalid pallet::hooks, expected item impl";
return Err(syn::Error::new(item.span(), msg));
};
let mut instances = vec![];
instances.push(helper::check_pallet_struct_usage(&item.self_ty)?);
instances.push(helper::check_impl_gen(&item.generics, item.impl_token.span())?);
let item_trait = &item.trait_.as_ref()
.ok_or_else(|| {
let msg = "Invalid pallet::hooks, expected impl<..> Hooks \
for Pallet<..>";
syn::Error::new(item.span(), msg)
})?.1;
if item_trait.segments.len() != 1
|| item_trait.segments[0].ident != "Hooks"
{
let msg = format!(
"Invalid pallet::hooks, expected trait to be `Hooks` found `{}`\
, you can import from `frame_support::pallet_prelude`",
quote::quote!(#item_trait)
);
return Err(syn::Error::new(item_trait.span(), msg));
}
Ok(Self {
index,
instances,
where_clause: item.generics.where_clause.clone(),
})
}
}
@@ -0,0 +1,59 @@
// This file is part of Substrate.
// Copyright (C) 2020 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 syn::spanned::Spanned;
use super::helper;
/// The definition of the pallet inherent implementation.
pub struct InherentDef {
/// The index of inherent item in pallet module.
pub index: usize,
/// A set of usage of instance, must be check for consistency with trait.
pub instances: Vec<helper::InstanceUsage>,
}
impl InherentDef {
pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result<Self> {
let item = if let syn::Item::Impl(item) = item {
item
} else {
let msg = "Invalid pallet::inherent, expected item impl";
return Err(syn::Error::new(item.span(), msg));
};
if item.trait_.is_none() {
let msg = "Invalid pallet::inherent, expected impl<..> ProvideInherent for Pallet<..>";
return Err(syn::Error::new(item.span(), msg));
}
if let Some(last) = item.trait_.as_ref().unwrap().1.segments.last() {
if last.ident != "ProvideInherent" {
let msg = "Invalid pallet::inherent, expected trait ProvideInherent";
return Err(syn::Error::new(last.span(), msg));
}
} else {
let msg = "Invalid pallet::inherent, expected impl<..> ProvideInherent for Pallet<..>";
return Err(syn::Error::new(item.span(), msg));
}
let mut instances = vec![];
instances.push(helper::check_pallet_struct_usage(&item.self_ty)?);
instances.push(helper::check_impl_gen(&item.generics, item.impl_token.span())?);
Ok(InherentDef { index, instances })
}
}
@@ -0,0 +1,461 @@
// This file is part of Substrate.
// Copyright (C) 2020 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.
//! Parse for pallet macro.
//!
//! Parse the module into `Def` struct through `Def::try_from` function.
pub mod config;
pub mod pallet_struct;
pub mod hooks;
pub mod call;
pub mod error;
pub mod origin;
pub mod inherent;
pub mod storage;
pub mod event;
pub mod helper;
pub mod genesis_config;
pub mod genesis_build;
pub mod validate_unsigned;
pub mod type_value;
pub mod extra_constants;
use syn::spanned::Spanned;
use frame_support_procedural_tools::generate_crate_access_2018;
/// Parsed definition of a pallet.
pub struct Def {
/// The module items.
/// (their order must not be modified because they are registered in individual definitions).
pub item: syn::ItemMod,
pub config: config::ConfigDef,
pub pallet_struct: pallet_struct::PalletStructDef,
pub hooks: hooks::HooksDef,
pub call: call::CallDef,
pub storages: Vec<storage::StorageDef>,
pub error: Option<error::ErrorDef>,
pub event: Option<event::EventDef>,
pub origin: Option<origin::OriginDef>,
pub inherent: Option<inherent::InherentDef>,
pub genesis_config: Option<genesis_config::GenesisConfigDef>,
pub genesis_build: Option<genesis_build::GenesisBuildDef>,
pub validate_unsigned: Option<validate_unsigned::ValidateUnsignedDef>,
pub extra_constants: Option<extra_constants::ExtraConstantsDef>,
pub type_values: Vec<type_value::TypeValueDef>,
pub frame_system: syn::Ident,
pub frame_support: syn::Ident,
}
impl Def {
pub fn try_from(mut item: syn::ItemMod) -> syn::Result<Self> {
let frame_system = generate_crate_access_2018("frame-system")?;
let frame_support = generate_crate_access_2018("frame-support")?;
let item_span = item.span().clone();
let items = &mut item.content.as_mut()
.ok_or_else(|| {
let msg = "Invalid pallet definition, expected mod to be inlined.";
syn::Error::new(item_span, msg)
})?.1;
let mut config = None;
let mut pallet_struct = None;
let mut hooks = None;
let mut call = None;
let mut error = None;
let mut event = None;
let mut origin = None;
let mut inherent = None;
let mut genesis_config = None;
let mut genesis_build = None;
let mut validate_unsigned = None;
let mut extra_constants = None;
let mut storages = vec![];
let mut type_values = vec![];
for (index, item) in items.iter_mut().enumerate() {
let pallet_attr: Option<PalletAttr> = helper::take_first_item_attr(item)?;
match pallet_attr {
Some(PalletAttr::Config(_)) if config.is_none() =>
config = Some(config::ConfigDef::try_from(&frame_system, index, item)?),
Some(PalletAttr::Pallet(_)) if pallet_struct.is_none() =>
pallet_struct = Some(pallet_struct::PalletStructDef::try_from(index, item)?),
Some(PalletAttr::Hooks(_)) if hooks.is_none() => {
let m = hooks::HooksDef::try_from(index, item)?;
hooks = Some(m);
},
Some(PalletAttr::Call(span)) if call.is_none() =>
call = Some(call::CallDef::try_from(span, index, item)?),
Some(PalletAttr::Error(_)) if error.is_none() =>
error = Some(error::ErrorDef::try_from(index, item)?),
Some(PalletAttr::Event(_)) if event.is_none() =>
event = Some(event::EventDef::try_from(index, item)?),
Some(PalletAttr::GenesisConfig(_)) if genesis_config.is_none() => {
genesis_config =
Some(genesis_config::GenesisConfigDef::try_from(index, item)?);
},
Some(PalletAttr::GenesisBuild(_)) if genesis_build.is_none() =>
genesis_build = Some(genesis_build::GenesisBuildDef::try_from(index, item)?),
Some(PalletAttr::Origin(_)) if origin.is_none() =>
origin = Some(origin::OriginDef::try_from(index, item)?),
Some(PalletAttr::Inherent(_)) if inherent.is_none() =>
inherent = Some(inherent::InherentDef::try_from(index, item)?),
Some(PalletAttr::Storage(_)) =>
storages.push(storage::StorageDef::try_from(index, item)?),
Some(PalletAttr::ValidateUnsigned(_)) if validate_unsigned.is_none() => {
let v = validate_unsigned::ValidateUnsignedDef::try_from(index, item)?;
validate_unsigned = Some(v);
},
Some(PalletAttr::TypeValue(_)) =>
type_values.push(type_value::TypeValueDef::try_from(index, item)?),
Some(PalletAttr::ExtraConstants(_)) => {
extra_constants =
Some(extra_constants::ExtraConstantsDef::try_from(index, item)?)
},
Some(attr) => {
let msg = "Invalid duplicated attribute";
return Err(syn::Error::new(attr.span(), msg));
},
None => (),
}
}
if genesis_config.is_some() != genesis_build.is_some() {
let msg = format!(
"`#[pallet::genesis_config]` and `#[pallet::genesis_build]` attributes must be \
either both used or both not used, instead genesis_config is {} and genesis_build \
is {}",
genesis_config.as_ref().map_or("unused", |_| "used"),
genesis_build.as_ref().map_or("unused", |_| "used"),
);
return Err(syn::Error::new(item_span, msg));
}
let def = Def {
item: item,
config: config.ok_or_else(|| syn::Error::new(item_span, "Missing `#[pallet::config]`"))?,
pallet_struct: pallet_struct
.ok_or_else(|| syn::Error::new(item_span, "Missing `#[pallet::pallet]`"))?,
hooks: hooks
.ok_or_else(|| syn::Error::new(item_span, "Missing `#[pallet::hooks]`"))?,
call: call.ok_or_else(|| syn::Error::new(item_span, "Missing `#[pallet::call]"))?,
extra_constants,
genesis_config,
genesis_build,
validate_unsigned,
error,
event,
origin,
inherent,
storages,
type_values,
frame_system,
frame_support,
};
def.check_instance_usage()?;
def.check_event_usage()?;
Ok(def)
}
/// Check that usage of trait `Event` is consistent with the definition, i.e. it is declared
/// and trait defines type Event, or not declared and no trait associated type.
fn check_event_usage(&self) -> syn::Result<()> {
match (
self.config.has_event_type,
self.event.is_some(),
) {
(true, false) => {
let msg = "Invalid usage of Event, `Config` contains associated type `Event`, \
but enum `Event` is not declared (i.e. no use of `#[pallet::event]`). \
Note that type `Event` in trait is reserved to work alongside pallet event.";
Err(syn::Error::new(proc_macro2::Span::call_site(), msg))
},
(false, true) => {
let msg = "Invalid usage of Event, `Config` contains no associated type \
`Event`, but enum `Event` is declared (in use of `#[pallet::event]`). \
An Event associated type must be declare on trait `Config`.";
Err(syn::Error::new(proc_macro2::Span::call_site(), msg))
},
_ => Ok(())
}
}
/// Check that usage of trait `Config` is consistent with the definition, i.e. it is used with
/// instance iff it is defined with instance.
fn check_instance_usage(&self) -> syn::Result<()> {
let mut instances = vec![];
instances.extend_from_slice(&self.call.instances[..]);
instances.extend_from_slice(&self.pallet_struct.instances[..]);
instances.extend_from_slice(&self.hooks.instances[..]);
instances.extend(&mut self.storages.iter().flat_map(|s| s.instances.clone()));
if let Some(event) = &self.event {
instances.extend_from_slice(&event.instances[..]);
}
if let Some(error) = &self.error {
instances.extend_from_slice(&error.instances[..]);
}
if let Some(inherent) = &self.inherent {
instances.extend_from_slice(&inherent.instances[..]);
}
if let Some(origin) = &self.origin {
instances.extend_from_slice(&origin.instances[..]);
}
if let Some(genesis_config) = &self.genesis_config {
instances.extend_from_slice(&genesis_config.instances[..]);
}
if let Some(genesis_build) = &self.genesis_build {
instances.extend_from_slice(&genesis_build.instances[..]);
}
if let Some(extra_constants) = &self.extra_constants {
instances.extend_from_slice(&extra_constants.instances[..]);
}
let mut errors = instances.into_iter()
.filter_map(|instances| {
if instances.has_instance == self.config.has_instance {
return None
}
let msg = if self.config.has_instance {
"Invalid generic declaration, trait is defined with instance but generic use none"
} else {
"Invalid generic declaration, trait is defined without instance but generic use \
some"
};
Some(syn::Error::new(instances.span, msg))
});
if let Some(mut first_error) = errors.next() {
for error in errors {
first_error.combine(error)
}
Err(first_error)
} else {
Ok(())
}
}
/// Depending on if pallet is instantiable:
/// * either `T: Config`
/// * or `T: Config<I>, I: 'static`
pub fn type_impl_generics(&self) -> proc_macro2::TokenStream {
if self.config.has_instance {
quote::quote!(T: Config<I>, I: 'static)
} else {
quote::quote!(T: Config)
}
}
/// Depending on if pallet is instantiable:
/// * either `T: Config`
/// * or `T: Config<I>, I: 'static = ()`
pub fn type_decl_bounded_generics(&self) -> proc_macro2::TokenStream {
if self.config.has_instance {
quote::quote!(T: Config<I>, I: 'static = ())
} else {
quote::quote!(T: Config)
}
}
/// Depending on if pallet is instantiable:
/// * either `T`
/// * or `T, I = ()`
pub fn type_decl_generics(&self) -> proc_macro2::TokenStream {
if self.config.has_instance {
quote::quote!(T, I = ())
} else {
quote::quote!(T)
}
}
/// Depending on if pallet is instantiable:
/// * either ``
/// * or `<I>`
/// to be used when using pallet trait `Config`
pub fn trait_use_generics(&self) -> proc_macro2::TokenStream {
if self.config.has_instance {
quote::quote!(<I>)
} else {
quote::quote!()
}
}
/// Depending on if pallet is instantiable:
/// * either `T`
/// * or `T, I`
pub fn type_use_generics(&self) -> proc_macro2::TokenStream {
if self.config.has_instance {
quote::quote!(T, I)
} else {
quote::quote!(T)
}
}
}
/// Some generic kind for type which can be not generic, or generic over config,
/// or generic over config and instance, but not generic only over instance.
pub enum GenericKind {
None,
Config,
ConfigAndInstance,
}
impl GenericKind {
/// Return Err if it is only generics over instance but not over config.
pub fn from_gens(has_config: bool, has_instance: bool) -> Result<Self, ()> {
match (has_config, has_instance) {
(false, false) => Ok(GenericKind::None),
(true, false) => Ok(GenericKind::Config),
(true, true) => Ok(GenericKind::ConfigAndInstance),
(false, true) => Err(()),
}
}
/// Return the generic to be used when using the type.
///
/// Depending on its definition it can be: ``, `T` or `T, I`
pub fn type_use_gen(&self) -> proc_macro2::TokenStream {
match self {
GenericKind::None => quote::quote!(),
GenericKind::Config => quote::quote!(T),
GenericKind::ConfigAndInstance => quote::quote!(T, I),
}
}
/// Return the generic to be used in `impl<..>` when implementing on the type.
pub fn type_impl_gen(&self) -> proc_macro2::TokenStream {
match self {
GenericKind::None => quote::quote!(),
GenericKind::Config => quote::quote!(T: Config),
GenericKind::ConfigAndInstance => quote::quote!(T: Config<I>, I: 'static),
}
}
/// Return whereas the type has some generic.
pub fn is_generic(&self) -> bool {
match self {
GenericKind::None => false,
GenericKind::Config | GenericKind::ConfigAndInstance => true,
}
}
}
/// List of additional token to be used for parsing.
mod keyword {
syn::custom_keyword!(origin);
syn::custom_keyword!(call);
syn::custom_keyword!(event);
syn::custom_keyword!(config);
syn::custom_keyword!(hooks);
syn::custom_keyword!(inherent);
syn::custom_keyword!(error);
syn::custom_keyword!(storage);
syn::custom_keyword!(genesis_build);
syn::custom_keyword!(genesis_config);
syn::custom_keyword!(validate_unsigned);
syn::custom_keyword!(type_value);
syn::custom_keyword!(pallet);
syn::custom_keyword!(generate_store);
syn::custom_keyword!(Store);
syn::custom_keyword!(extra_constants);
}
/// Parse attributes for item in pallet module
/// syntax must be `pallet::` (e.g. `#[pallet::config]`)
enum PalletAttr {
Config(proc_macro2::Span),
Pallet(proc_macro2::Span),
Hooks(proc_macro2::Span),
Call(proc_macro2::Span),
Error(proc_macro2::Span),
Event(proc_macro2::Span),
Origin(proc_macro2::Span),
Inherent(proc_macro2::Span),
Storage(proc_macro2::Span),
GenesisConfig(proc_macro2::Span),
GenesisBuild(proc_macro2::Span),
ValidateUnsigned(proc_macro2::Span),
TypeValue(proc_macro2::Span),
ExtraConstants(proc_macro2::Span),
}
impl PalletAttr {
fn span(&self) -> proc_macro2::Span {
match self {
Self::Config(span) => span.clone(),
Self::Pallet(span) => span.clone(),
Self::Hooks(span) => span.clone(),
Self::Call(span) => span.clone(),
Self::Error(span) => span.clone(),
Self::Event(span) => span.clone(),
Self::Origin(span) => span.clone(),
Self::Inherent(span) => span.clone(),
Self::Storage(span) => span.clone(),
Self::GenesisConfig(span) => span.clone(),
Self::GenesisBuild(span) => span.clone(),
Self::ValidateUnsigned(span) => span.clone(),
Self::TypeValue(span) => span.clone(),
Self::ExtraConstants(span) => span.clone(),
}
}
}
impl syn::parse::Parse for PalletAttr {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
input.parse::<syn::Token![#]>()?;
let content;
syn::bracketed!(content in input);
content.parse::<keyword::pallet>()?;
content.parse::<syn::Token![::]>()?;
let lookahead = content.lookahead1();
if lookahead.peek(keyword::config) {
Ok(PalletAttr::Config(content.parse::<keyword::config>()?.span()))
} else if lookahead.peek(keyword::pallet) {
Ok(PalletAttr::Pallet(content.parse::<keyword::pallet>()?.span()))
} else if lookahead.peek(keyword::hooks) {
Ok(PalletAttr::Hooks(content.parse::<keyword::hooks>()?.span()))
} else if lookahead.peek(keyword::call) {
Ok(PalletAttr::Call(content.parse::<keyword::call>()?.span()))
} else if lookahead.peek(keyword::error) {
Ok(PalletAttr::Error(content.parse::<keyword::error>()?.span()))
} else if lookahead.peek(keyword::event) {
Ok(PalletAttr::Event(content.parse::<keyword::event>()?.span()))
} else if lookahead.peek(keyword::origin) {
Ok(PalletAttr::Origin(content.parse::<keyword::origin>()?.span()))
} else if lookahead.peek(keyword::inherent) {
Ok(PalletAttr::Inherent(content.parse::<keyword::inherent>()?.span()))
} else if lookahead.peek(keyword::storage) {
Ok(PalletAttr::Storage(content.parse::<keyword::storage>()?.span()))
} else if lookahead.peek(keyword::genesis_config) {
Ok(PalletAttr::GenesisConfig(content.parse::<keyword::genesis_config>()?.span()))
} else if lookahead.peek(keyword::genesis_build) {
Ok(PalletAttr::GenesisBuild(content.parse::<keyword::genesis_build>()?.span()))
} else if lookahead.peek(keyword::validate_unsigned) {
Ok(PalletAttr::ValidateUnsigned(content.parse::<keyword::validate_unsigned>()?.span()))
} else if lookahead.peek(keyword::type_value) {
Ok(PalletAttr::TypeValue(content.parse::<keyword::type_value>()?.span()))
} else if lookahead.peek(keyword::extra_constants) {
Ok(PalletAttr::ExtraConstants(content.parse::<keyword::extra_constants>()?.span()))
} else {
Err(lookahead.error())
}
}
}
@@ -0,0 +1,80 @@
// This file is part of Substrate.
// Copyright (C) 2020 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 syn::spanned::Spanned;
use super::helper;
/// Definition of the pallet origin type.
///
/// Either:
/// * `type Origin`
/// * `struct Origin`
/// * `enum Origin`
pub struct OriginDef {
/// The index of item in pallet module.
pub index: usize,
pub has_instance: bool,
pub is_generic: bool,
/// A set of usage of instance, must be check for consistency with trait.
pub instances: Vec<helper::InstanceUsage>,
}
impl OriginDef {
pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result<Self> {
let item_span = item.span();
let (vis, ident, generics) = match &item {
syn::Item::Enum(item) => (&item.vis, &item.ident, &item.generics),
syn::Item::Struct(item) => (&item.vis, &item.ident, &item.generics),
syn::Item::Type(item) => (&item.vis, &item.ident, &item.generics),
_ => {
let msg = "Invalid pallet::origin, expected enum or struct or type";
return Err(syn::Error::new(item.span(), msg));
},
};
let has_instance = generics.params.len() == 2;
let is_generic = generics.params.len() > 0;
let mut instances = vec![];
if let Some(u) = helper::check_type_def_optional_gen(&generics, item.span())? {
instances.push(u);
} else {
// construct_runtime only allow generic event for instantiable pallet.
instances.push(helper::InstanceUsage {
has_instance: false,
span: ident.span(),
})
}
if !matches!(vis, syn::Visibility::Public(_)) {
let msg = "Invalid pallet::origin, Origin must be public";
return Err(syn::Error::new(item_span, msg));
}
if ident != "Origin" {
let msg = "Invalid pallet::origin, ident must `Origin`";
return Err(syn::Error::new(ident.span(), msg));
}
Ok(OriginDef {
index,
has_instance,
is_generic,
instances,
})
}
}
@@ -0,0 +1,99 @@
// This file is part of Substrate.
// Copyright (C) 2020 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 super::helper;
use syn::spanned::Spanned;
use quote::ToTokens;
/// List of additional token to be used for parsing.
mod keyword {
syn::custom_keyword!(pallet);
syn::custom_keyword!(Pallet);
syn::custom_keyword!(generate_store);
syn::custom_keyword!(Store);
}
/// Definition of the pallet pallet.
pub struct PalletStructDef {
/// The index of item in pallet pallet.
pub index: usize,
/// A set of usage of instance, must be check for consistency with config trait.
pub instances: Vec<helper::InstanceUsage>,
/// The keyword Pallet used (contains span).
pub pallet: keyword::Pallet,
/// Whether the trait `Store` must be generated.
pub store: Option<(syn::Visibility, keyword::Store)>
}
/// Parse for `#[pallet::generate_store($vis trait Store)]`
pub struct PalletStructAttr {
vis: syn::Visibility,
keyword: keyword::Store,
}
impl syn::parse::Parse for PalletStructAttr {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
input.parse::<syn::Token![#]>()?;
let content;
syn::bracketed!(content in input);
content.parse::<keyword::pallet>()?;
content.parse::<syn::Token![::]>()?;
content.parse::<keyword::generate_store>()?;
let generate_content;
syn::parenthesized!(generate_content in content);
let vis = generate_content.parse::<syn::Visibility>()?;
generate_content.parse::<syn::Token![trait]>()?;
let keyword = generate_content.parse::<keyword::Store>()?;
Ok(Self { vis, keyword })
}
}
impl PalletStructDef {
pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result<Self> {
let item = if let syn::Item::Struct(item) = item {
item
} else {
let msg = "Invalid pallet::pallet, expected struct definition";
return Err(syn::Error::new(item.span(), msg));
};
let mut event_attrs: Vec<PalletStructAttr> = helper::take_item_attrs(&mut item.attrs)?;
if event_attrs.len() > 1 {
let msg = "Invalid pallet::pallet, multiple argument pallet::generate_store found";
return Err(syn::Error::new(event_attrs[1].keyword.span(), msg));
}
let store = event_attrs.pop().map(|attr| (attr.vis, attr.keyword));
let pallet = syn::parse2::<keyword::Pallet>(item.ident.to_token_stream())?;
if !matches!(item.vis, syn::Visibility::Public(_)) {
let msg = "Invalid pallet::pallet, Pallet must be public";
return Err(syn::Error::new(item.span(), msg));
}
if item.generics.where_clause.is_some() {
let msg = "Invalid pallet::pallet, where clause not supported on Pallet declaration";
return Err(syn::Error::new(item.generics.where_clause.span(), msg));
}
let mut instances = vec![];
instances.push(helper::check_type_def_gen_no_bounds(&item.generics, item.ident.span())?);
Ok(Self { index, instances, pallet, store })
}
}
@@ -0,0 +1,221 @@
// This file is part of Substrate.
// Copyright (C) 2020 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 super::helper;
use syn::spanned::Spanned;
use quote::ToTokens;
/// List of additional token to be used for parsing.
mod keyword {
syn::custom_keyword!(Error);
syn::custom_keyword!(pallet);
syn::custom_keyword!(getter);
syn::custom_keyword!(OptionQuery);
syn::custom_keyword!(ValueQuery);
}
/// Parse for `#[pallet::getter(fn dummy)]`
pub struct PalletStorageAttr {
getter: syn::Ident,
}
impl syn::parse::Parse for PalletStorageAttr {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
input.parse::<syn::Token![#]>()?;
let content;
syn::bracketed!(content in input);
content.parse::<keyword::pallet>()?;
content.parse::<syn::Token![::]>()?;
content.parse::<keyword::getter>()?;
let generate_content;
syn::parenthesized!(generate_content in content);
generate_content.parse::<syn::Token![fn]>()?;
Ok(Self { getter: generate_content.parse::<syn::Ident>()? })
}
}
/// The value and key types used by storages. Needed to expand metadata.
pub enum Metadata{
Value { value: syn::GenericArgument },
Map { value: syn::GenericArgument, key: syn::GenericArgument },
DoubleMap {
value: syn::GenericArgument,
key1: syn::GenericArgument,
key2: syn::GenericArgument
},
}
pub enum QueryKind {
OptionQuery,
ValueQuery,
}
/// Definition of a storage, storage is a storage type like
/// `type MyStorage = StorageValue<MyStorageP, u32>`
/// The keys and values types are parsed in order to get metadata
pub struct StorageDef {
/// The index of error item in pallet module.
pub index: usize,
/// Visibility of the storage type.
pub vis: syn::Visibility,
/// The type ident, to generate the StoragePrefix for.
pub ident: syn::Ident,
/// The keys and value metadata of the storage.
pub metadata: Metadata,
/// The doc associated to the storage.
pub docs: Vec<syn::Lit>,
/// A set of usage of instance, must be check for consistency with config.
pub instances: Vec<helper::InstanceUsage>,
/// Optional getter to generate. If some then query_kind is ensured to be some as well.
pub getter: Option<syn::Ident>,
/// Whereas the querytype of the storage is OptionQuery or ValueQuery.
/// Note that this is best effort as it can't be determined when QueryKind is generic, and
/// result can be false if user do some unexpected type alias.
pub query_kind: Option<QueryKind>,
/// Where clause of type definition.
pub where_clause: Option<syn::WhereClause>,
}
/// In `Foo<A, B, C>` retrieve the argument at given position, i.e. A is argument at position 0.
fn retrieve_arg(
segment: &syn::PathSegment,
arg_pos: usize,
) -> syn::Result<syn::GenericArgument> {
if let syn::PathArguments::AngleBracketed(args) = &segment.arguments {
if arg_pos < args.args.len() {
Ok(args.args[arg_pos].clone())
} else {
let msg = format!("pallet::storage unexpected number of generic argument, expected at \
least {} args, found {}", arg_pos + 1, args.args.len());
Err(syn::Error::new(args.span(), msg))
}
} else {
let msg = format!("pallet::storage unexpected number of generic argument, expected at \
least {} args, found none", arg_pos + 1);
Err(syn::Error::new(segment.span(), msg))
}
}
impl StorageDef {
pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result<Self> {
let item = if let syn::Item::Type(item) = item {
item
} else {
return Err(syn::Error::new(item.span(), "Invalid pallet::storage, expected item type"));
};
let mut attrs: Vec<PalletStorageAttr> = helper::take_item_attrs(&mut item.attrs)?;
if attrs.len() > 1 {
let msg = "Invalid pallet::storage, multiple argument pallet::getter found";
return Err(syn::Error::new(attrs[1].getter.span(), msg));
}
let getter = attrs.pop().map(|attr| attr.getter);
let mut instances = vec![];
instances.push(helper::check_type_def_gen(&item.generics, item.ident.span())?);
let where_clause = item.generics.where_clause.clone();
let docs = helper::get_doc_literals(&item.attrs);
let typ = if let syn::Type::Path(typ) = &*item.ty {
typ
} else {
let msg = "Invalid pallet::storage, expected type path";
return Err(syn::Error::new(item.ty.span(), msg));
};
if typ.path.segments.len() != 1 {
let msg = "Invalid pallet::storage, expected type path with one segment";
return Err(syn::Error::new(item.ty.span(), msg));
}
let query_kind;
let metadata = match &*typ.path.segments[0].ident.to_string() {
"StorageValue" => {
query_kind = retrieve_arg(&typ.path.segments[0], 2);
Metadata::Value {
value: retrieve_arg(&typ.path.segments[0], 1)?,
}
}
"StorageMap" => {
query_kind = retrieve_arg(&typ.path.segments[0], 4);
Metadata::Map {
key: retrieve_arg(&typ.path.segments[0], 2)?,
value: retrieve_arg(&typ.path.segments[0], 3)?,
}
}
"StorageDoubleMap" => {
query_kind = retrieve_arg(&typ.path.segments[0], 6);
Metadata::DoubleMap {
key1: retrieve_arg(&typ.path.segments[0], 2)?,
key2: retrieve_arg(&typ.path.segments[0], 4)?,
value: retrieve_arg(&typ.path.segments[0], 5)?,
}
}
found @ _ => {
let msg = format!(
"Invalid pallet::storage, expected ident: `StorageValue` or \
`StorageMap` or `StorageDoubleMap` in order to expand metadata, found \
`{}`",
found,
);
return Err(syn::Error::new(item.ty.span(), msg));
}
};
let query_kind = query_kind
.map(|query_kind| match query_kind {
syn::GenericArgument::Type(syn::Type::Path(path))
if path.path.segments.last().map_or(false, |s| s.ident == "OptionQuery")
=> Some(QueryKind::OptionQuery),
syn::GenericArgument::Type(syn::Type::Path(path))
if path.path.segments.last().map_or(false, |s| s.ident == "ValueQuery")
=> Some(QueryKind::ValueQuery),
_ => None,
})
.unwrap_or(Some(QueryKind::OptionQuery)); // This value must match the default generic.
if query_kind.is_none() && getter.is_some() {
let msg = "Invalid pallet::storage, cannot generate getter because QueryKind is not \
identifiable. QueryKind must be `OptionQuery`, `ValueQuery`, or default one to be \
identifiable.";
return Err(syn::Error::new(getter.unwrap().span(), msg));
}
let prefix_arg = retrieve_arg(&typ.path.segments[0], 0)?;
syn::parse2::<syn::Token![_]>(prefix_arg.to_token_stream())
.map_err(|e| {
let msg = "Invalid use of `#[pallet::storage]`, the type first generic argument \
must be `_`, the final argument is automatically set by macro.";
let mut err = syn::Error::new(prefix_arg.span(), msg);
err.combine(e);
err
})?;
Ok(StorageDef {
index,
vis: item.vis.clone(),
ident: item.ident.clone(),
instances,
metadata,
docs,
getter,
query_kind,
where_clause,
})
}
}
@@ -0,0 +1,101 @@
// This file is part of Substrate.
// Copyright (C) 2020 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 super::helper;
use syn::spanned::Spanned;
/// Definition of type value. Just a function which is expanded to a struct implementing `Get`.
pub struct TypeValueDef {
/// The index of error item in pallet module.
pub index: usize,
/// Visibility of the struct to generate.
pub vis: syn::Visibility,
/// Ident of the struct to generate.
pub ident: syn::Ident,
/// The type return by Get.
pub type_: Box<syn::Type>,
/// The block returning the value to get
pub block: Box<syn::Block>,
/// If type value is generic over `T` (or `T` and `I` for instantiable pallet)
pub is_generic: bool,
/// A set of usage of instance, must be check for consistency with config.
pub instances: Vec<helper::InstanceUsage>,
/// The where clause of the function.
pub where_clause: Option<syn::WhereClause>,
}
impl TypeValueDef {
pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result<Self> {
let item = if let syn::Item::Fn(item) = item {
item
} else {
let msg = "Invalid pallet::type_value, expected item fn";
return Err(syn::Error::new(item.span(), msg));
};
if !item.attrs.is_empty() {
let msg = "Invalid pallet::type_value, unexpected attribute";
return Err(syn::Error::new(item.attrs[0].span(), msg));
}
if let Some(span) = item.sig.constness.as_ref().map(|t| t.span())
.or(item.sig.asyncness.as_ref().map(|t| t.span()))
.or(item.sig.unsafety.as_ref().map(|t| t.span()))
.or(item.sig.abi.as_ref().map(|t| t.span()))
.or(item.sig.variadic.as_ref().map(|t| t.span()))
{
let msg = "Invalid pallet::type_value, unexpected token";
return Err(syn::Error::new(span, msg));
}
if !item.sig.inputs.is_empty() {
let msg = "Invalid pallet::type_value, unexpected argument";
return Err(syn::Error::new(item.sig.inputs[0].span(), msg));
}
let vis = item.vis.clone();
let ident = item.sig.ident.clone();
let block = item.block.clone();
let type_ = match item.sig.output.clone() {
syn::ReturnType::Type(_, type_) => type_,
syn::ReturnType::Default => {
let msg = "Invalid pallet::type_value, expected return type";
return Err(syn::Error::new(item.sig.span(), msg));
},
};
let mut instances = vec![];
if let Some(usage) = helper::check_type_value_gen(&item.sig.generics, item.sig.span())? {
instances.push(usage);
}
let is_generic = item.sig.generics.type_params().count() > 0;
let where_clause = item.sig.generics.where_clause.clone();
Ok(TypeValueDef {
index,
is_generic,
vis,
ident,
block,
type_,
instances,
where_clause,
})
}
}
@@ -0,0 +1,61 @@
// This file is part of Substrate.
// Copyright (C) 2020 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 syn::spanned::Spanned;
use super::helper;
/// The definition of the pallet validate unsigned implementation.
pub struct ValidateUnsignedDef {
/// The index of validate unsigned item in pallet module.
pub index: usize,
/// A set of usage of instance, must be check for consistency with config.
pub instances: Vec<helper::InstanceUsage>,
}
impl ValidateUnsignedDef {
pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result<Self> {
let item = if let syn::Item::Impl(item) = item {
item
} else {
let msg = "Invalid pallet::validate_unsigned, expected item impl";
return Err(syn::Error::new(item.span(), msg));
};
if item.trait_.is_none() {
let msg = "Invalid pallet::validate_unsigned, expected impl<..> ValidateUnsigned for \
Pallet<..>";
return Err(syn::Error::new(item.span(), msg));
}
if let Some(last) = item.trait_.as_ref().unwrap().1.segments.last() {
if last.ident != "ValidateUnsigned" {
let msg = "Invalid pallet::validate_unsigned, expected trait ValidateUnsigned";
return Err(syn::Error::new(last.span(), msg));
}
} else {
let msg = "Invalid pallet::validate_unsigned, expected impl<..> ValidateUnsigned for \
Pallet<..>";
return Err(syn::Error::new(item.span(), msg));
}
let mut instances = vec![];
instances.push(helper::check_pallet_struct_usage(&item.self_ty)?);
instances.push(helper::check_impl_gen(&item.generics, item.impl_token.span())?);
Ok(ValidateUnsignedDef { index, instances })
}
}
@@ -52,7 +52,7 @@ pub fn crate_to_pallet_version(input: proc_macro::TokenStream) -> Result<TokenSt
let patch_version = get_version::<u8>("CARGO_PKG_VERSION_PATCH")
.map_err(|_| create_error("Patch version needs to fit into `u8`"))?;
let crate_ = generate_crate_access_2018()?;
let crate_ = generate_crate_access_2018("frame-support")?;
Ok(quote::quote! {
#crate_::traits::PalletVersion {
@@ -21,8 +21,8 @@
use proc_macro2::{TokenStream, Span};
use quote::quote;
use super::DeclStorageDefExt;
use genesis_config_def::GenesisConfigDef;
use builder_def::BuilderDef;
pub use genesis_config_def::GenesisConfigDef;
pub use builder_def::BuilderDef;
mod genesis_config_def;
mod builder_def;
@@ -24,6 +24,9 @@ mod getters;
mod metadata;
mod instance_trait;
mod genesis_config;
mod print_pallet_upgrade;
pub(crate) use instance_trait::INHERENT_INSTANCE_NAME;
use quote::quote;
use frame_support_procedural_tools::{
@@ -397,6 +400,8 @@ pub fn decl_storage_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStr
let def = syn::parse_macro_input!(input as DeclStorageDef);
let def_ext = DeclStorageDefExt::from(def);
print_pallet_upgrade::maybe_print_pallet_upgrade(&def_ext);
let hidden_crate_name = def_ext.hidden_crate.as_ref().map(|i| i.to_string())
.unwrap_or_else(|| "decl_storage".to_string());
@@ -0,0 +1,387 @@
use super::StorageLineTypeDef;
use quote::ToTokens;
use frame_support_procedural_tools::clean_type_string;
/// Environment variable that tells us to print pallet upgrade helper.
const PRINT_PALLET_UPGRADE: &str = "PRINT_PALLET_UPGRADE";
fn check_print_pallet_upgrade() -> bool {
std::env::var(PRINT_PALLET_UPGRADE).is_ok()
}
/// Convert visibilty as now objects are defined in a module.
fn convert_vis(vis: &syn::Visibility) -> &'static str{
match vis {
syn::Visibility::Inherited => "pub(super)",
syn::Visibility::Public(_) => "pub",
_ => "/* TODO_VISIBILITY */",
}
}
/// fn to convert to token stream then string using display and then call clean_type_string on it.
fn to_cleaned_string(t: impl quote::ToTokens) -> String {
clean_type_string(&format!("{}", t.into_token_stream()))
}
/// Print an incomplete upgrade from decl_storage macro to new pallet attribute.
pub fn maybe_print_pallet_upgrade(def: &super::DeclStorageDefExt) {
if !check_print_pallet_upgrade() {
return
}
let scrate = &quote::quote!(frame_support);
let config_gen = if def.optional_instance.is_some() {
"<I: 'static = ()>"
} else {
Default::default()
};
let impl_gen = if def.optional_instance.is_some() {
"<T: Config<I>, I: 'static>"
} else {
"<T: Config>"
};
let decl_gen = if def.optional_instance.is_some() {
"<T, I=()>"
} else {
"<T>"
};
let full_decl_gen = if def.optional_instance.is_some() {
"<T: Config<I>, I: 'static = ()>"
} else {
"<T: Config>"
};
let use_gen = if def.optional_instance.is_some() {
"<T, I>"
} else {
"<T>"
};
let use_gen_tuple = if def.optional_instance.is_some() {
"<(T, I)>"
} else {
"<T>"
};
let mut genesis_config = String::new();
let mut genesis_build = String::new();
let genesis_config_builder_def = super::genesis_config::BuilderDef::from_def(scrate, def);
if !genesis_config_builder_def.blocks.is_empty() {
let genesis_config_def = match super::genesis_config::GenesisConfigDef::from_def(def) {
Ok(g) => g,
Err(err) => {
println!("Could not print upgrade due compile error: {:?}", err);
return
},
};
let genesis_config_impl_gen = if genesis_config_def.is_generic {
impl_gen.clone()
} else {
Default::default()
};
let genesis_config_use_gen = if genesis_config_def.is_generic {
use_gen.clone()
} else {
Default::default()
};
let genesis_config_decl_gen = if genesis_config_def.is_generic {
if def.optional_instance.is_some() {
"<T: Config<I>, I: 'static = ()>"
} else {
"<T: Config>"
}
} else {
Default::default()
};
let mut genesis_config_decl_fields = String::new();
let mut genesis_config_default_fields = String::new();
for field in &genesis_config_def.fields {
genesis_config_decl_fields.push_str(&format!("
{attrs}pub {name}: {typ},",
attrs = field.attrs.iter()
.fold(String::new(), |res, attr| {
format!("{}#[{}]
",
res, attr.to_token_stream())
}),
name = field.name,
typ = to_cleaned_string(&field.typ),
));
genesis_config_default_fields.push_str(&format!("
{name}: {default},",
name = field.name,
default = to_cleaned_string(&field.default),
));
}
genesis_config = format!("
#[pallet::genesis_config]
pub struct GenesisConfig{genesis_config_decl_gen}
// TODO_MAYBE_WHERE_CLAUSE
{{{genesis_config_decl_fields}
}}
#[cfg(feature = \"std\")]
impl{genesis_config_impl_gen} Default for GenesisConfig{genesis_config_use_gen}
// TODO_MAYBE_WHERE_CLAUSE
{{
fn default() -> Self {{
Self {{{genesis_config_default_fields}
}}
}}
}}",
genesis_config_decl_gen = genesis_config_decl_gen,
genesis_config_decl_fields = genesis_config_decl_fields,
genesis_config_impl_gen = genesis_config_impl_gen,
genesis_config_default_fields = genesis_config_default_fields,
genesis_config_use_gen = genesis_config_use_gen,
);
let genesis_config_build = genesis_config_builder_def.blocks.iter()
.fold(String::new(), |res, block| {
format!("{}
{}",
res,
to_cleaned_string(block),
)
});
genesis_build = format!("
#[pallet::genesis_build]
impl{impl_gen} GenesisBuild{use_gen} for GenesisConfig{genesis_config_use_gen}
// TODO_MAYBE_WHERE_CLAUSE
{{
fn build(&self) {{{genesis_config_build}
}}
}}",
impl_gen = impl_gen,
use_gen = use_gen,
genesis_config_use_gen = genesis_config_use_gen,
genesis_config_build = genesis_config_build,
);
}
let mut storages = String::new();
for line in &def.storage_lines {
let storage_vis = convert_vis(&line.visibility);
let getter = if let Some(getter) = &line.getter {
format!("
#[pallet::getter(fn {getter})]",
getter = getter
)
} else {
Default::default()
};
let value_type = &line.value_type;
let default_value_type_value = line.default_value.as_ref()
.map(|default_expr| {
format!("
#[pallet::type_value]
{storage_vis} fn DefaultFor{name} /* TODO_MAYBE_GENERICS */ () -> {value_type} {{
{default_expr}
}}
",
name = line.name,
storage_vis = storage_vis,
value_type = to_cleaned_string(&line.value_type),
default_expr = to_cleaned_string(&default_expr),
)
})
.unwrap_or_else(|| String::new());
let comma_query_kind = if line.is_option {
if line.default_value.is_some() {
", OptionQuery"
} else {
Default::default()
}
} else {
", ValueQuery"
};
let comma_default_value_getter_name = line.default_value.as_ref()
.map(|_| format!(", DefaultFor{}", line.name))
.unwrap_or_else(|| String::new());
let typ = match &line.storage_type {
StorageLineTypeDef::Map(map) => {
format!("StorageMap<_, {hasher}, {key}, {value_type}{comma_query_kind}\
{comma_default_value_getter_name}>",
hasher = &map.hasher.to_storage_hasher_struct(),
key = to_cleaned_string(&map.key),
value_type = to_cleaned_string(&value_type),
comma_query_kind = comma_query_kind,
comma_default_value_getter_name = comma_default_value_getter_name,
)
},
StorageLineTypeDef::DoubleMap(double_map) => {
format!("StorageDoubleMap<_, {hasher1}, {key1}, {hasher2}, {key2}, {value_type}\
{comma_query_kind}{comma_default_value_getter_name}>",
hasher1 = double_map.hasher1.to_storage_hasher_struct(),
key1 = to_cleaned_string(&double_map.key1),
hasher2 = double_map.hasher2.to_storage_hasher_struct(),
key2 = to_cleaned_string(&double_map.key2),
value_type = to_cleaned_string(&value_type),
comma_query_kind = comma_query_kind,
comma_default_value_getter_name = comma_default_value_getter_name,
)
},
StorageLineTypeDef::Simple(_) => {
format!("StorageValue<_, {value_type}{comma_query_kind}\
{comma_default_value_getter_name}>",
value_type = to_cleaned_string(&value_type),
comma_query_kind = comma_query_kind,
comma_default_value_getter_name = comma_default_value_getter_name,
)
},
};
let additional_comment = if line.is_option && line.default_value.is_some() {
" // TODO: This type of storage is no longer supported: `OptionQuery` cannot be used \
alongside a not-none value on empty storage. Please use `ValueQuery` instead."
} else {
""
};
storages.push_str(&format!("
{default_value_type_value}{doc}
#[pallet::storage]{getter}
{storage_vis} type {name}{full_decl_gen} = {typ};{additional_comment}",
default_value_type_value = default_value_type_value,
getter = getter,
storage_vis = storage_vis,
name = line.name,
full_decl_gen = full_decl_gen,
typ = typ,
additional_comment = additional_comment,
doc = line.doc_attrs.iter()
.fold(String::new(), |mut res, attr| {
if let syn::Meta::NameValue(name_value) = attr {
if name_value.path.is_ident("doc") {
if let syn::Lit::Str(string) = &name_value.lit {
res = format!("{}
///{}",
res,
string.value(),
);
}
}
}
res
}),
));
}
let deprecated_instance_stuff = if def.optional_instance.is_some() {
"
/// Old name for default instance generated by decl_storage.
#[deprecated(note=\"use `()` instead\")]
pub type DefaultInstance = ();
/// Old name for instance trait used by old macros.
#[deprecated(note=\"use `'static` instead\")]
pub trait Instance: 'static {}
impl<I: 'static> Instance for I {}"
} else {
""
};
println!("
// Template for pallet upgrade for {pallet_name}
pub use pallet::*;
#[frame_support::pallet]
pub mod pallet {{
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
use super::*;
#[pallet::config]
pub trait Config{config_gen}: frame_system::Config
// TODO_MAYBE_ADDITIONAL_BOUNDS_AND_WHERE_CLAUSE
{{
// TODO_ASSOCIATED_TYPE_AND_CONSTANTS
}}
{deprecated_instance_stuff}
#[pallet::pallet]
#[pallet::generate_store({store_vis} trait Store)]
pub struct Pallet{decl_gen}(PhantomData{use_gen_tuple});
/// Old name for pallet.
#[deprecated(note=\"use `Pallet` instead\")]
pub type Module{decl_gen} = Pallet{use_gen};
#[pallet::interface]
impl{impl_gen} Hooks<BlockNumberFor<T>> for Pallet{use_gen}
// TODO_MAYBE_WHERE_CLAUSE
{{
// TODO_ON_FINALIZE
// TODO_ON_INITIALIZE
// TODO_ON_RUNTIME_UPGRADE
// TODO_INTEGRITY_TEST
// TODO_OFFCHAIN_WORKER
}}
#[pallet::call]
impl{impl_gen} Pallet{use_gen}
// TODO_MAYBE_WHERE_CLAUSE
{{
// TODO_UPGRADE_DISPATCHABLES
}}
#[pallet::inherent]
// TODO_INHERENT
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
// TODO_EVENT
// TODO_REMOVE_IF_NO_EVENT
/// Old name generated by `decl_event`.
#[deprecated(note=\"use `Event` instead\")]
pub type RawEvent /* TODO_PUT_EVENT_GENERICS */ = Event /* TODO_PUT_EVENT_GENERICS */;
#[pallet::error]
// TODO_ERROR
#[pallet::origin]
// TODO_ORIGIN
#[pallet::validate_unsigned]
// TODO_VALIDATE_UNSIGNED
{storages}
{genesis_config}
{genesis_build}
}}",
config_gen = config_gen,
store_vis = convert_vis(&def.visibility),
impl_gen = impl_gen,
use_gen = use_gen,
use_gen_tuple = use_gen_tuple,
decl_gen = decl_gen,
storages = storages,
genesis_config = genesis_config,
genesis_build = genesis_build,
pallet_name = def.crate_name,
deprecated_instance_stuff = deprecated_instance_stuff,
);
}
@@ -23,7 +23,7 @@ use frame_support_procedural_tools::generate_crate_access_2018;
pub fn transactional(_attr: TokenStream, input: TokenStream) -> Result<TokenStream> {
let ItemFn { attrs, vis, sig, block } = syn::parse(input)?;
let crate_ = generate_crate_access_2018()?;
let crate_ = generate_crate_access_2018("frame-support")?;
let output = quote! {
#(#attrs)*
#vis #sig {
@@ -45,7 +45,7 @@ pub fn transactional(_attr: TokenStream, input: TokenStream) -> Result<TokenStre
pub fn require_transactional(_attr: TokenStream, input: TokenStream) -> Result<TokenStream> {
let ItemFn { attrs, vis, sig, block } = syn::parse(input)?;
let crate_ = generate_crate_access_2018()?;
let crate_ = generate_crate_access_2018("frame-support")?;
let output = quote! {
#(#attrs)*
#vis #sig {