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,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,
);
}