mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-09 04:47:59 +00:00
Default Pallet Config Trait / derive_impl (#13454)
* first draft, probably won't work * first draft, probably won't work * good progress.. * good milestone, still a lot to do. * EVERYTHING WORKS * Update frame/support/procedural/src/derive_impl.rs Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Update frame/support/procedural/src/derive_impl.rs Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * clean up + cargo fmt * import tokens WIP * export_tokens working with impl Trait * WIP / notes * use macro_magic 0.2.0's export_tokens to access foreign items * token importing working properly using macro_magic 0.2.5 * combine_impls almost working * successfully get foreign path via macro_magic 0.2.6 * combine_impls using implementing_type generics * working + clean up * more clean up * decrease rightwards drift and add docs to combine_impls * add support for macros to impl_item_ident in case we hit that * add docs for impl_item_ident method * fix no_std issues * re-export of macro_magic working in pallets 🎉 * clean up + fully resolve no_std issue with macro_magic with v0.2.11 * remove trait item code for different trait item types since this is now handled directly by combine_impls * clean up * remove dev comments * only generate default trait if #[pallet::default_trait] is attached * authorship and most other pallets now compiling * compiling 🎉 * add check for more than two pallet attributes on Config trait * remove unused import in nomination-pool * clean up debug code * upgrade to macro_magic v0.2.12 * add neater #[register_default_config(SomeIdent)] macro * really just a thin wrapper around #[export_tokens] * upgrade to macro_magic 0.3.1 * rewrite parsing to be compatible with syn 2.x, compiling 🎉 * remove unused keywords * macro stubs for the new pallet:: macros, preliminary docs * upgrade to macro_magic v0.3.2 * rename register_default_config => register_default_impl * bump to macro_magic v0.3.3 * custom disambiguation_path working as 2nd arg to derive_impl * overhaul docs * fixes, ident-style paths shortcut working * remove ident-style shortcut because it makes testing difficult * add passing UI tests for derive_impl * switch to `ForeignPath as DisambiguationPath` syntax + update docs * add UI test for bad foreign path * add UI test for bad disambiguation path * add UI test for missing disambiguation path * add UI test for attached to non impl * fix derive_impl_attr_args_parsing test * move tests to bottom * fix nightly issue * add doc notes on importing/re-exporting * remove explicit use of macro_magic::use_attr Co-authored-by: Bastian Köcher <git@kchr.de> * use explicit macro_magic::use_attr Co-authored-by: Bastian Köcher <git@kchr.de> * remove unneeded {} Co-authored-by: Bastian Köcher <git@kchr.de> * remove unneeded collect Co-authored-by: Bastian Köcher <git@kchr.de> * add docs for TestDefaultConfig * remove unneeded `#[export_tokens]` on `DefaultConfig` * add docs for auto-generated `DefaultConfig` * no need to clone Co-authored-by: Bastian Köcher <git@kchr.de> * clean up combine_impls + compiling again * remove unused dependency * simplify struct definition Co-authored-by: Bastian Köcher <git@kchr.de> * fix register_default_impl docs * reduce rightward drift / refactor Co-authored-by: Keith Yeung <kungfukeith11@gmail.com> * fix derive_impl after keith's changes * simplify disambiguation_path calculation Co-authored-by: Keith Yeung <kungfukeith11@gmail.com> * compiling again * simplify parsing of trait item Co-authored-by: Keith Yeung <kungfukeith11@gmail.com> * rename preludes => prelude Co-authored-by: Keith Yeung <kungfukeith11@gmail.com> * fix other places where we used preludes instead of prelude * fix indents * simplify PalletAttr parsing Co-authored-by: Keith Yeung <kungfukeith11@gmail.com> * go back to having no_default and constant as keywords * make it more clear that disambiguation_path is optional * make default_trait_items just a Vec instead of Option<Vec> * rename foreign_path => default_impl_path within substrate * fix docs * Change {} to ; Co-authored-by: Bastian Köcher <git@kchr.de> * highlight full end-to-end example with link * add pallet-default-config-example, start by copying dev mode code * update dev-mode specific docs * use Person and Points instead of Dummy and Bar * add docs to example pallet * revert changes to pallets other than the default config example * fix outdated references to basic example pallet * re-order docs to be a bit more clear * better errors for extra attributes * add UI tests for duplicate/extra attributes on trait items * change `#[pallet::default_config]` to option on `#[pallet::config()]` * update UI tests * add UI test covering missing `#[pallet::config(with_default)]` when `#[pallet::no_default]` is used * add note about new optional conventions * improve docs about `DefaultConfig` and link to these from a few places * fix doc comment * fix old comment referencing `pallet::default_config` * use u32 instead of u64 for block number Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * use () instead of u32 for `AccountData` Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * use ConstU32<10> for BlockHashCount instead of ConstU64<10> Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * people are not dummies Co-authored-by: Liam Aharon <liam.aharon@hotmail.com> * fix wording Co-authored-by: Just van Stam <vstam1@users.noreply.github.com> * Person => People and compiling again * add docs for `prelude` module in frame_system * update Cargo.lock * cleaner example * tweaks * update docs more * update docs more * update docs more * update docs more * fix ui tests * err * Update frame/support/test/tests/pallet_ui.rs * update ui tests --------- Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> Co-authored-by: Sam Johnson <sam@durosoft.com> Co-authored-by: parity-processbot <> Co-authored-by: Bastian Köcher <git@kchr.de> Co-authored-by: Keith Yeung <kungfukeith11@gmail.com> Co-authored-by: Liam Aharon <liam.aharon@hotmail.com> Co-authored-by: Just van Stam <vstam1@users.noreply.github.com>
This commit is contained in:
Generated
+64
@@ -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"
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
[package]
|
||||
name = "pallet-default-config-example"
|
||||
version = "4.0.0-dev"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
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"]
|
||||
@@ -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
|
||||
@@ -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<Event<Self>> + IsType<<Self as frame_system::Config>::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<u32>;
|
||||
|
||||
/// Same as [`Config::WithDefaultValue`], but we don't intend to define a default for this
|
||||
/// in our tests below.
|
||||
type OverwrittenDefaultValue: Get<u32>;
|
||||
|
||||
/// An input parameter that relies on `<Self as frame_system::Config>::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<Self::AccountId>;
|
||||
|
||||
/// 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<T>(_);
|
||||
|
||||
#[pallet::event]
|
||||
pub enum Event<T: Config> {}
|
||||
}
|
||||
|
||||
#[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<Test>;
|
||||
type Block = frame_system::mocking::MockBlock<Test>;
|
||||
|
||||
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<Self::BlockNumber, Self::Hashing>;
|
||||
// type Hash = sp_core::hash::H256;
|
||||
// type Hashing = sp_runtime::traits::BlakeTwo256;
|
||||
// type AccountId = u64;
|
||||
// type Lookup = sp_runtime::traits::IdentityLookup<u64>;
|
||||
// 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!(
|
||||
<<Test as Config>::WithDefaultValue as Get<u32>>::get(),
|
||||
<<TestDefaultConfig as DefaultConfig>::WithDefaultValue as Get<u32>>::get()
|
||||
);
|
||||
|
||||
// assert one of the value types that is overwritten.
|
||||
assert_eq!(<<Test as Config>::OverwrittenDefaultValue as Get<u32>>::get(), 678u32);
|
||||
|
||||
// assert one of the types that is not overwritten.
|
||||
assert_eq!(
|
||||
std::any::TypeId::of::<<Test as Config>::WithDefaultType>(),
|
||||
std::any::TypeId::of::<<TestDefaultConfig as DefaultConfig>::WithDefaultType>()
|
||||
);
|
||||
|
||||
// assert one of the types that is overwritten.
|
||||
assert_eq!(
|
||||
std::any::TypeId::of::<<Test as Config>::OverwrittenDefaultType>(),
|
||||
std::any::TypeId::of::<u128>()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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 }
|
||||
|
||||
@@ -24,6 +24,7 @@ quote = "1.0.28"
|
||||
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"]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<Token![as]>,
|
||||
#[parse_if(_as.is_some())]
|
||||
pub disambiguation_path: Option<Path>,
|
||||
}
|
||||
|
||||
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<ImplItem>, HashSet<ImplItem>) =
|
||||
local_impl
|
||||
.items
|
||||
.iter()
|
||||
.cloned()
|
||||
.partition(|impl_item| impl_item_ident(impl_item).is_some());
|
||||
let existing_local_keys: HashSet<Ident> = 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<Path>,
|
||||
) -> Result<TokenStream2> {
|
||||
let local_impl = parse2::<ItemImpl>(local_tokens)?;
|
||||
let foreign_impl = parse2::<ItemImpl>(foreign_tokens)?;
|
||||
let default_impl_path = parse2::<Path>(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::<DeriveImplAttrArgs>(quote!(
|
||||
some::path::TestDefaultConfig as some::path::DefaultConfig
|
||||
))
|
||||
.unwrap();
|
||||
parse2::<DeriveImplAttrArgs>(quote!(
|
||||
frame_system::prelude::testing::TestDefaultConfig as DefaultConfig
|
||||
))
|
||||
.unwrap();
|
||||
parse2::<DeriveImplAttrArgs>(quote!(Something as some::path::DefaultConfig)).unwrap();
|
||||
parse2::<DeriveImplAttrArgs>(quote!(Something as DefaultConfig)).unwrap();
|
||||
parse2::<DeriveImplAttrArgs>(quote!(DefaultConfig)).unwrap();
|
||||
assert!(parse2::<DeriveImplAttrArgs>(quote!()).is_err());
|
||||
assert!(parse2::<DeriveImplAttrArgs>(quote!(Config Config)).is_err());
|
||||
}
|
||||
@@ -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
|
||||
/// `<some::path::TestTraitImpl as another::path::DefaultTrait>::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<u64>;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// 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<AccountId>;
|
||||
/// 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<u64>;
|
||||
/// type Version = <TestDefaultConfig as DefaultConfig>::Version;
|
||||
/// type BlockWeights = <TestDefaultConfig as DefaultConfig>::BlockWeights;
|
||||
/// type BlockLength = <TestDefaultConfig as DefaultConfig>::BlockLength;
|
||||
/// type DbWeight = <TestDefaultConfig as DefaultConfig>::DbWeight;
|
||||
/// type Index = <TestDefaultConfig as DefaultConfig>::Index;
|
||||
/// type BlockNumber = <TestDefaultConfig as DefaultConfig>::BlockNumber;
|
||||
/// type Hash = <TestDefaultConfig as DefaultConfig>::Hash;
|
||||
/// type Hashing = <TestDefaultConfig as DefaultConfig>::Hashing;
|
||||
/// type AccountId = <TestDefaultConfig as DefaultConfig>::AccountId;
|
||||
/// type Lookup = <TestDefaultConfig as DefaultConfig>::Lookup;
|
||||
/// type BlockHashCount = <TestDefaultConfig as DefaultConfig>::BlockHashCount;
|
||||
/// type OnNewAccount = <TestDefaultConfig as DefaultConfig>::OnNewAccount;
|
||||
/// type OnKilledAccount = <TestDefaultConfig as DefaultConfig>::OnKilledAccount;
|
||||
/// type SystemWeightInfo = <TestDefaultConfig as DefaultConfig>::SystemWeightInfo;
|
||||
/// type SS58Prefix = <TestDefaultConfig as DefaultConfig>::SS58Prefix;
|
||||
/// type MaxConsumers = <TestDefaultConfig as DefaultConfig>::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<Event<Self>>
|
||||
/// + Debug
|
||||
/// + IsType<<Self as frame_system::Config>::RuntimeEvent>;
|
||||
///
|
||||
/// #[pallet::no_default]
|
||||
/// type BaseCallFilter: Contains<Self::RuntimeCall>;
|
||||
/// // ...
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// 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()
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<syn::WhereClause>,
|
||||
/// 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<syn::TraitItem>,
|
||||
}
|
||||
|
||||
/// 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<Self> {
|
||||
let pound_token = input.parse::<syn::Token![#]>()?;
|
||||
let content;
|
||||
let bracket_token = syn::bracketed!(content in input);
|
||||
let pallet_ident = content.parse::<syn::Ident>()?;
|
||||
let path_sep_token = content.parse::<syn::Token![::]>()?;
|
||||
let constant_keyword = content.parse::<keyword::constant>()?;
|
||||
|
||||
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<Self> {
|
||||
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)?;
|
||||
let is_event = check_event_type(frame_system, trait_item, has_instance)?;
|
||||
has_event_type = has_event_type || is_event;
|
||||
|
||||
// Parse for constant
|
||||
let type_attrs_const: Vec<TypeAttrConst> = helper::take_item_pallet_attrs(trait_item)?;
|
||||
let mut already_no_default = false;
|
||||
let mut already_constant = false;
|
||||
|
||||
if type_attrs_const.len() > 1 {
|
||||
let msg = "Invalid attribute in pallet::config, only one attribute is expected";
|
||||
return Err(syn::Error::new(type_attrs_const[1].span(), msg))
|
||||
}
|
||||
|
||||
if type_attrs_const.len() == 1 {
|
||||
match trait_item {
|
||||
syn::TraitItem::Type(ref type_) => {
|
||||
let constant = ConstMetadataDef::try_from(type_)?;
|
||||
consts_metadata.push(constant);
|
||||
while let Ok(Some(pallet_attr)) =
|
||||
helper::take_first_item_pallet_attr::<PalletAttr>(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<DisableFrameSystemSupertraitCheck> =
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Attr>(item: &mut impl MutItemAttrs) -> syn::Result<Option<Attr>>
|
||||
pub(crate) fn take_first_item_pallet_attr<Attr>(
|
||||
item: &mut impl MutItemAttrs,
|
||||
) -> syn::Result<Option<Attr>>
|
||||
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<Attr>(item: &mut impl MutItemAttrs) -> syn::Result<Vec<Attr>>
|
||||
pub(crate) fn take_item_pallet_attrs<Attr>(item: &mut impl MutItemAttrs) -> syn::Result<Vec<Attr>>
|
||||
where
|
||||
Attr: syn::parse::Parse,
|
||||
{
|
||||
|
||||
@@ -100,8 +100,14 @@ impl Def {
|
||||
let pallet_attr: Option<PalletAttr> = 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::<keyword::config>()?.span()))
|
||||
let span = content.parse::<keyword::config>()?.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::<keyword::with_default>()?;
|
||||
}
|
||||
Ok(PalletAttr::Config(span, with_default))
|
||||
} else if lookahead.peek(keyword::pallet) {
|
||||
Ok(PalletAttr::Pallet(content.parse::<keyword::pallet>()?.span()))
|
||||
} else if lookahead.peek(keyword::hooks) {
|
||||
|
||||
@@ -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", $);
|
||||
|
||||
@@ -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"] }
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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<bool>;
|
||||
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() {}
|
||||
@@ -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)
|
||||
@@ -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<bool>;
|
||||
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() {}
|
||||
@@ -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)
|
||||
@@ -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<bool>;
|
||||
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() {}
|
||||
@@ -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)
|
||||
@@ -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<bool>;
|
||||
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() {}
|
||||
@@ -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)
|
||||
@@ -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<bool>;
|
||||
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!(<AcquaticMammal as Animal>::Locomotion, (Swims, RunsOnFourLegs));
|
||||
assert_type_eq_all!(<AcquaticMammal as Animal>::Environment, (Land, Sea));
|
||||
assert_type_eq_all!(<AcquaticMammal as Animal>::Diet, Omnivore);
|
||||
assert_type_eq_all!(<AcquaticMammal as Animal>::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!(<Lion as Animal>::Diet, Carnivore);
|
||||
assert_type_eq_all!(<Lion as Animal>::SleepingStrategy, Nocturnal);
|
||||
assert_type_eq_all!(<Lion as Animal>::Environment, Land);
|
||||
assert_type_eq_all!(<Lion as Animal>::Locomotion, RunsOnFourLegs);
|
||||
|
||||
fn main() {}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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<u32>;
|
||||
}
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(core::marker::PhantomData<T>);
|
||||
|
||||
#[pallet::hooks]
|
||||
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
|
||||
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
+5
@@ -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]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -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;
|
||||
| ^^^^^
|
||||
|
||||
@@ -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<u32>;
|
||||
}
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(core::marker::PhantomData<T>);
|
||||
|
||||
#[pallet::hooks]
|
||||
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
|
||||
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -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]
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
@@ -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<u32>;
|
||||
}
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(core::marker::PhantomData<T>);
|
||||
|
||||
#[pallet::hooks]
|
||||
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
|
||||
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -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]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -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<Self::BlockNumber, Self::Hashing>;
|
||||
type Hash = sp_core::hash::H256;
|
||||
type Hashing = sp_runtime::traits::BlakeTwo256;
|
||||
type AccountId = u64;
|
||||
type Lookup = sp_runtime::traits::IdentityLookup<u64>;
|
||||
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<Event<Self>>
|
||||
+ Debug
|
||||
+ IsType<<Self as frame_system::Config>::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<Self::RuntimeCall>;
|
||||
|
||||
/// Block & extrinsics weights: base values and limits.
|
||||
@@ -218,12 +261,14 @@ pub mod pallet {
|
||||
type BlockLength: Get<limits::BlockLength>;
|
||||
|
||||
/// The `RuntimeOrigin` type used by dispatchable calls.
|
||||
#[pallet::no_default]
|
||||
type RuntimeOrigin: Into<Result<RawOrigin<Self::AccountId>, Self::RuntimeOrigin>>
|
||||
+ From<RawOrigin<Self::AccountId>>
|
||||
+ Clone
|
||||
+ OriginTrait<Call = Self::RuntimeCall>;
|
||||
|
||||
/// The aggregated `RuntimeCall` type.
|
||||
#[pallet::no_default]
|
||||
type RuntimeCall: Parameter
|
||||
+ Dispatchable<RuntimeOrigin = Self::RuntimeOrigin>
|
||||
+ Debug
|
||||
@@ -295,13 +340,6 @@ pub mod pallet {
|
||||
/// The block header.
|
||||
type Header: Parameter + traits::Header<Number = Self::BlockNumber, Hash = Self::Hash>;
|
||||
|
||||
/// The aggregated event type of the runtime.
|
||||
type RuntimeEvent: Parameter
|
||||
+ Member
|
||||
+ From<Event<Self>>
|
||||
+ Debug
|
||||
+ IsType<<Self as frame_system::Config>::RuntimeEvent>;
|
||||
|
||||
/// Maximum number of block number to block hash mappings to keep (oldest pruned first).
|
||||
#[pallet::constant]
|
||||
type BlockHashCount: Get<Self::BlockNumber>;
|
||||
@@ -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<Self>;
|
||||
|
||||
/// The maximum number of consumers allowed on a single account.
|
||||
|
||||
Reference in New Issue
Block a user