diff --git a/substrate/frame/support/procedural/src/pallet/expand/call.rs b/substrate/frame/support/procedural/src/pallet/expand/call.rs index bd7676c49a..a3ac7ecc5f 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/call.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/call.rs @@ -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::>(); + let fn_name = methods.iter().map(|method| &method.name).collect::>(); - 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::>(); + let fn_doc = methods.iter().map(|method| &method.docs).collect::>(); - let args_name = def.call.methods.iter() + let args_name = methods.iter() .map(|method| method.args.iter().map(|(_, name, _)| name.clone()).collect::>()) .collect::>(); - let args_type = def.call.methods.iter() + let args_type = methods.iter() .map(|method| method.args.iter().map(|(_, _, type_)| type_.clone()).collect::>()) .collect::>(); - 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::>() }); - 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, diff --git a/substrate/frame/support/procedural/src/pallet/expand/hooks.rs b/substrate/frame/support/procedural/src/pallet/expand/hooks.rs index 2d12d5ecf9..6e21c892d8 100644 --- a/substrate/frame/support/procedural/src/pallet/expand/hooks.rs +++ b/substrate/frame/support/procedural/src/pallet/expand/hooks.rs @@ -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<::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<::BlockNumber> for #pallet_ident<#type_use_gen> #where_clause diff --git a/substrate/frame/support/procedural/src/pallet/parse/call.rs b/substrate/frame/support/procedural/src/pallet/parse/call.rs index 23406aeb23..c2e6dce225 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/call.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/call.rs @@ -45,6 +45,7 @@ pub struct CallDef { pub docs: Vec, } +#[derive(Clone)] /// Definition of dispatchable typically: `#[weight...] fn foo(origin .., param1: ...) -> ..` pub struct CallVariantDef { /// Function name. diff --git a/substrate/frame/support/procedural/src/pallet/parse/mod.rs b/substrate/frame/support/procedural/src/pallet/parse/mod.rs index 39a40fc148..2f378c52e8 100644 --- a/substrate/frame/support/procedural/src/pallet/parse/mod.rs +++ b/substrate/frame/support/procedural/src/pallet/parse/mod.rs @@ -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, + pub call: Option, pub storages: Vec, pub error: Option, pub event: Option, @@ -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[..]); } diff --git a/substrate/frame/support/src/lib.rs b/substrate/frame/support/src/lib.rs index cc7bd2126c..d1874b65b6 100644 --- a/substrate/frame/support/src/lib.rs +++ b/substrate/frame/support/src/lib.rs @@ -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>` (they are defined in preludes), for the type `Pallet` /// 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 Hooks> for Pallet {} +/// ``` +/// /// ### 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 Pallet {} +/// ``` +/// /// **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). diff --git a/substrate/frame/support/test/tests/pallet.rs b/substrate/frame/support/test/tests/pallet.rs index cc3d83f472..7478da189d 100644 --- a/substrate/frame/support/test/tests/pallet.rs +++ b/substrate/frame/support/test/tests/pallet.rs @@ -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 {} #[pallet::pallet] pub struct Pallet(_); - - #[pallet::hooks] - impl Hooks> for Pallet {} - - #[pallet::call] - impl Pallet {} } frame_support::parameter_types!( diff --git a/substrate/frame/support/test/tests/pallet_instance.rs b/substrate/frame/support/test/tests/pallet_instance.rs index 7d6c6983b0..846a96a237 100644 --- a/substrate/frame/support/test/tests/pallet_instance.rs +++ b/substrate/frame/support/test/tests/pallet_instance.rs @@ -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: frame_system::Config { @@ -220,12 +219,6 @@ pub mod pallet2 { #[pallet::generate_store(pub(crate) trait Store)] pub struct Pallet(PhantomData<(T, I)>); - #[pallet::hooks] - impl, I: 'static> Hooks> for Pallet {} - - #[pallet::call] - impl, I: 'static> Pallet {} - #[pallet::event] pub enum Event, I: 'static = ()> { /// Something diff --git a/substrate/frame/support/test/tests/pallet_ui/inconsistent_instance_1.stderr b/substrate/frame/support/test/tests/pallet_ui/inconsistent_instance_1.stderr index 352c21013c..06c7941a0b 100644 --- a/substrate/frame/support/test/tests/pallet_ui/inconsistent_instance_1.stderr +++ b/substrate/frame/support/test/tests/pallet_ui/inconsistent_instance_1.stderr @@ -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(core::marker::PhantomData); + | ^ + 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 Pallet {} | ^^^^^^ -error: Invalid generic declaration, trait is defined with instance but generic use none - --> $DIR/inconsistent_instance_1.rs:10:20 - | -10 | pub struct Pallet(core::marker::PhantomData); - | ^ - error: Invalid generic declaration, trait is defined with instance but generic use none --> $DIR/inconsistent_instance_1.rs:13:47 | diff --git a/substrate/frame/support/test/tests/pallet_ui/inconsistent_instance_2.stderr b/substrate/frame/support/test/tests/pallet_ui/inconsistent_instance_2.stderr index 9f5d3c740c..9d61f2976b 100644 --- a/substrate/frame/support/test/tests/pallet_ui/inconsistent_instance_2.stderr +++ b/substrate/frame/support/test/tests/pallet_ui/inconsistent_instance_2.stderr @@ -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(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, I: 'static> Pallet {} | ^^^^^^ -error: Invalid generic declaration, trait is defined without instance but generic use some - --> $DIR/inconsistent_instance_2.rs:10:20 - | -10 | pub struct Pallet(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 |