diff --git a/Cargo.lock b/Cargo.lock index 35d230f1714f6..8a9aeb0233776 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2712,6 +2712,7 @@ dependencies = [ "impl-trait-for-tuples", "k256", "log", + "macro_magic", "once_cell", "parity-scale-codec", "paste", @@ -2745,6 +2746,7 @@ dependencies = [ "derive-syn-parse", "frame-support-procedural-tools", "itertools", + "macro_magic", "proc-macro-warning", "proc-macro2", "quote", @@ -2793,6 +2795,7 @@ dependencies = [ "sp-state-machine", "sp-std", "sp-version", + "static_assertions", "trybuild", ] @@ -4766,6 +4769,53 @@ dependencies = [ "libc", ] +[[package]] +name = "macro_magic" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f3703e9d296140171bbd6a6fa560e6059a35299ee3ce8c5bd646afa0c3992" +dependencies = [ + "macro_magic_core", + "macro_magic_macros", + "quote", + "syn 2.0.16", +] + +[[package]] +name = "macro_magic_core" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e060da9399c1535b3f190084d7c6121bb3355b70c610110cff3f4978526f54e2" +dependencies = [ + "derive-syn-parse", + "macro_magic_core_macros", + "proc-macro2", + "quote", + "syn 2.0.16", +] + +[[package]] +name = "macro_magic_core_macros" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fb9865e03b9641e57448b9743915eadd0f642e41a41c4d9eaa8b5b861d887b0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.16", +] + +[[package]] +name = "macro_magic_macros" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68cf083ba5ba151ce3230a7c687cb5c890498adae59c85f3be7e8e741a6c9f65" +dependencies = [ + "macro_magic_core", + "quote", + "syn 2.0.16", +] + [[package]] name = "maplit" version = "1.0.2" @@ -6295,6 +6345,20 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-default-config-example" +version = "4.0.0-dev" +dependencies = [ + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-democracy" version = "4.0.0-dev" diff --git a/Cargo.toml b/Cargo.toml index 5c413e7d09860..eed878428d387 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -109,6 +109,7 @@ members = [ "frame/examples/basic", "frame/examples/offchain-worker", "frame/examples/dev-mode", + "frame/examples/default-config", "frame/executive", "frame/nis", "frame/grandpa", diff --git a/frame/examples/default-config/Cargo.toml b/frame/examples/default-config/Cargo.toml new file mode 100644 index 0000000000000..1f2dde1a4de22 --- /dev/null +++ b/frame/examples/default-config/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "pallet-default-config-example" +version = "4.0.0-dev" +authors = ["Parity Technologies "] +edition = "2021" +license = "MIT-0" +homepage = "https://substrate.io" +repository = "https://github.com/paritytech/substrate/" +description = "FRAME example pallet demonstrating derive_impl / default_config in action" +readme = "README.md" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } +log = { version = "0.4.17", default-features = false } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } +frame-support = { default-features = false, path = "../../support" } +frame-system = { default-features = false, path = "../../system" } + +sp-io = { default-features = false, path = "../../../primitives/io" } +sp-runtime = { default-features = false, path = "../../../primitives/runtime" } +sp-std = { default-features = false, path = "../../../primitives/std" } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-support/std", + "frame-system/std", + "log/std", + "scale-info/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", +] +try-runtime = ["frame-support/try-runtime"] diff --git a/frame/examples/default-config/README.md b/frame/examples/default-config/README.md new file mode 100644 index 0000000000000..b1a67a5c16e55 --- /dev/null +++ b/frame/examples/default-config/README.md @@ -0,0 +1,8 @@ +# Default Config Example Pallet + +An example pallet demonstrating the ability to derive default testing configs via +`#[derive_impl]` and `#[pallet::config(with_default)]`. + +Run `cargo doc --package pallet-default-config-example --open` to view this pallet's documentation. + +License: MIT-0 diff --git a/frame/examples/default-config/src/lib.rs b/frame/examples/default-config/src/lib.rs new file mode 100644 index 0000000000000..adb2469e92fd9 --- /dev/null +++ b/frame/examples/default-config/src/lib.rs @@ -0,0 +1,210 @@ +// This file is part of Substrate. + +// Copyright (C) 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. + +//! # Default Config Pallet Example +//! +//! A simple example of a FRAME pallet that utilizes [`frame_support::derive_impl`] to demonstrate +//! the simpler way to implement `Config` trait of pallets. This example only showcases this in a +//! `mock.rs` environment, but the same applies to a real runtime as well. +//! +//! See the source code of [`tests`] for a real examples. +//! +//! Study the following types: +//! +//! - [`pallet::DefaultConfig`], and how it differs from [`pallet::Config`]. +//! - [`pallet::config_preludes::TestDefaultConfig`] and how it implements +//! [`pallet::DefaultConfig`]. +//! - Notice how [`pallet::DefaultConfig`] is independent of [`frame_system::Config`]. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[frame_support::pallet] +pub mod pallet { + use frame_support::pallet_prelude::*; + + /// This pallet is annotated to have a default config. This will auto-generate + /// [`DefaultConfig`]. + /// + /// It will be an identical, but won't have anything that is `#[pallet::no_default]`. + #[pallet::config(with_default)] + pub trait Config: frame_system::Config { + /// The overarching event type. This is coming from the runtime, and cannot have a default. + /// In general, `Runtime*`-oriented types cannot have a sensible default. + #[pallet::no_default] // optional. `RuntimeEvent` is automatically excluded as well. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + /// An input parameter to this pallet. This value can have a default, because it is not + /// reliant on `frame_system::Config` or the overarching runtime in any way. + type WithDefaultValue: Get; + + /// Same as [`Config::WithDefaultValue`], but we don't intend to define a default for this + /// in our tests below. + type OverwrittenDefaultValue: Get; + + /// An input parameter that relies on `::AccountId`. As of + /// now, such types cannot have defaults and need to be annotated as such, iff + /// `#[pallet::config(with_default)]` is enabled: + #[pallet::no_default] + type CannotHaveDefault: Get; + + /// Something that is a normal type, with default. + type WithDefaultType; + + /// Same as [`Config::WithDefaultType`], but we don't intend to define a default for this + /// in our tests below. + type OverwrittenDefaultType; + } + + /// Container for different types that implement [`DefaultConfig`]` of this pallet. + pub mod config_preludes { + // This will help use not need to disambiguate anything when using `derive_impl`. + use super::*; + + /// A type providing default configurations for this pallet in testing environment. + pub struct TestDefaultConfig; + #[frame_support::register_default_impl(TestDefaultConfig)] + impl DefaultConfig for TestDefaultConfig { + type WithDefaultValue = frame_support::traits::ConstU32<42>; + type OverwrittenDefaultValue = frame_support::traits::ConstU32<42>; + + type WithDefaultType = u32; + type OverwrittenDefaultType = u32; + } + + /// A type providing default configurations for this pallet in a parachain environment. + pub struct ParachainDefaultConfig; + #[frame_support::register_default_impl(ParachainDefaultConfig)] + impl DefaultConfig for ParachainDefaultConfig { + type WithDefaultValue = frame_support::traits::ConstU32<66>; + type OverwrittenDefaultValue = frame_support::traits::ConstU32<66>; + type WithDefaultType = u32; + type OverwrittenDefaultType = u32; + } + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::event] + pub enum Event {} +} + +#[cfg(any(test, doc))] +pub mod tests { + use super::*; + + use frame_support::macro_magic::use_attr; + // Because `derive_impl` is a [macro_magic](https://crates.io/crates/macro_magic) attribute + // macro, [`#[use_attr]`](`frame_support::macro_magic::use_attr`) must be attached to any use + // statement that brings it into scope. + #[use_attr] + use frame_support::derive_impl; + + use super::pallet as pallet_default_config_example; + + type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; + type Block = frame_system::mocking::MockBlock; + + frame_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system, + DefaultPallet: pallet_default_config_example, + } + ); + + #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] + impl frame_system::Config for Test { + // these items are defined by frame-system as `no_default`, so we must specify them here. + // Note that these are types that actually rely on the outer runtime, and can't sensibly + // have an _independent_ default. + type BaseCallFilter = frame_support::traits::Everything; + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type PalletInfo = PalletInfo; + type OnSetCode = (); + + // all of this is coming from `frame_system::config_preludes::TestDefaultConfig`. + + // type Index = u32; + // type BlockNumber = u32; + // type Header = sp_runtime::generic::Header; + // type Hash = sp_core::hash::H256; + // type Hashing = sp_runtime::traits::BlakeTwo256; + // type AccountId = u64; + // type Lookup = sp_runtime::traits::IdentityLookup; + // type BlockHashCount = frame_support::traits::ConstU32<10>; + // type MaxConsumers = frame_support::traits::ConstU32<16>; + // type AccountData = (); + // type OnNewAccount = (); + // type OnKilledAccount = (); + // type SystemWeightInfo = (); + // type SS58Prefix = (); + // type Version = (); + // type BlockWeights = (); + // type BlockLength = (); + // type DbWeight = (); + + // you could still overwrite any of them if desired. + type SS58Prefix = frame_support::traits::ConstU16<456>; + } + + // Similarly, we use the defaults provided by own crate as well. + use pallet::config_preludes::TestDefaultConfig; + #[derive_impl(TestDefaultConfig as pallet::DefaultConfig)] + impl crate::pallet::Config for Test { + // These two both cannot have defaults. + type RuntimeEvent = RuntimeEvent; + // Note that the default account-id type in + // `frame_system::config_preludes::TestDefaultConfig` is `u64`. + type CannotHaveDefault = frame_support::traits::ConstU64<1>; + + type OverwrittenDefaultValue = frame_support::traits::ConstU32<678>; + type OverwrittenDefaultType = u128; + } + + #[test] + fn it_works() { + use frame_support::traits::Get; + use pallet::{Config, DefaultConfig}; + + // assert one of the value types that is not overwritten. + assert_eq!( + <::WithDefaultValue as Get>::get(), + <::WithDefaultValue as Get>::get() + ); + + // assert one of the value types that is overwritten. + assert_eq!(<::OverwrittenDefaultValue as Get>::get(), 678u32); + + // assert one of the types that is not overwritten. + assert_eq!( + std::any::TypeId::of::<::WithDefaultType>(), + std::any::TypeId::of::<::WithDefaultType>() + ); + + // assert one of the types that is overwritten. + assert_eq!( + std::any::TypeId::of::<::OverwrittenDefaultType>(), + std::any::TypeId::of::() + ) + } +} diff --git a/frame/support/Cargo.toml b/frame/support/Cargo.toml index 4a433016e4c58..0f22e6d9e1a7d 100644 --- a/frame/support/Cargo.toml +++ b/frame/support/Cargo.toml @@ -29,6 +29,7 @@ sp-staking = { version = "4.0.0-dev", default-features = false, path = "../../pr sp-weights = { version = "5.0.0", default-features = false, path = "../../primitives/weights" } sp-debug-derive = { default-features = false, path = "../../primitives/debug-derive" } tt-call = "1.0.8" +macro_magic = "0.3.3" frame-support-procedural = { version = "4.0.0-dev", default-features = false, path = "./procedural" } paste = "1.0" once_cell = { version = "1", default-features = false, optional = true } diff --git a/frame/support/procedural/Cargo.toml b/frame/support/procedural/Cargo.toml index b5f6e7309e2a1..9372947eccdd6 100644 --- a/frame/support/procedural/Cargo.toml +++ b/frame/support/procedural/Cargo.toml @@ -24,6 +24,7 @@ quote = "1.0.26" syn = { version = "2.0.16", features = ["full"] } frame-support-procedural-tools = { version = "4.0.0-dev", path = "./tools" } proc-macro-warning = { version = "0.4.1", default-features = false } +macro_magic = { version = "0.3.3", features = ["proc_support"] } [features] default = ["std"] diff --git a/frame/support/procedural/src/construct_runtime/mod.rs b/frame/support/procedural/src/construct_runtime/mod.rs index 1af44fc00a0ec..958a10e7242dc 100644 --- a/frame/support/procedural/src/construct_runtime/mod.rs +++ b/frame/support/procedural/src/construct_runtime/mod.rs @@ -24,7 +24,7 @@ //! `::{Call, ...}` or implicitly. //! //! In case a pallet defines its parts implicitly, then the pallet must provide the -//! `tt_default_parts` macro. `construct_rutime` will generate some code which utilizes `tt_call` +//! `tt_default_parts` macro. `construct_runtime` will generate some code which utilizes `tt_call` //! to call the `tt_default_parts` macro of the pallet. `tt_default_parts` will then return the //! default pallet parts as input tokens to the `match_and_replace` macro, which ultimately //! generates a call to `construct_runtime` again, this time with all the pallet parts explicitly diff --git a/frame/support/procedural/src/derive_impl.rs b/frame/support/procedural/src/derive_impl.rs new file mode 100644 index 0000000000000..5ea44c35bb646 --- /dev/null +++ b/frame/support/procedural/src/derive_impl.rs @@ -0,0 +1,175 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Implementation of the `derive_impl` attribute macro. + +use derive_syn_parse::Parse; +use macro_magic::mm_core::ForeignPath; +use proc_macro2::TokenStream as TokenStream2; +use quote::{quote, ToTokens}; +use std::collections::HashSet; +use syn::{parse2, parse_quote, spanned::Spanned, Ident, ImplItem, ItemImpl, Path, Result, Token}; + +#[derive(Parse)] +pub struct DeriveImplAttrArgs { + pub default_impl_path: Path, + _as: Option, + #[parse_if(_as.is_some())] + pub disambiguation_path: Option, +} + +impl ForeignPath for DeriveImplAttrArgs { + fn foreign_path(&self) -> &Path { + &self.default_impl_path + } +} + +impl ToTokens for DeriveImplAttrArgs { + fn to_tokens(&self, tokens: &mut TokenStream2) { + tokens.extend(self.default_impl_path.to_token_stream()); + tokens.extend(self._as.to_token_stream()); + tokens.extend(self.disambiguation_path.to_token_stream()); + } +} + +/// Gets the [`Ident`] representation of the given [`ImplItem`], if one exists. Otherwise +/// returns [`None`]. +/// +/// Used by [`combine_impls`] to determine whether we can compare [`ImplItem`]s by [`Ident`] +/// or not. +fn impl_item_ident(impl_item: &ImplItem) -> Option<&Ident> { + match impl_item { + ImplItem::Const(item) => Some(&item.ident), + ImplItem::Fn(item) => Some(&item.sig.ident), + ImplItem::Type(item) => Some(&item.ident), + ImplItem::Macro(item) => item.mac.path.get_ident(), + _ => None, + } +} + +/// The real meat behind `derive_impl`. Takes in a `local_impl`, which is the impl for which we +/// want to implement defaults (i.e. the one the attribute macro is attached to), and a +/// `foreign_impl`, which is the impl containing the defaults we want to use, and returns an +/// [`ItemImpl`] containing the final generated impl. +/// +/// This process has the following caveats: +/// * Colliding items that have an ident are not copied into `local_impl` +/// * Uncolliding items that have an ident are copied into `local_impl` but are qualified as `type +/// #ident = <#default_impl_path as #disambiguation_path>::#ident;` +/// * Items that lack an ident are de-duplicated so only unique items that lack an ident are copied +/// into `local_impl`. Items that lack an ident and also exist verbatim in `local_impl` are not +/// copied over. +fn combine_impls( + local_impl: ItemImpl, + foreign_impl: ItemImpl, + default_impl_path: Path, + disambiguation_path: Path, +) -> ItemImpl { + let (existing_local_keys, existing_unsupported_items): (HashSet, HashSet) = + local_impl + .items + .iter() + .cloned() + .partition(|impl_item| impl_item_ident(impl_item).is_some()); + let existing_local_keys: HashSet = existing_local_keys + .into_iter() + .filter_map(|item| impl_item_ident(&item).cloned()) + .collect(); + let mut final_impl = local_impl; + let extended_items = foreign_impl.items.into_iter().filter_map(|item| { + if let Some(ident) = impl_item_ident(&item) { + if existing_local_keys.contains(&ident) { + // do not copy colliding items that have an ident + return None + } + if matches!(item, ImplItem::Type(_)) { + // modify and insert uncolliding type items + let modified_item: ImplItem = parse_quote! { + type #ident = <#default_impl_path as #disambiguation_path>::#ident; + }; + return Some(modified_item) + } + // copy uncolliding non-type items that have an ident + Some(item) + } else { + // do not copy colliding items that lack an ident + (!existing_unsupported_items.contains(&item)) + // copy uncolliding items without an ident verbatim + .then_some(item) + } + }); + final_impl.items.extend(extended_items); + final_impl +} + +/// Internal implementation behind [`#[derive_impl(..)]`](`macro@crate::derive_impl`). +/// +/// `default_impl_path`: the module path of the external `impl` statement whose tokens we are +/// importing via `macro_magic` +/// +/// `foreign_tokens`: the tokens for the external `impl` statement +/// +/// `local_tokens`: the tokens for the local `impl` statement this attribute is attached to +/// +/// `disambiguation_path`: the module path of the external trait we will use to qualify +/// defaults imported from the external `impl` statement +pub fn derive_impl( + default_impl_path: TokenStream2, + foreign_tokens: TokenStream2, + local_tokens: TokenStream2, + disambiguation_path: Option, +) -> Result { + let local_impl = parse2::(local_tokens)?; + let foreign_impl = parse2::(foreign_tokens)?; + let default_impl_path = parse2::(default_impl_path)?; + + // have disambiguation_path default to the item being impl'd in the foreign impl if we + // don't specify an `as [disambiguation_path]` in the macro attr + let disambiguation_path = match (disambiguation_path, foreign_impl.clone().trait_) { + (Some(disambiguation_path), _) => disambiguation_path, + (None, Some((_, foreign_impl_path, _))) => foreign_impl_path, + _ => + return Err(syn::Error::new( + foreign_impl.span(), + "Impl statement must have a defined type being implemented \ + for a defined type such as `impl A for B`", + )), + }; + + // generate the combined impl + let combined_impl = + combine_impls(local_impl, foreign_impl, default_impl_path, disambiguation_path); + + Ok(quote!(#combined_impl)) +} + +#[test] +fn test_derive_impl_attr_args_parsing() { + parse2::(quote!( + some::path::TestDefaultConfig as some::path::DefaultConfig + )) + .unwrap(); + parse2::(quote!( + frame_system::prelude::testing::TestDefaultConfig as DefaultConfig + )) + .unwrap(); + parse2::(quote!(Something as some::path::DefaultConfig)).unwrap(); + parse2::(quote!(Something as DefaultConfig)).unwrap(); + parse2::(quote!(DefaultConfig)).unwrap(); + assert!(parse2::(quote!()).is_err()); + assert!(parse2::(quote!(Config Config)).is_err()); +} diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index 25df8410b02d1..84a8b37f92e06 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -25,6 +25,7 @@ mod construct_runtime; mod crate_version; mod debug_no_bound; mod default_no_bound; +mod derive_impl; mod dummy_part_checker; mod key_prefix; mod match_and_insert; @@ -36,10 +37,12 @@ mod storage_alias; mod transactional; mod tt_macro; +use macro_magic::import_tokens_attr; use proc_macro::TokenStream; -use quote::quote; +use quote::{quote, ToTokens}; use std::{cell::RefCell, str::FromStr}; pub(crate) use storage::INHERENT_INSTANCE_NAME; +use syn::{parse_macro_input, ItemImpl}; thread_local! { /// A global counter, can be used to generate a relatively unique identifier. @@ -777,6 +780,274 @@ pub fn storage_alias(_: TokenStream, input: TokenStream) -> TokenStream { .into() } +/// This attribute can be used to derive a full implementation of a trait based on a local partial +/// impl and an external impl containing defaults that can be overriden in the local impl. +/// +/// For a full end-to-end example, see [below](#use-case-auto-derive-test-pallet-config-traits). +/// +/// # Usage +/// +/// The attribute should be attached to an impl block (strictly speaking a `syn::ItemImpl`) for +/// which we want to inject defaults in the event of missing trait items in the block. +/// +/// The attribute minimally takes a single `default_impl_path` argument, which should be the module +/// path to an impl registered via [`#[register_default_impl]`](`macro@register_default_impl`) that +/// contains the default trait items we want to potentially inject, with the general form: +/// +/// ```ignore +/// #[derive_impl(default_impl_path)] +/// impl SomeTrait for SomeStruct { +/// ... +/// } +/// ``` +/// +/// Optionally, a `disambiguation_path` can be specified as follows by providing `as path::here` +/// after the `default_impl_path`: +/// +/// ```ignore +/// #[derive_impl(default_impl_path as disambiguation_path)] +/// impl SomeTrait for SomeStruct { +/// ... +/// } +/// ``` +/// +/// The `disambiguation_path`, if specified, should be the path to a trait that will be used to +/// qualify all default entries that are injected into the local impl. For example if your +/// `default_impl_path` is `some::path::TestTraitImpl` and your `disambiguation_path` is +/// `another::path::DefaultTrait`, any items injected into the local impl will be qualified as +/// `::specific_trait_item`. +/// +/// If you omit the `as disambiguation_path` portion, the `disambiguation_path` will internally +/// default to `A` from the `impl A for B` part of the default impl. This is useful for scenarios +/// where all of the relevant types are already in scope via `use` statements. +/// +/// Conversely, the `default_impl_path` argument is required and cannot be omitted. +/// +/// You can also make use of `#[pallet::no_default]` on specific items in your default impl that you +/// want to ensure will not be copied over but that you nonetheless want to use locally in the +/// context of the foreign impl and the pallet (or context) in which it is defined. +/// +/// ## Use-Case Example: Auto-Derive Test Pallet Config Traits +/// +/// The `#[derive_imp(..)]` attribute can be used to derive a test pallet `Config` based on an +/// existing pallet `Config` that has been marked with +/// [`#[pallet::config(with_default)]`](`macro@config`) (which under the hood, generates a +/// `DefaultConfig` trait in the pallet in which the macro was invoked). +/// +/// In this case, the `#[derive_impl(..)]` attribute should be attached to an `impl` block that +/// implements a compatible `Config` such as `frame_system::Config` for a test/mock runtime, and +/// should receive as its first argument the path to a `DefaultConfig` impl that has been registered +/// via [`#[register_default_impl]`](`macro@register_default_impl`), and as its second argument, the +/// path to the auto-generated `DefaultConfig` for the existing pallet `Config` we want to base our +/// test config off of. +/// +/// The following is what the `basic` example pallet would look like with a default testing config: +/// +/// ```ignore +/// #[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::pallet::DefaultConfig)] +/// impl frame_system::Config for Test { +/// // These are all defined by system as mandatory. +/// type BaseCallFilter = frame_support::traits::Everything; +/// type RuntimeEvent = RuntimeEvent; +/// type RuntimeCall = RuntimeCall; +/// type RuntimeOrigin = RuntimeOrigin; +/// type OnSetCode = (); +/// type PalletInfo = PalletInfo; +/// type Header = Header; +/// // We decide to override this one. +/// type AccountData = pallet_balances::AccountData; +/// } +/// ``` +/// +/// where `TestDefaultConfig` was defined and registered as follows: +/// +/// ```ignore +/// pub struct TestDefaultConfig; +/// +/// #[register_default_impl(TestDefaultConfig)] +/// impl DefaultConfig for TestDefaultConfig { +/// type Version = (); +/// type BlockWeights = (); +/// type BlockLength = (); +/// type DbWeight = (); +/// type Index = u64; +/// type BlockNumber = u64; +/// type Hash = sp_core::hash::H256; +/// type Hashing = sp_runtime::traits::BlakeTwo256; +/// type AccountId = AccountId; +/// type Lookup = IdentityLookup; +/// type BlockHashCount = frame_support::traits::ConstU64<10>; +/// type AccountData = u32; +/// type OnNewAccount = (); +/// type OnKilledAccount = (); +/// type SystemWeightInfo = (); +/// type SS58Prefix = (); +/// type MaxConsumers = frame_support::traits::ConstU32<16>; +/// } +/// ``` +/// +/// The above call to `derive_impl` would expand to roughly the following: +/// +/// ```ignore +/// impl frame_system::Config for Test { +/// use frame_system::config_preludes::TestDefaultConfig; +/// use frame_system::pallet::DefaultConfig; +/// +/// type BaseCallFilter = frame_support::traits::Everything; +/// type RuntimeEvent = RuntimeEvent; +/// type RuntimeCall = RuntimeCall; +/// type RuntimeOrigin = RuntimeOrigin; +/// type OnSetCode = (); +/// type PalletInfo = PalletInfo; +/// type Header = Header; +/// type AccountData = pallet_balances::AccountData; +/// type Version = ::Version; +/// type BlockWeights = ::BlockWeights; +/// type BlockLength = ::BlockLength; +/// type DbWeight = ::DbWeight; +/// type Index = ::Index; +/// type BlockNumber = ::BlockNumber; +/// type Hash = ::Hash; +/// type Hashing = ::Hashing; +/// type AccountId = ::AccountId; +/// type Lookup = ::Lookup; +/// type BlockHashCount = ::BlockHashCount; +/// type OnNewAccount = ::OnNewAccount; +/// type OnKilledAccount = ::OnKilledAccount; +/// type SystemWeightInfo = ::SystemWeightInfo; +/// type SS58Prefix = ::SS58Prefix; +/// type MaxConsumers = ::MaxConsumers; +/// } +/// ``` +/// +/// You can then use the resulting `Test` config in test scenarios. +/// +/// Note that items that are _not_ present in our local `DefaultConfig` are automatically copied +/// from the foreign trait (in this case `TestDefaultConfig`) into the local trait impl (in this +/// case `Test`), unless the trait item in the local trait impl is marked with +/// [`#[pallet::no_default]`](`macro@no_default`), in which case it cannot be overridden, and any +/// attempts to do so will result in a compiler error. +/// +/// See `frame/examples/default-config/tests.rs` for a runnable end-to-end example pallet that makes +/// use of `derive_impl` to derive its testing config. +/// +/// See [here](`macro@config`) for more information and caveats about the auto-generated +/// `DefaultConfig` trait. +/// +/// ## Optional Conventions +/// +/// Note that as an optional convention, we encourage creating a `config_preludes` module inside of +/// your pallet. This is the convention we follow for `frame_system`'s `TestDefaultConfig` which, as +/// shown above, is located at `frame_system::config_preludes::TestDefaultConfig`. This is just a +/// suggested convention -- there is nothing in the code that expects modules with these names to be +/// in place, so there is no imperative to follow this pattern unless desired. +/// +/// In `config_preludes`, you can place types named like: +/// +/// * `TestDefaultConfig` +/// * `ParachainDefaultConfig` +/// * `SolochainDefaultConfig` +/// +/// Signifying in which context they can be used. +/// +/// # Advanced Usage +/// +/// ## Importing & Re-Exporting +/// +/// Since `#[derive_impl(..)]` is a +/// [`macro_magic`](https://docs.rs/macro_magic/latest/macro_magic/)-based attribute macro, special +/// care must be taken when importing and re-exporting it. Glob imports will work properly, such as +/// `use frame_support::*` to bring `derive_impl` into scope, however any other use statements +/// involving `derive_impl` should have +/// [`#[macro_magic::use_attr]`](https://docs.rs/macro_magic/latest/macro_magic/attr.use_attr.html) +/// attached or your use statement will fail to fully bring the macro into scope. +/// +/// This brings `derive_impl` into scope in the current context: +/// ```ignore +/// #[use_attr] +/// use frame_support::derive_impl; +/// ``` +/// +/// This brings `derive_impl` into scope and publicly re-exports it from the current context: +/// ```ignore +/// #[use_attr] +/// pub use frame_support::derive_impl; +/// ``` +/// +/// ## Expansion +/// +/// The `#[derive_impl(default_impl_path as disambiguation_path)]` attribute will expand to the +/// local impl, with any extra items from the foreign impl that aren't present in the local impl +/// also included. In the case of a colliding trait item, the version of the item that exists in the +/// local impl will be retained. All imported items are qualified by the `disambiguation_path`, as +/// discussed above. +/// +/// ## Handling of Unnamed Trait Items +/// +/// Items that lack a `syn::Ident` for whatever reason are first checked to see if they exist, +/// verbatim, in the local/destination trait before they are copied over, so you should not need to +/// worry about collisions between identical unnamed items. +#[import_tokens_attr(frame_support::macro_magic)] +#[with_custom_parsing(derive_impl::DeriveImplAttrArgs)] +#[proc_macro_attribute] +pub fn derive_impl(attrs: TokenStream, input: TokenStream) -> TokenStream { + let custom_attrs = parse_macro_input!(__custom_tokens as derive_impl::DeriveImplAttrArgs); + derive_impl::derive_impl( + __source_path.into(), + attrs.into(), + input.into(), + custom_attrs.disambiguation_path, + ) + .unwrap_or_else(|r| r.into_compile_error()) + .into() +} + +/// The optional attribute `#[pallet::no_default]` can be attached to trait items within a +/// `Config` trait impl that has [`#[pallet::config(with_default)]`](`macro@config`) attached. +/// +/// Attaching this attribute to a trait item ensures that that trait item will not be used as a +/// default with the [`#[derive_impl(..)]`](`macro@derive_impl`) attribute macro. +#[proc_macro_attribute] +pub fn no_default(_: TokenStream, _: TokenStream) -> TokenStream { + pallet_macro_stub() +} + +/// Attach this attribute to an impl statement that you want to use with +/// [`#[derive_impl(..)]`](`macro@derive_impl`). +/// +/// You must also provide an identifier/name as the attribute's argument. This is the name you +/// must provide to [`#[derive_impl(..)]`](`macro@derive_impl`) when you import this impl via +/// the `default_impl_path` argument. This name should be unique at the crate-level. +/// +/// ## Example +/// +/// ```ignore +/// pub struct ExampleTestDefaultConfig; +/// +/// #[register_default_impl(ExampleTestDefaultConfig)] +/// impl DefaultConfig for ExampleTestDefaultConfig { +/// type Version = (); +/// type BlockWeights = (); +/// type BlockLength = (); +/// ... +/// type SS58Prefix = (); +/// type MaxConsumers = frame_support::traits::ConstU32<16>; +/// } +/// ``` +/// This macro acts as a thin wrapper around macro_magic's `#[export_tokens]`. See the docs +/// [here](https://docs.rs/macro_magic/latest/macro_magic/attr.export_tokens.html) for more info. +#[proc_macro_attribute] +pub fn register_default_impl(attrs: TokenStream, tokens: TokenStream) -> TokenStream { + // ensure this is a impl statement + let item_impl = syn::parse_macro_input!(tokens as ItemImpl); + + // internally wrap macro_magic's `#[export_tokens]` macro + match macro_magic::mm_core::export_tokens_internal(attrs, item_impl.to_token_stream(), true) { + Ok(tokens) => tokens.into(), + Err(err) => err.to_compile_error().into(), + } +} + /// Used internally to decorate pallet attribute macro stubs when they are erroneously used /// outside of a pallet module fn pallet_macro_stub() -> TokenStream { @@ -809,6 +1080,52 @@ fn pallet_macro_stub() -> TokenStream { /// /// [`pallet::event`](`macro@event`) must be present if `RuntimeEvent` exists as a config item /// in your `#[pallet::config]`. +/// +/// ## Optional: `with_default` +/// +/// An optional `with_default` argument may also be specified. Doing so will automatically +/// generate a `DefaultConfig` trait inside your pallet which is suitable for use with +/// [`[#[derive_impl(..)]`](`macro@derive_impl`) to derive a default testing config: +/// +/// ```ignore +/// #[pallet::config(with_default)] +/// pub trait Config: frame_system::Config { +/// type RuntimeEvent: Parameter +/// + Member +/// + From> +/// + Debug +/// + IsType<::RuntimeEvent>; +/// +/// #[pallet::no_default] +/// type BaseCallFilter: Contains; +/// // ... +/// } +/// ``` +/// +/// As shown above, you may also attach the [`#[pallet::no_default]`](`macro@no_default`) +/// attribute to specify that a particular trait item _cannot_ be used as a default when a test +/// `Config` is derived using the [`#[derive_impl(..)]`](`macro@derive_impl`) attribute macro. +/// This will cause that particular trait item to simply not appear in default testing configs +/// based on this config (the trait item will not be included in `DefaultConfig`). +/// +/// ### `DefaultConfig` Caveats +/// +/// The auto-generated `DefaultConfig` trait: +/// - is always a _subset_ of your pallet's `Config` trait. +/// - can only contain items that don't rely on externalities, such as `frame_system::Config`. +/// +/// Trait items that _do_ rely on externalities should be marked with +/// [`#[pallet::no_default]`](`macro@no_default`) +/// +/// Consequently: +/// - Any items that rely on externalities _must_ be marked with +/// [`#[pallet::no_default]`](`macro@no_default`) or your trait will fail to compile when used +/// with [`derive_impl`](`macro@derive_impl`). +/// - Items marked with [`#[pallet::no_default]`](`macro@no_default`) are entirely excluded from the +/// `DefaultConfig` trait, and therefore any impl of `DefaultConfig` doesn't need to implement +/// such items. +/// +/// For more information, see [`macro@derive_impl`]. #[proc_macro_attribute] pub fn config(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() diff --git a/frame/support/procedural/src/pallet/expand/config.rs b/frame/support/procedural/src/pallet/expand/config.rs index dd7471aa767cb..4ae0afdbbc8e3 100644 --- a/frame/support/procedural/src/pallet/expand/config.rs +++ b/frame/support/procedural/src/pallet/expand/config.rs @@ -16,14 +16,17 @@ // limitations under the License. use crate::pallet::Def; +use proc_macro2::TokenStream; +use quote::quote; +use syn::{parse_quote, Item}; /// /// * Generate default rust doc -pub fn expand_config(def: &mut Def) -> proc_macro2::TokenStream { +pub fn expand_config(def: &mut Def) -> TokenStream { let config = &def.config; let config_item = { let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[config.index]; - if let syn::Item::Trait(item) = item { + if let Item::Trait(item) = item { item } else { unreachable!("Checked by config parser") @@ -32,8 +35,9 @@ pub fn expand_config(def: &mut Def) -> proc_macro2::TokenStream { config_item.attrs.insert( 0, - syn::parse_quote!( - #[doc = r"Configuration trait of this pallet. + parse_quote!( + #[doc = r" +Configuration trait of this pallet. The main purpose of this trait is to act as an interface between this pallet and the runtime in which it is embedded in. A type, function, or constant in this trait is essentially left to be @@ -44,5 +48,25 @@ Consequently, a runtime that wants to include this pallet must implement this tr ), ); - Default::default() + // we only emit `DefaultConfig` if there are trait items, so an empty `DefaultConfig` is + // impossible consequently + if config.default_sub_trait.len() > 0 { + let trait_items = &config.default_sub_trait; + quote!( + /// Based on [`Config`]. Auto-generated by + /// [`#[pallet::config(with_default)]`](`frame_support::pallet_macros::config`). + /// Can be used in tandem with + /// [`#[register_default_config]`](`frame_support::register_default_config`) and + /// [`#[derive_impl]`](`frame_support::derive_impl`) to derive test config traits + /// based on existing pallet config traits in a safe and developer-friendly way. + /// + /// See [here](`frame_support::pallet_macros::config`) for more information and caveats about + /// the auto-generated `DefaultConfig` trait and how it is generated. + pub trait DefaultConfig { + #(#trait_items)* + } + ) + } else { + Default::default() + } } diff --git a/frame/support/procedural/src/pallet/parse/config.rs b/frame/support/procedural/src/pallet/parse/config.rs index 7b52c1664ba45..6a3693a3ab0f2 100644 --- a/frame/support/procedural/src/pallet/parse/config.rs +++ b/frame/support/procedural/src/pallet/parse/config.rs @@ -18,7 +18,7 @@ use super::helper; use frame_support_procedural_tools::get_doc_literals; use quote::ToTokens; -use syn::spanned::Spanned; +use syn::{spanned::Spanned, token, Token}; /// List of additional token to be used for parsing. mod keyword { @@ -27,12 +27,14 @@ mod keyword { syn::custom_keyword!(T); syn::custom_keyword!(I); syn::custom_keyword!(config); + syn::custom_keyword!(pallet); syn::custom_keyword!(IsType); syn::custom_keyword!(RuntimeEvent); syn::custom_keyword!(Event); - syn::custom_keyword!(constant); syn::custom_keyword!(frame_system); syn::custom_keyword!(disable_frame_system_supertrait_check); + syn::custom_keyword!(no_default); + syn::custom_keyword!(constant); } /// Input definition for the pallet config. @@ -52,6 +54,12 @@ pub struct ConfigDef { pub where_clause: Option, /// The span of the pallet::config attribute. pub attr_span: proc_macro2::Span, + /// Whether a default sub-trait should be generated. + /// + /// Contains default sub-trait items (instantiated by `#[pallet::config(with_default)]`). + /// Vec will be empty if `#[pallet::config(with_default)]` is not specified or if there are + /// no trait items + pub default_sub_trait: Vec, } /// Input definition for a constant in pallet config. @@ -123,40 +131,28 @@ impl syn::parse::Parse for DisableFrameSystemSupertraitCheck { } } -/// Parse for `#[pallet::constant]` -pub struct TypeAttrConst { - pound_token: syn::Token![#], - bracket_token: syn::token::Bracket, - pallet_ident: syn::Ident, - path_sep_token: syn::token::PathSep, - constant_keyword: keyword::constant, +/// Parsing for the `typ` portion of `PalletAttr` +#[derive(derive_syn_parse::Parse, PartialEq, Eq)] +pub enum PalletAttrType { + #[peek(keyword::no_default, name = "no_default")] + NoDefault(keyword::no_default), + #[peek(keyword::constant, name = "constant")] + Constant(keyword::constant), } -impl syn::parse::Parse for TypeAttrConst { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let pound_token = input.parse::()?; - let content; - let bracket_token = syn::bracketed!(content in input); - let pallet_ident = content.parse::()?; - let path_sep_token = content.parse::()?; - let constant_keyword = content.parse::()?; - - Ok(Self { pound_token, bracket_token, pallet_ident, path_sep_token, constant_keyword }) - } +/// Parsing for `#[pallet::X]` +#[derive(derive_syn_parse::Parse)] +pub struct PalletAttr { + _pound: Token![#], + #[bracket] + _bracket: token::Bracket, + #[inside(_bracket)] + _pallet: keyword::pallet, + #[prefix(Token![::] in _bracket)] + #[inside(_bracket)] + typ: PalletAttrType, } -impl ToTokens for TypeAttrConst { - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - self.pound_token.to_tokens(tokens); - self.bracket_token.surround(tokens, |tokens| { - self.pallet_ident.to_tokens(tokens); - self.path_sep_token.to_tokens(tokens); - self.constant_keyword.to_tokens(tokens); - }) - } -} - -/// Parse for `$ident::Config` pub struct ConfigBoundParse(syn::Ident); impl syn::parse::Parse for ConfigBoundParse { @@ -307,6 +303,7 @@ impl ConfigDef { attr_span: proc_macro2::Span, index: usize, item: &mut syn::Item, + enable_default: bool, ) -> syn::Result { let item = if let syn::Item::Trait(item) = item { item @@ -344,38 +341,59 @@ impl ConfigDef { let mut has_event_type = false; let mut consts_metadata = vec![]; + let mut default_sub_trait = 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 = helper::take_item_pallet_attrs(trait_item)?; + let is_event = check_event_type(frame_system, trait_item, has_instance)?; + has_event_type = has_event_type || is_event; - 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)) - } + let mut already_no_default = false; + let mut already_constant = false; - if type_attrs_const.len() == 1 { - match trait_item { - syn::TraitItem::Type(ref type_) => { - let constant = ConstMetadataDef::try_from(type_)?; - consts_metadata.push(constant); + while let Ok(Some(pallet_attr)) = + helper::take_first_item_pallet_attr::(trait_item) + { + match (pallet_attr.typ, &trait_item) { + (PalletAttrType::Constant(_), syn::TraitItem::Type(ref typ)) => { + if already_constant { + return Err(syn::Error::new( + pallet_attr._bracket.span.join(), + "Duplicate #[pallet::constant] attribute not allowed.", + )) + } + already_constant = true; + consts_metadata.push(ConstMetadataDef::try_from(typ)?); }, - _ => { - let msg = - "Invalid pallet::constant in pallet::config, expected type trait \ - item"; - return Err(syn::Error::new(trait_item.span(), msg)) + (PalletAttrType::Constant(_), _) => + return Err(syn::Error::new( + trait_item.span(), + "Invalid #[pallet::constant] in #[pallet::config], expected type item", + )), + (PalletAttrType::NoDefault(_), _) => { + if !enable_default { + return Err(syn::Error::new( + pallet_attr._bracket.span.join(), + "`#[pallet:no_default]` can only be used if `#[pallet::config(with_default)]` \ + has been specified" + )) + } + if already_no_default { + return Err(syn::Error::new( + pallet_attr._bracket.span.join(), + "Duplicate #[pallet::no_default] attribute not allowed.", + )) + } + already_no_default = true; }, } } + + if !already_no_default && !is_event && enable_default { + default_sub_trait.push(trait_item.clone()); + } } let attr: Option = helper::take_first_item_pallet_attr(&mut item.attrs)?; - let disable_system_supertrait_check = attr.is_some(); let has_frame_system_supertrait = item.supertraits.iter().any(|s| { @@ -407,6 +425,14 @@ impl ConfigDef { return Err(syn::Error::new(item.span(), msg)) } - Ok(Self { index, has_instance, consts_metadata, has_event_type, where_clause, attr_span }) + Ok(Self { + index, + has_instance, + consts_metadata, + has_event_type, + where_clause, + attr_span, + default_sub_trait, + }) } } diff --git a/frame/support/procedural/src/pallet/parse/helper.rs b/frame/support/procedural/src/pallet/parse/helper.rs index 3cdbfb1f591d9..9319248771642 100644 --- a/frame/support/procedural/src/pallet/parse/helper.rs +++ b/frame/support/procedural/src/pallet/parse/helper.rs @@ -47,7 +47,9 @@ pub trait MutItemAttrs { } /// Take the first pallet attribute (e.g. attribute like `#[pallet..]`) and decode it to `Attr` -pub fn take_first_item_pallet_attr(item: &mut impl MutItemAttrs) -> syn::Result> +pub(crate) fn take_first_item_pallet_attr( + item: &mut impl MutItemAttrs, +) -> syn::Result> where Attr: syn::parse::Parse, { @@ -64,7 +66,7 @@ where } /// Take all the pallet attributes (e.g. attribute like `#[pallet..]`) and decode them to `Attr` -pub fn take_item_pallet_attrs(item: &mut impl MutItemAttrs) -> syn::Result> +pub(crate) fn take_item_pallet_attrs(item: &mut impl MutItemAttrs) -> syn::Result> where Attr: syn::parse::Parse, { diff --git a/frame/support/procedural/src/pallet/parse/mod.rs b/frame/support/procedural/src/pallet/parse/mod.rs index 770cba68c1aad..4a4602964a996 100644 --- a/frame/support/procedural/src/pallet/parse/mod.rs +++ b/frame/support/procedural/src/pallet/parse/mod.rs @@ -100,8 +100,14 @@ impl Def { let pallet_attr: Option = helper::take_first_item_pallet_attr(item)?; match pallet_attr { - Some(PalletAttr::Config(span)) if config.is_none() => - config = Some(config::ConfigDef::try_from(&frame_system, span, index, item)?), + Some(PalletAttr::Config(span, with_default)) if config.is_none() => + config = Some(config::ConfigDef::try_from( + &frame_system, + span, + index, + item, + with_default, + )?), Some(PalletAttr::Pallet(span)) if pallet_struct.is_none() => { let p = pallet_struct::PalletStructDef::try_from(span, index, item)?; pallet_struct = Some(p); @@ -405,6 +411,7 @@ mod keyword { syn::custom_keyword!(weight); syn::custom_keyword!(event); syn::custom_keyword!(config); + syn::custom_keyword!(with_default); syn::custom_keyword!(hooks); syn::custom_keyword!(inherent); syn::custom_keyword!(error); @@ -423,7 +430,7 @@ mod keyword { /// Parse attributes for item in pallet module /// syntax must be `pallet::` (e.g. `#[pallet::config]`) enum PalletAttr { - Config(proc_macro2::Span), + Config(proc_macro2::Span, bool), Pallet(proc_macro2::Span), Hooks(proc_macro2::Span), /// A `#[pallet::call]` with optional attributes to specialize the behaviour. @@ -480,7 +487,7 @@ enum PalletAttr { impl PalletAttr { fn span(&self) -> proc_macro2::Span { match self { - Self::Config(span) => *span, + Self::Config(span, _) => *span, Self::Pallet(span) => *span, Self::Hooks(span) => *span, Self::RuntimeCall(_, span) => *span, @@ -509,7 +516,14 @@ impl syn::parse::Parse for PalletAttr { let lookahead = content.lookahead1(); if lookahead.peek(keyword::config) { - Ok(PalletAttr::Config(content.parse::()?.span())) + let span = content.parse::()?.span(); + let with_default = content.peek(syn::token::Paren); + if with_default { + let inside_config; + let _paren = syn::parenthesized!(inside_config in content); + inside_config.parse::()?; + } + Ok(PalletAttr::Config(span, with_default)) } else if lookahead.peek(keyword::pallet) { Ok(PalletAttr::Pallet(content.parse::()?.span())) } else if lookahead.peek(keyword::hooks) { diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 80629b9425cef..de87b59a0b943 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -211,6 +211,9 @@ impl TypeId for PalletId { /// ``` pub use frame_support_procedural::storage_alias; +#[macro_magic::use_attr] +pub use frame_support_procedural::derive_impl; + /// Create new implementations of the [`Get`](crate::traits::Get) trait. /// /// The so-called parameter type can be created in four different ways: @@ -822,6 +825,10 @@ macro_rules! assert_error_encoded_size { #[doc(hidden)] pub use serde::{Deserialize, Serialize}; +#[doc(hidden)] +#[cfg(not(no_std))] +pub use macro_magic; + #[cfg(test)] pub mod tests { use super::*; @@ -1557,6 +1564,7 @@ pub mod pallet_prelude { }; pub use codec::{Decode, Encode, MaxEncodedLen}; pub use frame_support::pallet_macros::*; + pub use frame_support_procedural::register_default_impl; pub use scale_info::TypeInfo; pub use sp_runtime::{ traits::{MaybeSerializeDeserialize, Member, ValidateUnsigned}, @@ -2893,11 +2901,14 @@ pub mod pallet_macros { pub use frame_support_procedural::{ call_index, compact, composite_enum, config, constant, disable_frame_system_supertrait_check, error, event, extra_constants, generate_deposit, - generate_store, genesis_build, genesis_config, getter, hooks, inherent, origin, storage, - storage_prefix, storage_version, type_value, unbounded, validate_unsigned, weight, + generate_store, genesis_build, genesis_config, getter, hooks, inherent, no_default, origin, + storage, storage_prefix, storage_version, type_value, unbounded, validate_unsigned, weight, whitelist_storage, }; } +#[doc(inline)] +pub use frame_support_procedural::register_default_impl; + // Generate a macro that will enable/disable code based on `std` feature being active. sp_core::generate_feature_enabled_macro!(std_enabled, feature = "std", $); diff --git a/frame/support/test/Cargo.toml b/frame/support/test/Cargo.toml index cb80129b78c71..dc85f9eb1f083 100644 --- a/frame/support/test/Cargo.toml +++ b/frame/support/test/Cargo.toml @@ -12,6 +12,7 @@ repository = "https://github.com/paritytech/substrate/" targets = ["x86_64-unknown-linux-gnu"] [dependencies] +static_assertions = "1.1.0" serde = { version = "1.0.136", default-features = false, features = ["derive"] } codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } diff --git a/frame/support/test/tests/derive_impl_ui.rs b/frame/support/test/tests/derive_impl_ui.rs new file mode 100644 index 0000000000000..ee219d0670aaf --- /dev/null +++ b/frame/support/test/tests/derive_impl_ui.rs @@ -0,0 +1,38 @@ +// This file is part of Substrate. + +// Copyright (C) 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. + +#![cfg(not(feature = "disable-ui-tests"))] +#![cfg(test)] + +#[rustversion::attr(not(stable), ignore)] +#[test] +fn derive_impl_ui() { + // Only run the ui tests when `RUN_UI_TESTS` is set. + if std::env::var("RUN_UI_TESTS").is_err() { + return + } + + // As trybuild is using `cargo check`, we don't need the real WASM binaries. + std::env::set_var("SKIP_WASM_BUILD", "1"); + + // Deny all warnings since we emit warnings as part of a Pallet's UI. + std::env::set_var("RUSTFLAGS", "--deny warnings"); + + let t = trybuild::TestCases::new(); + t.compile_fail("tests/derive_impl_ui/*.rs"); + t.pass("tests/derive_impl_ui/pass/*.rs"); +} diff --git a/frame/support/test/tests/derive_impl_ui/attached_to_non_impl.rs b/frame/support/test/tests/derive_impl_ui/attached_to_non_impl.rs new file mode 100644 index 0000000000000..3b27916933865 --- /dev/null +++ b/frame/support/test/tests/derive_impl_ui/attached_to_non_impl.rs @@ -0,0 +1,41 @@ +use frame_support::*; + +pub trait Animal { + type Locomotion; + type Diet; + type SleepingStrategy; + type Environment; + + fn animal_name() -> &'static str; +} + +pub type RunsOnFourLegs = (usize, usize, usize, usize); +pub type RunsOnTwoLegs = (usize, usize); +pub type Swims = isize; +pub type Diurnal = bool; +pub type Nocturnal = Option; +pub type Omnivore = char; +pub type Land = ((), ()); +pub type Sea = ((), (), ()); +pub type Carnivore = (char, char); + +pub struct FourLeggedAnimal {} + +#[register_default_impl(FourLeggedAnimal)] +impl Animal for FourLeggedAnimal { + type Locomotion = RunsOnFourLegs; + type Diet = Omnivore; + type SleepingStrategy = Diurnal; + type Environment = Land; + + fn animal_name() -> &'static str { + "A Four-Legged Animal" + } +} + +pub struct AcquaticMammal {} + +#[derive_impl(FourLeggedAnimal as Animal)] +struct Something {} + +fn main() {} diff --git a/frame/support/test/tests/derive_impl_ui/attached_to_non_impl.stderr b/frame/support/test/tests/derive_impl_ui/attached_to_non_impl.stderr new file mode 100644 index 0000000000000..94aee2442513a --- /dev/null +++ b/frame/support/test/tests/derive_impl_ui/attached_to_non_impl.stderr @@ -0,0 +1,15 @@ +error: expected `impl` + --> tests/derive_impl_ui/attached_to_non_impl.rs:24:1 + | +24 | / #[register_default_impl(FourLeggedAnimal)] +25 | | impl Animal for FourLeggedAnimal { +26 | | type Locomotion = RunsOnFourLegs; +27 | | type Diet = Omnivore; +... | +37 | | +38 | | #[derive_impl(FourLeggedAnimal as Animal)] + | |_-----------------------------------------^ + | | + | in this procedural macro expansion + | + = note: this error originates in the macro `__import_tokens_attr_derive_impl_inner` which comes from the expansion of the attribute macro `derive_impl` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/frame/support/test/tests/derive_impl_ui/bad_default_impl_path.rs b/frame/support/test/tests/derive_impl_ui/bad_default_impl_path.rs new file mode 100644 index 0000000000000..2badd1830033b --- /dev/null +++ b/frame/support/test/tests/derive_impl_ui/bad_default_impl_path.rs @@ -0,0 +1,48 @@ +use frame_support::*; + +pub trait Animal { + type Locomotion; + type Diet; + type SleepingStrategy; + type Environment; + + fn animal_name() -> &'static str; +} + +pub type RunsOnFourLegs = (usize, usize, usize, usize); +pub type RunsOnTwoLegs = (usize, usize); +pub type Swims = isize; +pub type Diurnal = bool; +pub type Nocturnal = Option; +pub type Omnivore = char; +pub type Land = ((), ()); +pub type Sea = ((), (), ()); +pub type Carnivore = (char, char); + +pub struct FourLeggedAnimal {} + +#[register_default_impl(FourLeggedAnimal)] +impl Animal for FourLeggedAnimal { + type Locomotion = RunsOnFourLegs; + type Diet = Omnivore; + type SleepingStrategy = Diurnal; + type Environment = Land; + + fn animal_name() -> &'static str { + "A Four-Legged Animal" + } +} + +pub struct AcquaticMammal {} + +// Should throw: `error: cannot find macro `__export_tokens_tt_tiger` in this scope` +// +// Note that there is really no better way to clean up this error, tt_call suffers from the +// same downside but this is really the only rough edge when using macro magic. +#[derive_impl(Tiger as Animal)] +impl Animal for AcquaticMammal { + type Locomotion = (Swims, RunsOnFourLegs); + type Environment = (Land, Sea); +} + +fn main() {} diff --git a/frame/support/test/tests/derive_impl_ui/bad_default_impl_path.stderr b/frame/support/test/tests/derive_impl_ui/bad_default_impl_path.stderr new file mode 100644 index 0000000000000..1cac166246276 --- /dev/null +++ b/frame/support/test/tests/derive_impl_ui/bad_default_impl_path.stderr @@ -0,0 +1,7 @@ +error: cannot find macro `__export_tokens_tt_tiger` in this scope + --> tests/derive_impl_ui/bad_default_impl_path.rs:42:1 + | +42 | #[derive_impl(Tiger as Animal)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `frame_support::macro_magic::forward_tokens` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/frame/support/test/tests/derive_impl_ui/bad_disambiguation_path.rs b/frame/support/test/tests/derive_impl_ui/bad_disambiguation_path.rs new file mode 100644 index 0000000000000..adc5df23a759a --- /dev/null +++ b/frame/support/test/tests/derive_impl_ui/bad_disambiguation_path.rs @@ -0,0 +1,44 @@ +use frame_support::*; + +pub trait Animal { + type Locomotion; + type Diet; + type SleepingStrategy; + type Environment; + + fn animal_name() -> &'static str; +} + +pub type RunsOnFourLegs = (usize, usize, usize, usize); +pub type RunsOnTwoLegs = (usize, usize); +pub type Swims = isize; +pub type Diurnal = bool; +pub type Nocturnal = Option; +pub type Omnivore = char; +pub type Land = ((), ()); +pub type Sea = ((), (), ()); +pub type Carnivore = (char, char); + +pub struct FourLeggedAnimal {} + +#[register_default_impl(FourLeggedAnimal)] +impl Animal for FourLeggedAnimal { + type Locomotion = RunsOnFourLegs; + type Diet = Omnivore; + type SleepingStrategy = Diurnal; + type Environment = Land; + + fn animal_name() -> &'static str { + "A Four-Legged Animal" + } +} + +pub struct AcquaticMammal {} + +#[derive_impl(FourLeggedAnimal as Insect)] +impl Animal for AcquaticMammal { + type Locomotion = (Swims, RunsOnFourLegs); + type Environment = (Land, Sea); +} + +fn main() {} diff --git a/frame/support/test/tests/derive_impl_ui/bad_disambiguation_path.stderr b/frame/support/test/tests/derive_impl_ui/bad_disambiguation_path.stderr new file mode 100644 index 0000000000000..dd098d82c1e9b --- /dev/null +++ b/frame/support/test/tests/derive_impl_ui/bad_disambiguation_path.stderr @@ -0,0 +1,16 @@ +error[E0433]: failed to resolve: use of undeclared type `Insect` + --> tests/derive_impl_ui/bad_disambiguation_path.rs:24:1 + | +24 | / #[register_default_impl(FourLeggedAnimal)] +25 | | impl Animal for FourLeggedAnimal { +26 | | type Locomotion = RunsOnFourLegs; +27 | | type Diet = Omnivore; +... | +37 | | +38 | | #[derive_impl(FourLeggedAnimal as Insect)] + | | -----------------------------------------^ + | |_|________________________________________| + | | use of undeclared type `Insect` + | in this procedural macro expansion + | + = note: this error originates in the macro `__import_tokens_attr_derive_impl_inner` which comes from the expansion of the attribute macro `derive_impl` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/frame/support/test/tests/derive_impl_ui/missing_disambiguation_path.rs b/frame/support/test/tests/derive_impl_ui/missing_disambiguation_path.rs new file mode 100644 index 0000000000000..21f1cc32009a5 --- /dev/null +++ b/frame/support/test/tests/derive_impl_ui/missing_disambiguation_path.rs @@ -0,0 +1,44 @@ +use frame_support::*; + +pub trait Animal { + type Locomotion; + type Diet; + type SleepingStrategy; + type Environment; + + fn animal_name() -> &'static str; +} + +pub type RunsOnFourLegs = (usize, usize, usize, usize); +pub type RunsOnTwoLegs = (usize, usize); +pub type Swims = isize; +pub type Diurnal = bool; +pub type Nocturnal = Option; +pub type Omnivore = char; +pub type Land = ((), ()); +pub type Sea = ((), (), ()); +pub type Carnivore = (char, char); + +pub struct FourLeggedAnimal {} + +#[register_default_impl(FourLeggedAnimal)] +impl Animal for FourLeggedAnimal { + type Locomotion = RunsOnFourLegs; + type Diet = Omnivore; + type SleepingStrategy = Diurnal; + type Environment = Land; + + fn animal_name() -> &'static str { + "A Four-Legged Animal" + } +} + +pub struct AcquaticMammal {} + +#[derive_impl(FourLeggedAnimal as)] +impl Animal for AcquaticMammal { + type Locomotion = (Swims, RunsOnFourLegs); + type Environment = (Land, Sea); +} + +fn main() {} diff --git a/frame/support/test/tests/derive_impl_ui/missing_disambiguation_path.stderr b/frame/support/test/tests/derive_impl_ui/missing_disambiguation_path.stderr new file mode 100644 index 0000000000000..85cd94ae08ae7 --- /dev/null +++ b/frame/support/test/tests/derive_impl_ui/missing_disambiguation_path.stderr @@ -0,0 +1,7 @@ +error: unexpected end of input, expected identifier + --> tests/derive_impl_ui/missing_disambiguation_path.rs:38:1 + | +38 | #[derive_impl(FourLeggedAnimal as)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the attribute macro `derive_impl` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/frame/support/test/tests/derive_impl_ui/pass/basic_overriding.rs b/frame/support/test/tests/derive_impl_ui/pass/basic_overriding.rs new file mode 100644 index 0000000000000..336ddc315f8cb --- /dev/null +++ b/frame/support/test/tests/derive_impl_ui/pass/basic_overriding.rs @@ -0,0 +1,69 @@ +use frame_support::*; +use static_assertions::assert_type_eq_all; + +pub trait Animal { + type Locomotion; + type Diet; + type SleepingStrategy; + type Environment; + + fn animal_name() -> &'static str; +} + +pub type RunsOnFourLegs = (usize, usize, usize, usize); +pub type RunsOnTwoLegs = (usize, usize); +pub type Swims = isize; +pub type Diurnal = bool; +pub type Nocturnal = Option; +pub type Omnivore = char; +pub type Land = ((), ()); +pub type Sea = ((), (), ()); +pub type Carnivore = (char, char); + +pub struct FourLeggedAnimal {} + +#[register_default_impl(FourLeggedAnimal)] +impl Animal for FourLeggedAnimal { + type Locomotion = RunsOnFourLegs; + type Diet = Omnivore; + type SleepingStrategy = Diurnal; + type Environment = Land; + + fn animal_name() -> &'static str { + "A Four-Legged Animal" + } +} + +pub struct AcquaticMammal {} + +// without omitting the `as X` +#[derive_impl(FourLeggedAnimal as Animal)] +impl Animal for AcquaticMammal { + type Locomotion = (Swims, RunsOnFourLegs); + type Environment = (Land, Sea); +} + +assert_type_eq_all!(::Locomotion, (Swims, RunsOnFourLegs)); +assert_type_eq_all!(::Environment, (Land, Sea)); +assert_type_eq_all!(::Diet, Omnivore); +assert_type_eq_all!(::SleepingStrategy, Diurnal); + +pub struct Lion {} + +// test omitting the `as X` +#[derive_impl(FourLeggedAnimal)] +impl Animal for Lion { + type Diet = Carnivore; + type SleepingStrategy = Nocturnal; + + fn animal_name() -> &'static str { + "Lion" + } +} + +assert_type_eq_all!(::Diet, Carnivore); +assert_type_eq_all!(::SleepingStrategy, Nocturnal); +assert_type_eq_all!(::Environment, Land); +assert_type_eq_all!(::Locomotion, RunsOnFourLegs); + +fn main() {} diff --git a/frame/support/test/tests/derive_impl_ui/pass/macro_magic_working.rs b/frame/support/test/tests/derive_impl_ui/pass/macro_magic_working.rs new file mode 100644 index 0000000000000..ec09bd15e0173 --- /dev/null +++ b/frame/support/test/tests/derive_impl_ui/pass/macro_magic_working.rs @@ -0,0 +1,18 @@ +#[frame_support::macro_magic::export_tokens] +struct MyCoolStruct { + field: u32, +} + +// create a test receiver since `proc_support` isn't enabled so we're on our own in terms of +// what we can call +macro_rules! receiver { + ($_tokens_var:ident, $($tokens:tt)*) => { + stringify!($($tokens)*) + }; +} + +fn main() { + let _instance: MyCoolStruct = MyCoolStruct { field: 3 }; + let _str = __export_tokens_tt_my_cool_struct!(tokens, receiver); + // this compiling demonstrates that macro_magic is working properly +} diff --git a/frame/support/test/tests/pallet_ui/no_default_but_missing_with_default.rs b/frame/support/test/tests/pallet_ui/no_default_but_missing_with_default.rs new file mode 100644 index 0000000000000..5ffa13c22243d --- /dev/null +++ b/frame/support/test/tests/pallet_ui/no_default_but_missing_with_default.rs @@ -0,0 +1,23 @@ +#[frame_support::pallet] +mod pallet { + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + #[pallet::constant] + #[pallet::no_default] + type MyGetParam2: Get; + } + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::hooks] + impl Hooks> for Pallet {} + + #[pallet::call] + impl Pallet {} +} + +fn main() {} diff --git a/frame/support/test/tests/pallet_ui/no_default_but_missing_with_default.stderr b/frame/support/test/tests/pallet_ui/no_default_but_missing_with_default.stderr new file mode 100644 index 0000000000000..aebde115eb80e --- /dev/null +++ b/frame/support/test/tests/pallet_ui/no_default_but_missing_with_default.stderr @@ -0,0 +1,5 @@ +error: `#[pallet:no_default]` can only be used if `#[pallet::config(with_default)]` has been specified + --> tests/pallet_ui/no_default_but_missing_with_default.rs:9:4 + | +9 | #[pallet::no_default] + | ^^^^^^^^^^^^^^^^^^^^ diff --git a/frame/support/test/tests/pallet_ui/trait_invalid_item.stderr b/frame/support/test/tests/pallet_ui/trait_invalid_item.stderr index 72495d94b3079..e3409a819114a 100644 --- a/frame/support/test/tests/pallet_ui/trait_invalid_item.stderr +++ b/frame/support/test/tests/pallet_ui/trait_invalid_item.stderr @@ -1,5 +1,5 @@ -error: Invalid pallet::constant in pallet::config, expected type trait item - --> $DIR/trait_invalid_item.rs:9:3 +error: Invalid #[pallet::constant] in #[pallet::config], expected type item + --> tests/pallet_ui/trait_invalid_item.rs:9:3 | 9 | const U: u8 = 3; | ^^^^^ diff --git a/frame/support/test/tests/pallet_ui/trait_item_duplicate_constant_attr.rs b/frame/support/test/tests/pallet_ui/trait_item_duplicate_constant_attr.rs new file mode 100644 index 0000000000000..8f3d9f3f3e2f9 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/trait_item_duplicate_constant_attr.rs @@ -0,0 +1,23 @@ +#[frame_support::pallet] +mod pallet { + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + #[pallet::config] + pub trait Config: frame_system::Config { + #[pallet::constant] + #[pallet::constant] + type MyGetParam2: Get; + } + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::hooks] + impl Hooks> for Pallet {} + + #[pallet::call] + impl Pallet {} +} + +fn main() {} diff --git a/frame/support/test/tests/pallet_ui/trait_item_duplicate_constant_attr.stderr b/frame/support/test/tests/pallet_ui/trait_item_duplicate_constant_attr.stderr new file mode 100644 index 0000000000000..3679b67f07b53 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/trait_item_duplicate_constant_attr.stderr @@ -0,0 +1,5 @@ +error: Duplicate #[pallet::constant] attribute not allowed. + --> tests/pallet_ui/trait_item_duplicate_constant_attr.rs:9:4 + | +9 | #[pallet::constant] + | ^^^^^^^^^^^^^^^^^^ diff --git a/frame/support/test/tests/pallet_ui/trait_item_duplicate_no_default.rs b/frame/support/test/tests/pallet_ui/trait_item_duplicate_no_default.rs new file mode 100644 index 0000000000000..d2040ec74dc4e --- /dev/null +++ b/frame/support/test/tests/pallet_ui/trait_item_duplicate_no_default.rs @@ -0,0 +1,24 @@ +#[frame_support::pallet] +mod pallet { + use frame_support::pallet_prelude::*; + use frame_system::pallet_prelude::*; + + #[pallet::config(with_default)] + pub trait Config: frame_system::Config { + #[pallet::constant] + #[pallet::no_default] + #[pallet::no_default] + type MyGetParam2: Get; + } + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::hooks] + impl Hooks> for Pallet {} + + #[pallet::call] + impl Pallet {} +} + +fn main() {} diff --git a/frame/support/test/tests/pallet_ui/trait_item_duplicate_no_default.stderr b/frame/support/test/tests/pallet_ui/trait_item_duplicate_no_default.stderr new file mode 100644 index 0000000000000..77a29c394d62d --- /dev/null +++ b/frame/support/test/tests/pallet_ui/trait_item_duplicate_no_default.stderr @@ -0,0 +1,5 @@ +error: Duplicate #[pallet::no_default] attribute not allowed. + --> tests/pallet_ui/trait_item_duplicate_no_default.rs:10:4 + | +10 | #[pallet::no_default] + | ^^^^^^^^^^^^^^^^^^^^ diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index 64db2f46fd4eb..8c717e3f5eb12 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -201,12 +201,55 @@ pub mod pallet { use crate::{self as frame_system, pallet_prelude::*, *}; use frame_support::pallet_prelude::*; + /// Contains default types suitable for various environments + pub mod config_preludes { + use super::DefaultConfig; + + /// Provides a viable default config that can be used with + /// [`derive_impl`](`frame_support::derive_impl`) to derive a testing pallet config + /// based on this one. + /// + /// See `Test` in the `default-config` example pallet's `test.rs` for an example of + /// a downstream user of this particular `TestDefaultConfig` + pub struct TestDefaultConfig; + + #[frame_support::register_default_impl(TestDefaultConfig)] + impl DefaultConfig for TestDefaultConfig { + type Index = u32; + type BlockNumber = u32; + type Header = sp_runtime::generic::Header; + type Hash = sp_core::hash::H256; + type Hashing = sp_runtime::traits::BlakeTwo256; + type AccountId = u64; + type Lookup = sp_runtime::traits::IdentityLookup; + type BlockHashCount = frame_support::traits::ConstU32<10>; + type MaxConsumers = frame_support::traits::ConstU32<16>; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type Version = (); + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + } + } + /// System configuration trait. Implemented by runtime. - #[pallet::config] + #[pallet::config(with_default)] #[pallet::disable_frame_system_supertrait_check] pub trait Config: 'static + Eq + Clone { + /// The aggregated event type of the runtime. + type RuntimeEvent: Parameter + + Member + + From> + + Debug + + IsType<::RuntimeEvent>; + /// The basic call filter to use in Origin. All origins are built with this filter as base, /// except Root. + #[pallet::no_default] type BaseCallFilter: Contains; /// Block & extrinsics weights: base values and limits. @@ -218,12 +261,14 @@ pub mod pallet { type BlockLength: Get; /// The `RuntimeOrigin` type used by dispatchable calls. + #[pallet::no_default] type RuntimeOrigin: Into, Self::RuntimeOrigin>> + From> + Clone + OriginTrait; /// The aggregated `RuntimeCall` type. + #[pallet::no_default] type RuntimeCall: Parameter + Dispatchable + Debug @@ -295,13 +340,6 @@ pub mod pallet { /// The block header. type Header: Parameter + traits::Header; - /// The aggregated event type of the runtime. - type RuntimeEvent: Parameter - + Member - + From> - + Debug - + IsType<::RuntimeEvent>; - /// Maximum number of block number to block hash mappings to keep (oldest pruned first). #[pallet::constant] type BlockHashCount: Get; @@ -320,6 +358,7 @@ pub mod pallet { /// runtime. /// /// For tests it is okay to use `()` as type, however it will provide "useless" data. + #[pallet::no_default] type PalletInfo: PalletInfo; /// Data to be associated with an account (other than nonce/transaction counter, which this @@ -351,6 +390,7 @@ pub mod pallet { /// [`Pallet::update_code_in_storage`]). /// It's unlikely that this needs to be customized, unless you are writing a parachain using /// `Cumulus`, where the actual code change is deferred. + #[pallet::no_default] type OnSetCode: SetCode; /// The maximum number of consumers allowed on a single account.