Make hooks and call attributes optional in pallet macro (#8853)

* Make #[pallet::hooks] optional

* Make #[pallet::call] optional

* Remove unused imports

* Update UI test expectations

* Update UI test expectations

* Remove unnecessary HooksDef::empty method

* Remove unnecessary CallDef::empty method

* Clarify what would happen when no call or hooks are specified in a pallet
This commit is contained in:
Keith Yeung
2021-05-20 12:31:56 -07:00
committed by GitHub
parent 029b8a1d07
commit e5954cf863
9 changed files with 90 additions and 57 deletions
@@ -22,30 +22,40 @@ 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 (span, where_clause, methods, docs) = match def.call.as_ref() {
Some(call) => {
let span = call.attr_span;
let where_clause = call.where_clause.clone();
let methods = call.methods.clone();
let docs = call.docs.clone();
(span, where_clause, methods, docs)
}
None => (def.pallet_struct.attr_span, None, Vec::new(), Vec::new()),
};
let frame_support = &def.frame_support;
let frame_system = &def.frame_system;
let type_impl_gen = &def.type_impl_generics(def.call.attr_span);
let type_decl_bounded_gen = &def.type_decl_bounded_generics(def.call.attr_span);
let type_use_gen = &def.type_use_generics(def.call.attr_span);
let call_ident = syn::Ident::new("Call", def.call.attr_span);
let type_impl_gen = &def.type_impl_generics(span);
let type_decl_bounded_gen = &def.type_decl_bounded_generics(span);
let type_use_gen = &def.type_use_generics(span);
let call_ident = syn::Ident::new("Call", span);
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_name = methods.iter().map(|method| &method.name).collect::<Vec<_>>();
let fn_weight = def.call.methods.iter().map(|method| &method.weight);
let fn_weight = methods.iter().map(|method| &method.weight);
let fn_doc = def.call.methods.iter().map(|method| &method.docs).collect::<Vec<_>>();
let fn_doc = methods.iter().map(|method| &method.docs).collect::<Vec<_>>();
let args_name = def.call.methods.iter()
let args_name = methods.iter()
.map(|method| method.args.iter().map(|(_, name, _)| name.clone()).collect::<Vec<_>>())
.collect::<Vec<_>>();
let args_type = def.call.methods.iter()
let args_type = 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| {
let args_compact_attr = methods.iter().map(|method| {
method.args.iter()
.map(|(is_compact, _, type_)| {
if *is_compact {
@@ -57,7 +67,7 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream {
.collect::<Vec<_>>()
});
let args_metadata_type = def.call.methods.iter().map(|method| {
let args_metadata_type = methods.iter().map(|method| {
method.args.iter()
.map(|(is_compact, _, type_)| {
let final_type = if *is_compact {
@@ -73,13 +83,13 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream {
let default_docs = [syn::parse_quote!(
r"Contains one variant per dispatchable that can be called by an extrinsic."
)];
let docs = if def.call.docs.is_empty() {
let docs = if docs.is_empty() {
&default_docs[..]
} else {
&def.call.docs[..]
&docs[..]
};
quote::quote_spanned!(def.call.attr_span =>
quote::quote_spanned!(span =>
#( #[doc = #docs] )*
#[derive(
#frame_support::RuntimeDebugNoBound,
@@ -19,13 +19,21 @@ use crate::pallet::Def;
/// * implement the individual traits using the Hooks trait
pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream {
let (where_clause, span, has_runtime_upgrade) = match def.hooks.as_ref() {
Some(hooks) => {
let where_clause = hooks.where_clause.clone();
let span = hooks.attr_span;
let has_runtime_upgrade = hooks.has_runtime_upgrade;
(where_clause, span, has_runtime_upgrade)
},
None => (None, def.pallet_struct.attr_span, false),
};
let frame_support = &def.frame_support;
let type_impl_gen = &def.type_impl_generics(def.hooks.attr_span);
let type_use_gen = &def.type_use_generics(def.hooks.attr_span);
let type_impl_gen = &def.type_impl_generics(span);
let type_use_gen = &def.type_use_generics(span);
let pallet_ident = &def.pallet_struct.pallet;
let where_clause = &def.hooks.where_clause;
let frame_system = &def.frame_system;
let has_runtime_upgrade = def.hooks.has_runtime_upgrade;
let log_runtime_upgrade = if has_runtime_upgrade {
// a migration is defined here.
@@ -49,7 +57,20 @@ pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream {
}
};
quote::quote_spanned!(def.hooks.attr_span =>
let hooks_impl = if def.hooks.is_none() {
let frame_system = &def.frame_system;
quote::quote!{
impl<#type_impl_gen>
#frame_support::traits::Hooks<<T as #frame_system::Config>::BlockNumber>
for Pallet<#type_use_gen> {}
}
} else {
proc_macro2::TokenStream::new()
};
quote::quote_spanned!(span =>
#hooks_impl
impl<#type_impl_gen>
#frame_support::traits::OnFinalize<<T as #frame_system::Config>::BlockNumber>
for #pallet_ident<#type_use_gen> #where_clause
@@ -45,6 +45,7 @@ pub struct CallDef {
pub docs: Vec<syn::Lit>,
}
#[derive(Clone)]
/// Definition of dispatchable typically: `#[weight...] fn foo(origin .., param1: ...) -> ..`
pub struct CallVariantDef {
/// Function name.
@@ -45,8 +45,8 @@ pub struct Def {
pub item: syn::ItemMod,
pub config: config::ConfigDef,
pub pallet_struct: pallet_struct::PalletStructDef,
pub hooks: hooks::HooksDef,
pub call: call::CallDef,
pub hooks: Option<hooks::HooksDef>,
pub call: Option<call::CallDef>,
pub storages: Vec<storage::StorageDef>,
pub error: Option<error::ErrorDef>,
pub event: Option<event::EventDef>,
@@ -156,9 +156,8 @@ impl Def {
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]"))?,
hooks,
call,
extra_constants,
genesis_config,
genesis_build,
@@ -206,10 +205,14 @@ impl Def {
/// 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(call) = &self.call {
instances.extend_from_slice(&call.instances[..]);
}
if let Some(hooks) = &self.hooks {
instances.extend_from_slice(&hooks.instances[..]);
}
if let Some(event) = &self.event {
instances.extend_from_slice(&event.instances[..]);
}
+16 -2
View File
@@ -1393,7 +1393,7 @@ pub mod pallet_prelude {
/// [`traits::StorageInfoTrait`] for each storage in the implementation of
/// [`traits::StorageInfoTrait`] for the pallet.
///
/// # Hooks: `#[pallet::hooks]` mandatory
/// # Hooks: `#[pallet::hooks]` optional
///
/// Implementation of `Hooks` on `Pallet` allowing to define some specific pallet logic.
///
@@ -1407,6 +1407,13 @@ pub mod pallet_prelude {
/// `Hooks<BlockNumberFor<T>>` (they are defined in preludes), for the type `Pallet<T>`
/// and with an optional where clause.
///
/// If no `#[pallet::hooks]` exists, then a default implementation corresponding to the following
/// code is automatically generated:
/// ```ignore
/// #[pallet::hooks]
/// impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
/// ```
///
/// ### Macro expansion:
///
/// The macro implements the traits `OnInitialize`, `OnIdle`, `OnFinalize`, `OnRuntimeUpgrade`,
@@ -1418,7 +1425,7 @@ pub mod pallet_prelude {
/// NOTE: The macro also adds some tracing logic when implementing the above traits. The following
/// hooks emit traces: `on_initialize`, `on_finalize` and `on_runtime_upgrade`.
///
/// # Call: `#[pallet::call]` mandatory
/// # Call: `#[pallet::call]` optional
///
/// Implementation of pallet dispatchables.
///
@@ -1450,6 +1457,13 @@ pub mod pallet_prelude {
/// All arguments must implement `Debug`, `PartialEq`, `Eq`, `Decode`, `Encode`, `Clone`. For ease
/// of use, bound the trait `Member` available in frame_support::pallet_prelude.
///
/// If no `#[pallet::call]` exists, then a default implementation corresponding to the following
/// code is automatically generated:
/// ```ignore
/// #[pallet::call]
/// impl<T: Config> Pallet<T> {}
/// ```
///
/// **WARNING**: modifying dispatchables, changing their order, removing some must be done with
/// care. Indeed this will change the outer runtime call type (which is an enum with one variant
/// per pallet), this outer runtime call can be stored on-chain (e.g. in pallet-scheduler).
@@ -406,20 +406,11 @@ pub mod pallet2 {
/// Test that the supertrait check works when we pass some parameter to the `frame_system::Config`.
#[frame_support::pallet]
pub mod pallet3 {
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
#[pallet::config]
pub trait Config: frame_system::Config<Origin = ()> {}
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
#[pallet::call]
impl<T: Config> Pallet<T> {}
}
frame_support::parameter_types!(
@@ -209,7 +209,6 @@ pub mod pallet {
#[frame_support::pallet]
pub mod pallet2 {
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
#[pallet::config]
pub trait Config<I: 'static = ()>: frame_system::Config {
@@ -220,12 +219,6 @@ pub mod pallet2 {
#[pallet::generate_store(pub(crate) trait Store)]
pub struct Pallet<T, I = ()>(PhantomData<(T, I)>);
#[pallet::hooks]
impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {}
#[pallet::call]
impl<T: Config<I>, I: 'static> Pallet<T, I> {}
#[pallet::event]
pub enum Event<T: Config<I>, I: 'static = ()> {
/// Something
@@ -1,3 +1,9 @@
error: Invalid generic declaration, trait is defined with instance but generic use none
--> $DIR/inconsistent_instance_1.rs:10:20
|
10 | pub struct Pallet<T>(core::marker::PhantomData<T>);
| ^
error: Invalid generic declaration, trait is defined with instance but generic use none
--> $DIR/inconsistent_instance_1.rs:16:7
|
@@ -10,12 +16,6 @@ error: Invalid generic declaration, trait is defined with instance but generic u
16 | impl<T: Config> Pallet<T> {}
| ^^^^^^
error: Invalid generic declaration, trait is defined with instance but generic use none
--> $DIR/inconsistent_instance_1.rs:10:20
|
10 | pub struct Pallet<T>(core::marker::PhantomData<T>);
| ^
error: Invalid generic declaration, trait is defined with instance but generic use none
--> $DIR/inconsistent_instance_1.rs:13:47
|
@@ -1,3 +1,9 @@
error: Invalid generic declaration, trait is defined without instance but generic use some
--> $DIR/inconsistent_instance_2.rs:10:20
|
10 | pub struct Pallet<T, I = ()>(core::marker::PhantomData<(T, I)>);
| ^
error: Invalid generic declaration, trait is defined without instance but generic use some
--> $DIR/inconsistent_instance_2.rs:16:7
|
@@ -10,12 +16,6 @@ error: Invalid generic declaration, trait is defined without instance but generi
16 | impl<T: Config<I>, I: 'static> Pallet<T, I> {}
| ^^^^^^
error: Invalid generic declaration, trait is defined without instance but generic use some
--> $DIR/inconsistent_instance_2.rs:10:20
|
10 | pub struct Pallet<T, I = ()>(core::marker::PhantomData<(T, I)>);
| ^
error: Invalid generic declaration, trait is defined without instance but generic use some
--> $DIR/inconsistent_instance_2.rs:13:62
|