From d0d8e29197a783f3ea300569afc50244a280cafa Mon Sep 17 00:00:00 2001 From: gupnik Date: Mon, 22 Jul 2024 15:43:21 +0530 Subject: [PATCH] Fixes doc links for `procedural` crate (#5023) This PR fixes the documentation for FRAME Macros when pointed from `polkadot_sdk_docs` crate. This is achieved by referring to the examples in the `procedural` crate, embedded via `docify`. --------- Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> --- Cargo.lock | 11 + substrate/frame/support/procedural/Cargo.toml | 31 +- .../proc_main}/inject_runtime_type.rs | 3 + .../procedural/examples/proc_main/main.rs | 737 ++++++++++++++++++ .../examples/proc_main}/runtime.rs | 0 .../examples/proc_main}/tasks.rs | 11 +- substrate/frame/support/procedural/src/lib.rs | 109 ++- substrate/frame/support/src/lib.rs | 117 +-- substrate/frame/support/src/tests/mod.rs | 5 - 9 files changed, 877 insertions(+), 147 deletions(-) rename substrate/frame/support/{src/tests => procedural/examples/proc_main}/inject_runtime_type.rs (97%) create mode 100644 substrate/frame/support/procedural/examples/proc_main/main.rs rename substrate/frame/support/{src/tests => procedural/examples/proc_main}/runtime.rs (100%) rename substrate/frame/support/{src/tests => procedural/examples/proc_main}/tasks.rs (91%) diff --git a/Cargo.lock b/Cargo.lock index 2c808f2a6be6..ead688ec9f56 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6051,15 +6051,26 @@ dependencies = [ "Inflector", "cfg-expr", "derive-syn-parse", + "docify", "expander", + "frame-support", "frame-support-procedural-tools", + "frame-system", "itertools 0.11.0", "macro_magic", + "parity-scale-codec", + "pretty_assertions", "proc-macro-warning 1.0.0", "proc-macro2 1.0.82", "quote 1.0.36", "regex", + "scale-info", + "sp-core", "sp-crypto-hashing", + "sp-io", + "sp-metadata-ir", + "sp-runtime", + "static_assertions", "syn 2.0.61", ] diff --git a/substrate/frame/support/procedural/Cargo.toml b/substrate/frame/support/procedural/Cargo.toml index fbb4da0177a4..aa0665a2250f 100644 --- a/substrate/frame/support/procedural/Cargo.toml +++ b/substrate/frame/support/procedural/Cargo.toml @@ -19,6 +19,7 @@ proc-macro = true [dependencies] derive-syn-parse = { workspace = true } +docify = { workspace = true } Inflector = { workspace = true } cfg-expr = { workspace = true } itertools = { workspace = true } @@ -32,14 +33,42 @@ expander = { workspace = true } sp-crypto-hashing = { workspace = true } [dev-dependencies] +codec = { features = [ + "derive", + "max-encoded-len", +], workspace = true } regex = { workspace = true } +sp-metadata-ir = { workspace = true } +scale-info = { features = ["derive"], workspace = true } +sp-core = { workspace = true } +sp-io = { workspace = true } +sp-runtime = { features = [ + "serde", +], workspace = true } +frame-system = { workspace = true } +frame-support = { workspace = true } +pretty_assertions = { workspace = true } +static_assertions = { workspace = true } [features] default = ["std"] -std = ["sp-crypto-hashing/std"] +std = [ + "codec/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "sp-core/std", + "sp-crypto-hashing/std", + "sp-io/std", + "sp-metadata-ir/std", + "sp-runtime/std", +] no-metadata-docs = [] experimental = [] # Generate impl-trait for tuples with the given number of tuples. Will be needed as the number of # pallets in a runtime grows. Does increase the compile time! tuples-96 = [] tuples-128 = [] + +[[example]] +name = "proc_main" diff --git a/substrate/frame/support/src/tests/inject_runtime_type.rs b/substrate/frame/support/procedural/examples/proc_main/inject_runtime_type.rs similarity index 97% rename from substrate/frame/support/src/tests/inject_runtime_type.rs rename to substrate/frame/support/procedural/examples/proc_main/inject_runtime_type.rs index 429a743d3b7b..cd5660f2bda7 100644 --- a/substrate/frame/support/src/tests/inject_runtime_type.rs +++ b/substrate/frame/support/procedural/examples/proc_main/inject_runtime_type.rs @@ -15,8 +15,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +#[cfg(test)] use super::{Config, Runtime}; +#[cfg(test)] use crate::{derive_impl, pallet_prelude::inject_runtime_type}; +#[cfg(test)] use static_assertions::assert_type_eq_all; #[docify::export] diff --git a/substrate/frame/support/procedural/examples/proc_main/main.rs b/substrate/frame/support/procedural/examples/proc_main/main.rs new file mode 100644 index 000000000000..4bdfc76dd92f --- /dev/null +++ b/substrate/frame/support/procedural/examples/proc_main/main.rs @@ -0,0 +1,737 @@ +// 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. + +use frame_support::*; +use frame_support_procedural::import_section; +#[cfg(test)] +use sp_io::{MultiRemovalResults, TestExternalities}; +#[cfg(test)] +use sp_metadata_ir::{ + PalletStorageMetadataIR, StorageEntryMetadataIR, StorageEntryModifierIR, StorageEntryTypeIR, + StorageHasherIR, +}; +#[cfg(test)] +use sp_runtime::BuildStorage; +use sp_runtime::{generic, traits::BlakeTwo256}; + +pub use self::frame_system::{pallet_prelude::*, Config, Pallet}; + +mod inject_runtime_type; +mod runtime; +mod tasks; + +#[import_section(tasks::tasks_example)] +#[pallet] +pub mod frame_system { + #[allow(unused)] + use super::{frame_system, frame_system::pallet_prelude::*}; + pub use crate::dispatch::RawOrigin; + use crate::{pallet_prelude::*, traits::tasks::Task as TaskTrait}; + + pub mod config_preludes { + use super::{inject_runtime_type, DefaultConfig}; + pub struct TestDefaultConfig; + + #[crate::register_default_impl(TestDefaultConfig)] + impl DefaultConfig for TestDefaultConfig { + type AccountId = u64; + type BaseCallFilter = frame_support::traits::Everything; + #[inject_runtime_type] + type RuntimeOrigin = (); + #[inject_runtime_type] + type RuntimeCall = (); + #[inject_runtime_type] + type PalletInfo = (); + #[inject_runtime_type] + type RuntimeTask = (); + type DbWeight = (); + } + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::config(with_default)] + #[pallet::disable_frame_system_supertrait_check] + pub trait Config: 'static { + #[pallet::no_default] + type Block: Parameter + sp_runtime::traits::Block; + type AccountId; + #[pallet::no_default_bounds] + type BaseCallFilter: crate::traits::Contains; + #[pallet::no_default_bounds] + type RuntimeOrigin; + #[pallet::no_default_bounds] + type RuntimeCall; + #[pallet::no_default_bounds] + type RuntimeTask: crate::traits::tasks::Task; + #[pallet::no_default_bounds] + type PalletInfo: crate::traits::PalletInfo; + type DbWeight: Get; + } + + #[pallet::error] + pub enum Error { + /// Required by construct_runtime + CallFiltered, + /// Used in tasks example. + NotFound, + /// The specified [`Task`] is not valid. + InvalidTask, + /// The specified [`Task`] failed during execution. + FailedTask, + } + + #[pallet::origin] + pub type Origin = RawOrigin<::AccountId>; + + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + #[pallet::weight(task.weight())] + pub fn do_task(_origin: OriginFor, task: T::RuntimeTask) -> DispatchResultWithPostInfo { + if !task.is_valid() { + return Err(Error::::InvalidTask.into()) + } + + if let Err(_err) = task.run() { + return Err(Error::::FailedTask.into()) + } + + Ok(().into()) + } + } + + #[pallet::storage] + pub type Data = StorageMap<_, Twox64Concat, u32, u64, ValueQuery>; + + #[pallet::storage] + pub type OptionLinkedMap = StorageMap<_, Blake2_128Concat, u32, u32, OptionQuery>; + + #[pallet::storage] + pub type GenericData = + StorageMap<_, Identity, BlockNumberFor, BlockNumberFor, ValueQuery>; + + #[pallet::storage] + pub type GenericData2 = + StorageMap<_, Blake2_128Concat, BlockNumberFor, BlockNumberFor, OptionQuery>; + + #[pallet::storage] + pub type DataDM = + StorageDoubleMap<_, Twox64Concat, u32, Blake2_128Concat, u32, u64, ValueQuery>; + + #[pallet::storage] + pub type GenericDataDM = StorageDoubleMap< + _, + Blake2_128Concat, + BlockNumberFor, + Identity, + BlockNumberFor, + BlockNumberFor, + ValueQuery, + >; + + #[pallet::storage] + pub type GenericData2DM = StorageDoubleMap< + _, + Blake2_128Concat, + BlockNumberFor, + Twox64Concat, + BlockNumberFor, + BlockNumberFor, + OptionQuery, + >; + + #[pallet::storage] + #[pallet::unbounded] + pub type AppendableDM = StorageDoubleMap< + _, + Blake2_128Concat, + u32, + Blake2_128Concat, + BlockNumberFor, + Vec, + ValueQuery, + >; + + #[pallet::genesis_config] + pub struct GenesisConfig { + pub data: Vec<(u32, u64)>, + pub test_config: Vec<(u32, u32, u64)>, + #[serde(skip)] + pub _config: core::marker::PhantomData, + } + + impl Default for GenesisConfig { + fn default() -> Self { + Self { + _config: Default::default(), + data: vec![(15u32, 42u64)], + test_config: vec![(15u32, 16u32, 42u64)], + } + } + } + + #[pallet::genesis_build] + impl BuildGenesisConfig for GenesisConfig { + fn build(&self) { + for (k, v) in &self.data { + >::insert(k, v); + } + for (k1, k2, v) in &self.test_config { + >::insert(k1, k2, v); + } + } + } + + /// Some running total. + #[pallet::storage] + pub type Total = StorageValue<_, (u32, u32), ValueQuery>; + + /// Numbers to be added into the total. + #[pallet::storage] + pub type Numbers = StorageMap<_, Twox64Concat, u32, u32, OptionQuery>; + + pub mod pallet_prelude { + pub type OriginFor = ::RuntimeOrigin; + + pub type HeaderFor = + <::Block as sp_runtime::traits::HeaderProvider>::HeaderT; + + pub type BlockNumberFor = as sp_runtime::traits::Header>::Number; + } +} + +type BlockNumber = u32; +type AccountId = u32; +type Header = generic::Header; +type UncheckedExtrinsic = generic::UncheckedExtrinsic; +type Block = generic::Block; + +#[crate::runtime] +mod runtime { + #[runtime::runtime] + #[runtime::derive( + RuntimeCall, + RuntimeEvent, + RuntimeError, + RuntimeOrigin, + RuntimeFreezeReason, + RuntimeHoldReason, + RuntimeSlashReason, + RuntimeLockId, + RuntimeTask + )] + pub struct Runtime; + + #[runtime::pallet_index(0)] + pub type System = self::frame_system; +} + +#[crate::derive_impl(self::frame_system::config_preludes::TestDefaultConfig as self::frame_system::DefaultConfig)] +impl Config for Runtime { + type Block = Block; + type AccountId = AccountId; +} + +#[cfg(test)] +fn new_test_ext() -> TestExternalities { + RuntimeGenesisConfig::default().build_storage().unwrap().into() +} + +#[cfg(test)] +trait Sorted { + fn sorted(self) -> Self; +} + +#[cfg(test)] +impl Sorted for Vec { + fn sorted(mut self) -> Self { + self.sort(); + self + } +} + +#[test] +fn map_issue_3318() { + new_test_ext().execute_with(|| { + type OptionLinkedMap = self::frame_system::OptionLinkedMap; + + OptionLinkedMap::insert(1, 1); + assert_eq!(OptionLinkedMap::get(1), Some(1)); + OptionLinkedMap::insert(1, 2); + assert_eq!(OptionLinkedMap::get(1), Some(2)); + }); +} + +#[test] +fn map_swap_works() { + new_test_ext().execute_with(|| { + type OptionLinkedMap = self::frame_system::OptionLinkedMap; + + OptionLinkedMap::insert(0, 0); + OptionLinkedMap::insert(1, 1); + OptionLinkedMap::insert(2, 2); + OptionLinkedMap::insert(3, 3); + + let collect = || OptionLinkedMap::iter().collect::>().sorted(); + assert_eq!(collect(), vec![(0, 0), (1, 1), (2, 2), (3, 3)]); + + // Two existing + OptionLinkedMap::swap(1, 2); + assert_eq!(collect(), vec![(0, 0), (1, 2), (2, 1), (3, 3)]); + + // Back to normal + OptionLinkedMap::swap(2, 1); + assert_eq!(collect(), vec![(0, 0), (1, 1), (2, 2), (3, 3)]); + + // Left existing + OptionLinkedMap::swap(2, 5); + assert_eq!(collect(), vec![(0, 0), (1, 1), (3, 3), (5, 2)]); + + // Right existing + OptionLinkedMap::swap(5, 2); + assert_eq!(collect(), vec![(0, 0), (1, 1), (2, 2), (3, 3)]); + }); +} + +#[test] +fn double_map_swap_works() { + new_test_ext().execute_with(|| { + type DataDM = self::frame_system::DataDM; + + DataDM::insert(0, 1, 1); + DataDM::insert(1, 0, 2); + DataDM::insert(1, 1, 3); + + let get_all = || { + vec![ + DataDM::get(0, 1), + DataDM::get(1, 0), + DataDM::get(1, 1), + DataDM::get(2, 0), + DataDM::get(2, 1), + ] + }; + assert_eq!(get_all(), vec![1, 2, 3, 0, 0]); + + // Two existing + DataDM::swap(0, 1, 1, 0); + assert_eq!(get_all(), vec![2, 1, 3, 0, 0]); + + // Left existing + DataDM::swap(1, 0, 2, 0); + assert_eq!(get_all(), vec![2, 0, 3, 1, 0]); + + // Right existing + DataDM::swap(2, 1, 1, 1); + assert_eq!(get_all(), vec![2, 0, 0, 1, 3]); + }); +} + +#[test] +fn map_basic_insert_remove_should_work() { + new_test_ext().execute_with(|| { + type Map = self::frame_system::Data; + + // initialized during genesis + assert_eq!(Map::get(&15u32), 42u64); + + // get / insert / take + let key = 17u32; + assert_eq!(Map::get(&key), 0u64); + Map::insert(key, 4u64); + assert_eq!(Map::get(&key), 4u64); + assert_eq!(Map::take(&key), 4u64); + assert_eq!(Map::get(&key), 0u64); + + // mutate + Map::mutate(&key, |val| { + *val = 15; + }); + assert_eq!(Map::get(&key), 15u64); + + // remove + Map::remove(&key); + assert_eq!(Map::get(&key), 0u64); + }); +} + +#[test] +fn map_iteration_should_work() { + new_test_ext().execute_with(|| { + type Map = self::frame_system::Data; + + assert_eq!(Map::iter().collect::>().sorted(), vec![(15, 42)]); + // insert / remove + let key = 17u32; + Map::insert(key, 4u64); + assert_eq!(Map::iter().collect::>().sorted(), vec![(15, 42), (key, 4)]); + assert_eq!(Map::take(&15), 42u64); + assert_eq!(Map::take(&key), 4u64); + assert_eq!(Map::iter().collect::>().sorted(), vec![]); + + // Add couple of more elements + Map::insert(key, 42u64); + assert_eq!(Map::iter().collect::>().sorted(), vec![(key, 42)]); + Map::insert(key + 1, 43u64); + assert_eq!(Map::iter().collect::>().sorted(), vec![(key, 42), (key + 1, 43)]); + + // mutate + let key = key + 2; + Map::mutate(&key, |val| { + *val = 15; + }); + assert_eq!( + Map::iter().collect::>().sorted(), + vec![(key - 2, 42), (key - 1, 43), (key, 15)] + ); + Map::mutate(&key, |val| { + *val = 17; + }); + assert_eq!( + Map::iter().collect::>().sorted(), + vec![(key - 2, 42), (key - 1, 43), (key, 17)] + ); + + // remove first + Map::remove(&key); + assert_eq!(Map::iter().collect::>().sorted(), vec![(key - 2, 42), (key - 1, 43)]); + + // remove last from the list + Map::remove(&(key - 2)); + assert_eq!(Map::iter().collect::>().sorted(), vec![(key - 1, 43)]); + + // remove the last element + Map::remove(&(key - 1)); + assert_eq!(Map::iter().collect::>().sorted(), vec![]); + }); +} + +#[test] +fn double_map_basic_insert_remove_remove_prefix_with_commit_should_work() { + let key1 = 17u32; + let key2 = 18u32; + type DoubleMap = self::frame_system::DataDM; + let mut e = new_test_ext(); + e.execute_with(|| { + // initialized during genesis + assert_eq!(DoubleMap::get(&15u32, &16u32), 42u64); + + // get / insert / take + assert_eq!(DoubleMap::get(&key1, &key2), 0u64); + DoubleMap::insert(&key1, &key2, &4u64); + assert_eq!(DoubleMap::get(&key1, &key2), 4u64); + assert_eq!(DoubleMap::take(&key1, &key2), 4u64); + assert_eq!(DoubleMap::get(&key1, &key2), 0u64); + + // mutate + DoubleMap::mutate(&key1, &key2, |val| *val = 15); + assert_eq!(DoubleMap::get(&key1, &key2), 15u64); + + // remove + DoubleMap::remove(&key1, &key2); + assert_eq!(DoubleMap::get(&key1, &key2), 0u64); + + // remove prefix + DoubleMap::insert(&key1, &key2, &4u64); + DoubleMap::insert(&key1, &(key2 + 1), &4u64); + DoubleMap::insert(&(key1 + 1), &key2, &4u64); + DoubleMap::insert(&(key1 + 1), &(key2 + 1), &4u64); + }); + e.commit_all().unwrap(); + e.execute_with(|| { + assert!(matches!( + DoubleMap::clear_prefix(&key1, u32::max_value(), None), + MultiRemovalResults { maybe_cursor: None, backend: 2, unique: 2, loops: 2 } + )); + assert_eq!(DoubleMap::get(&key1, &key2), 0u64); + assert_eq!(DoubleMap::get(&key1, &(key2 + 1)), 0u64); + assert_eq!(DoubleMap::get(&(key1 + 1), &key2), 4u64); + assert_eq!(DoubleMap::get(&(key1 + 1), &(key2 + 1)), 4u64); + }); +} + +#[test] +fn double_map_basic_insert_remove_remove_prefix_should_work() { + new_test_ext().execute_with(|| { + let key1 = 17u32; + let key2 = 18u32; + type DoubleMap = self::frame_system::DataDM; + + // initialized during genesis + assert_eq!(DoubleMap::get(&15u32, &16u32), 42u64); + + // get / insert / take + assert_eq!(DoubleMap::get(&key1, &key2), 0u64); + DoubleMap::insert(&key1, &key2, &4u64); + assert_eq!(DoubleMap::get(&key1, &key2), 4u64); + assert_eq!(DoubleMap::take(&key1, &key2), 4u64); + assert_eq!(DoubleMap::get(&key1, &key2), 0u64); + + // mutate + DoubleMap::mutate(&key1, &key2, |val| *val = 15); + assert_eq!(DoubleMap::get(&key1, &key2), 15u64); + + // remove + DoubleMap::remove(&key1, &key2); + assert_eq!(DoubleMap::get(&key1, &key2), 0u64); + + // remove prefix + DoubleMap::insert(&key1, &key2, &4u64); + DoubleMap::insert(&key1, &(key2 + 1), &4u64); + DoubleMap::insert(&(key1 + 1), &key2, &4u64); + DoubleMap::insert(&(key1 + 1), &(key2 + 1), &4u64); + // all in overlay + assert!(matches!( + DoubleMap::clear_prefix(&key1, u32::max_value(), None), + MultiRemovalResults { maybe_cursor: None, backend: 0, unique: 0, loops: 0 } + )); + // Note this is the incorrect answer (for now), since we are using v2 of + // `clear_prefix`. + // When we switch to v3, then this will become: + // MultiRemovalResults:: { maybe_cursor: None, backend: 0, unique: 2, loops: 2 }, + assert!(matches!( + DoubleMap::clear_prefix(&key1, u32::max_value(), None), + MultiRemovalResults { maybe_cursor: None, backend: 0, unique: 0, loops: 0 } + )); + assert_eq!(DoubleMap::get(&key1, &key2), 0u64); + assert_eq!(DoubleMap::get(&key1, &(key2 + 1)), 0u64); + assert_eq!(DoubleMap::get(&(key1 + 1), &key2), 4u64); + assert_eq!(DoubleMap::get(&(key1 + 1), &(key2 + 1)), 4u64); + }); +} + +#[test] +fn double_map_append_should_work() { + new_test_ext().execute_with(|| { + type DoubleMap = self::frame_system::AppendableDM; + + let key1 = 17u32; + let key2 = 18u32; + + DoubleMap::insert(&key1, &key2, &vec![1]); + DoubleMap::append(&key1, &key2, 2); + assert_eq!(DoubleMap::get(&key1, &key2), &[1, 2]); + }); +} + +#[test] +fn double_map_mutate_exists_should_work() { + new_test_ext().execute_with(|| { + type DoubleMap = self::frame_system::DataDM; + + let (key1, key2) = (11, 13); + + // mutated + DoubleMap::mutate_exists(key1, key2, |v| *v = Some(1)); + assert_eq!(DoubleMap::get(&key1, key2), 1); + + // removed if mutated to `None` + DoubleMap::mutate_exists(key1, key2, |v| *v = None); + assert!(!DoubleMap::contains_key(&key1, key2)); + }); +} + +#[test] +fn double_map_try_mutate_exists_should_work() { + new_test_ext().execute_with(|| { + type DoubleMap = self::frame_system::DataDM; + type TestResult = Result<(), &'static str>; + + let (key1, key2) = (11, 13); + + // mutated if `Ok` + assert_ok!(DoubleMap::try_mutate_exists(key1, key2, |v| -> TestResult { + *v = Some(1); + Ok(()) + })); + assert_eq!(DoubleMap::get(&key1, key2), 1); + + // no-op if `Err` + assert_noop!( + DoubleMap::try_mutate_exists(key1, key2, |v| -> TestResult { + *v = Some(2); + Err("nah") + }), + "nah" + ); + + // removed if mutated to`None` + assert_ok!(DoubleMap::try_mutate_exists(key1, key2, |v| -> TestResult { + *v = None; + Ok(()) + })); + assert!(!DoubleMap::contains_key(&key1, key2)); + }); +} + +#[cfg(test)] +fn expected_metadata() -> PalletStorageMetadataIR { + PalletStorageMetadataIR { + prefix: "System", + entries: vec![ + StorageEntryMetadataIR { + name: "Data", + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Map { + hashers: vec![StorageHasherIR::Twox64Concat], + key: scale_info::meta_type::(), + value: scale_info::meta_type::(), + }, + default: vec![0, 0, 0, 0, 0, 0, 0, 0], + docs: vec![], + }, + StorageEntryMetadataIR { + name: "OptionLinkedMap", + modifier: StorageEntryModifierIR::Optional, + ty: StorageEntryTypeIR::Map { + hashers: vec![StorageHasherIR::Blake2_128Concat], + key: scale_info::meta_type::(), + value: scale_info::meta_type::(), + }, + default: vec![0], + docs: vec![], + }, + StorageEntryMetadataIR { + name: "GenericData", + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Map { + hashers: vec![StorageHasherIR::Identity], + key: scale_info::meta_type::(), + value: scale_info::meta_type::(), + }, + default: vec![0, 0, 0, 0], + docs: vec![], + }, + StorageEntryMetadataIR { + name: "GenericData2", + modifier: StorageEntryModifierIR::Optional, + ty: StorageEntryTypeIR::Map { + hashers: vec![StorageHasherIR::Blake2_128Concat], + key: scale_info::meta_type::(), + value: scale_info::meta_type::(), + }, + default: vec![0], + docs: vec![], + }, + StorageEntryMetadataIR { + name: "DataDM", + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Map { + hashers: vec![StorageHasherIR::Twox64Concat, StorageHasherIR::Blake2_128Concat], + key: scale_info::meta_type::<(u32, u32)>(), + value: scale_info::meta_type::(), + }, + default: vec![0, 0, 0, 0, 0, 0, 0, 0], + docs: vec![], + }, + StorageEntryMetadataIR { + name: "GenericDataDM", + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Map { + hashers: vec![StorageHasherIR::Blake2_128Concat, StorageHasherIR::Identity], + key: scale_info::meta_type::<(u32, u32)>(), + value: scale_info::meta_type::(), + }, + default: vec![0, 0, 0, 0], + docs: vec![], + }, + StorageEntryMetadataIR { + name: "GenericData2DM", + modifier: StorageEntryModifierIR::Optional, + ty: StorageEntryTypeIR::Map { + hashers: vec![StorageHasherIR::Blake2_128Concat, StorageHasherIR::Twox64Concat], + key: scale_info::meta_type::<(u32, u32)>(), + value: scale_info::meta_type::(), + }, + default: vec![0], + docs: vec![], + }, + StorageEntryMetadataIR { + name: "AppendableDM", + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Map { + hashers: vec![ + StorageHasherIR::Blake2_128Concat, + StorageHasherIR::Blake2_128Concat, + ], + key: scale_info::meta_type::<(u32, u32)>(), + value: scale_info::meta_type::>(), + }, + default: vec![0], + docs: vec![], + }, + StorageEntryMetadataIR { + name: "Total", + modifier: StorageEntryModifierIR::Default, + ty: StorageEntryTypeIR::Plain(scale_info::meta_type::<(u32, u32)>()), + default: vec![0, 0, 0, 0, 0, 0, 0, 0], + docs: vec![" Some running total."], + }, + StorageEntryMetadataIR { + name: "Numbers", + modifier: StorageEntryModifierIR::Optional, + ty: StorageEntryTypeIR::Map { + hashers: vec![StorageHasherIR::Twox64Concat], + key: scale_info::meta_type::(), + value: scale_info::meta_type::(), + }, + default: vec![0], + docs: vec![" Numbers to be added into the total."], + }, + ], + } +} + +#[test] +fn store_metadata() { + let metadata = Pallet::::storage_metadata(); + pretty_assertions::assert_eq!(expected_metadata(), metadata); +} + +parameter_types! { + storage StorageParameter: u64 = 10; +} + +#[test] +fn check_storage_parameter_type_works() { + TestExternalities::default().execute_with(|| { + assert_eq!(sp_io::hashing::twox_128(b":StorageParameter:"), StorageParameter::key()); + + assert_eq!(10, StorageParameter::get()); + + StorageParameter::set(&300); + assert_eq!(300, StorageParameter::get()); + }) +} + +#[test] +fn derive_partial_eq_no_bound_core_mod() { + mod core {} + + #[derive( + crate::PartialEqNoBound, + crate::CloneNoBound, + crate::DebugNoBound, + crate::DefaultNoBound, + crate::EqNoBound, + )] + struct Test; +} + +fn main() {} diff --git a/substrate/frame/support/src/tests/runtime.rs b/substrate/frame/support/procedural/examples/proc_main/runtime.rs similarity index 100% rename from substrate/frame/support/src/tests/runtime.rs rename to substrate/frame/support/procedural/examples/proc_main/runtime.rs diff --git a/substrate/frame/support/src/tests/tasks.rs b/substrate/frame/support/procedural/examples/proc_main/tasks.rs similarity index 91% rename from substrate/frame/support/src/tests/tasks.rs rename to substrate/frame/support/procedural/examples/proc_main/tasks.rs index 2774c1300757..326c6e5bcbc5 100644 --- a/substrate/frame/support/src/tests/tasks.rs +++ b/substrate/frame/support/procedural/examples/proc_main/tasks.rs @@ -15,12 +15,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{ +#[cfg(test)] +use super::{ assert_ok, - tests::{ - frame_system::{Numbers, Total}, - new_test_ext, Runtime, RuntimeOrigin, RuntimeTask, System, - }, + frame_system::{Numbers, Total}, + Runtime, RuntimeOrigin, RuntimeTask, System, }; use frame_support_procedural::pallet_section; @@ -48,7 +47,7 @@ mod tasks_example { #[docify::export] #[test] fn tasks_work() { - new_test_ext().execute_with(|| { + super::new_test_ext().execute_with(|| { Numbers::::insert(0, 1); let task = RuntimeTask::System(super::frame_system::Task::::AddNumberIntoTotal { diff --git a/substrate/frame/support/procedural/src/lib.rs b/substrate/frame/support/procedural/src/lib.rs index 51e5657a2e8b..ef99faee86ae 100644 --- a/substrate/frame/support/procedural/src/lib.rs +++ b/substrate/frame/support/procedural/src/lib.rs @@ -779,11 +779,20 @@ pub fn register_default_impl(attrs: TokenStream, tokens: TokenStream) -> TokenSt } } -/// -/// --- -/// -/// Documentation for this macro can be found at -/// `frame_support::pallet_prelude::inject_runtime_type`. +/// The optional attribute `#[inject_runtime_type]` can be attached to `RuntimeCall`, +/// `RuntimeEvent`, `RuntimeOrigin` or `PalletInfo` in an impl statement that has +/// `#[register_default_impl]` attached to indicate that this item is generated by +/// `construct_runtime`. +/// +/// Attaching this attribute to such an item ensures that the combined impl generated via +/// [`#[derive_impl(..)]`](macro@derive_impl) will use the correct type auto-generated by +/// `construct_runtime!`. +#[doc = docify::embed!("examples/proc_main/inject_runtime_type.rs", derive_impl_works_with_runtime_type_injection)] +/// +/// However, if `no_aggregated_types` is specified while using +/// [`#[derive_impl(..)]`](macro@derive_impl), then these items are attached verbatim to the +/// combined impl. +#[doc = docify::embed!("examples/proc_main/inject_runtime_type.rs", derive_impl_works_with_no_aggregated_types)] #[proc_macro_attribute] pub fn inject_runtime_type(_: TokenStream, tokens: TokenStream) -> TokenStream { let item = tokens.clone(); @@ -1073,51 +1082,87 @@ pub fn composite_enum(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } +/// Allows you to define some service work that can be recognized by a script or an +/// off-chain worker. /// -/// --- +/// Such a script can then create and submit all such work items at any given time. /// -/// **Rust-Analyzer users**: See the documentation of the Rust item in -/// `frame_support::pallet_macros::tasks_experimental`. +/// These work items are defined as instances of the `Task` trait (found at +/// `frame_support::traits::Task`). [`pallet:tasks_experimental`](macro@tasks_experimental) when +/// attached to an `impl` block inside a pallet, will generate an enum `Task` whose variants +/// are mapped to functions inside this `impl` block. +/// +/// Each such function must have the following set of attributes: +/// +/// * [`pallet::task_list`](macro@task_list) +/// * [`pallet::task_condition`](macro@task_condition) +/// * [`pallet::task_weight`](macro@task_weight) +/// * [`pallet::task_index`](macro@task_index) +/// +/// All of such Tasks are then aggregated into a `RuntimeTask` by +/// [`construct_runtime`](macro@construct_runtime). +/// +/// Finally, the `RuntimeTask` can then used by a script or off-chain worker to create and +/// submit such tasks via an extrinsic defined in `frame_system` called `do_task`. +/// +/// When submitted as unsigned transactions (for example via an off-chain workder), note +/// that the tasks will be executed in a random order. +/// +/// ## Example +#[doc = docify::embed!("examples/proc_main/tasks.rs", tasks_example)] +/// Now, this can be executed as follows: +#[doc = docify::embed!("examples/proc_main/tasks.rs", tasks_work)] #[proc_macro_attribute] pub fn tasks_experimental(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } +/// Allows defining an iterator over available work items for a task. /// -/// --- +/// This attribute is attached to a function inside an `impl` block annotated with +/// [`pallet::tasks_experimental`](macro@tasks_experimental). /// -/// **Rust-Analyzer users**: See the documentation of the Rust item in -/// `frame_support::pallet_macros::task_list`. +/// It takes an iterator as input that yields a tuple with same types as the function +/// arguments. #[proc_macro_attribute] pub fn task_list(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } +/// Allows defining conditions for a task to run. /// -/// --- +/// This attribute is attached to a function inside an `impl` block annotated with +/// [`pallet::tasks_experimental`](macro@tasks_experimental) to define the conditions for a +/// given work item to be valid. /// -/// **Rust-Analyzer users**: See the documentation of the Rust item in -/// `frame_support::pallet_macros::task_condition`. +/// It takes a closure as input, which is then used to define the condition. The closure +/// should have the same signature as the function it is attached to, except that it should +/// return a `bool` instead. #[proc_macro_attribute] pub fn task_condition(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } +/// Allows defining the weight of a task. /// -/// --- +/// This attribute is attached to a function inside an `impl` block annotated with +/// [`pallet::tasks_experimental`](macro@tasks_experimental) define the weight of a given work +/// item. /// -/// **Rust-Analyzer users**: See the documentation of the Rust item in -/// `frame_support::pallet_macros::task_weight`. +/// It takes a closure as input, which should return a `Weight` value. #[proc_macro_attribute] pub fn task_weight(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() } +/// Allows defining an index for a task. /// -/// --- +/// This attribute is attached to a function inside an `impl` block annotated with +/// [`pallet::tasks_experimental`](macro@tasks_experimental) to define the index of a given +/// work item. /// -/// **Rust-Analyzer users**: See the documentation of the Rust item in -/// `frame_support::pallet_macros::task_index`. +/// It takes an integer literal as input, which is then used to define the index. This +/// index should be unique for each function in the `impl` block. #[proc_macro_attribute] pub fn task_index(_: TokenStream, _: TokenStream) -> TokenStream { pallet_macro_stub() @@ -1191,11 +1236,29 @@ pub fn import_section(attr: TokenStream, tokens: TokenStream) -> TokenStream { .into() } +/// Construct a runtime, with the given name and the given pallets. /// -/// --- +/// # Example: +#[doc = docify::embed!("examples/proc_main/runtime.rs", runtime_macro)] /// -/// **Rust-Analyzer users**: See the documentation of the Rust item in -/// `frame_support::runtime`. +/// # Supported Attributes: +/// +/// ## Legacy Ordering +/// +/// An optional attribute can be defined as #[frame_support::runtime(legacy_ordering)] to +/// ensure that the order of hooks is same as the order of pallets (and not based on the +/// pallet_index). This is to support legacy runtimes and should be avoided for new ones. +/// +/// # Note +/// +/// The population of the genesis storage depends on the order of pallets. So, if one of your +/// pallets depends on another pallet, the pallet that is depended upon needs to come before +/// the pallet depending on it. +/// +/// # Type definitions +/// +/// * The macro generates a type alias for each pallet to their `Pallet`. E.g. `type System = +/// frame_system::Pallet` #[proc_macro_attribute] pub fn runtime(attr: TokenStream, item: TokenStream) -> TokenStream { runtime::runtime(attr, item) diff --git a/substrate/frame/support/src/lib.rs b/substrate/frame/support/src/lib.rs index 3ad8c5fabaa2..06696488e298 100644 --- a/substrate/frame/support/src/lib.rs +++ b/substrate/frame/support/src/lib.rs @@ -517,29 +517,6 @@ pub use frame_support_procedural::{ construct_runtime, match_and_insert, transactional, PalletError, RuntimeDebugNoBound, }; -/// Construct a runtime, with the given name and the given pallets. -/// -/// # Example: -#[doc = docify::embed!("src/tests/runtime.rs", runtime_macro)] -/// -/// # Supported Attributes: -/// -/// ## Legacy Ordering -/// -/// An optional attribute can be defined as #[frame_support::runtime(legacy_ordering)] to -/// ensure that the order of hooks is same as the order of pallets (and not based on the -/// pallet_index). This is to support legacy runtimes and should be avoided for new ones. -/// -/// # Note -/// -/// The population of the genesis storage depends on the order of pallets. So, if one of your -/// pallets depends on another pallet, the pallet that is depended upon needs to come before -/// the pallet depending on it. -/// -/// # Type definitions -/// -/// * The macro generates a type alias for each pallet to their `Pallet`. E.g. `type System = -/// frame_system::Pallet` pub use frame_support_procedural::runtime; #[doc(hidden)] @@ -933,26 +910,9 @@ pub mod pallet_prelude { PartialEqNoBound, RuntimeDebugNoBound, Twox128, Twox256, Twox64Concat, }; pub use codec::{Decode, Encode, MaxEncodedLen}; - pub use frame_support::pallet_macros::*; - pub use core::marker::PhantomData; - /// The optional attribute `#[inject_runtime_type]` can be attached to `RuntimeCall`, - /// `RuntimeEvent`, `RuntimeOrigin` or `PalletInfo` in an impl statement that has - /// `#[register_default_impl]` attached to indicate that this item is generated by - /// `construct_runtime`. - /// - /// Attaching this attribute to such an item ensures that the combined impl generated via - /// [`#[derive_impl(..)]`](`frame_support::derive_impl`) will use the correct - /// type auto-generated by - /// `construct_runtime!`. - #[doc = docify::embed!("src/tests/inject_runtime_type.rs", derive_impl_works_with_runtime_type_injection)] - /// - /// However, if `no_aggregated_types` is specified while using - /// `[`#[derive_impl(..)]`](`frame_support::derive_impl`)`, then these items are attached - /// verbatim to the combined impl. - #[doc = docify::embed!("src/tests/inject_runtime_type.rs", derive_impl_works_with_no_aggregated_types)] - pub use frame_support_procedural::inject_runtime_type; - pub use frame_support_procedural::register_default_impl; + pub use frame_support::pallet_macros::*; + pub use frame_support_procedural::{inject_runtime_type, register_default_impl}; pub use scale_info::TypeInfo; pub use sp_inherents::MakeFatalError; pub use sp_runtime::{ @@ -2446,76 +2406,9 @@ pub mod pallet_macros { /// ``` pub use frame_support_procedural::storage; - /// Allows defining conditions for a task to run. - /// - /// This attribute is attached to a function inside an `impl` block annotated with - /// [`pallet::tasks_experimental`](`tasks_experimental`) to define the conditions for a - /// given work item to be valid. - /// - /// It takes a closure as input, which is then used to define the condition. The closure - /// should have the same signature as the function it is attached to, except that it should - /// return a `bool` instead. - pub use frame_support_procedural::task_condition; - - /// Allows defining an index for a task. - /// - /// This attribute is attached to a function inside an `impl` block annotated with - /// [`pallet::tasks_experimental`](`tasks_experimental`) to define the index of a given - /// work item. - /// - /// It takes an integer literal as input, which is then used to define the index. This - /// index should be unique for each function in the `impl` block. - pub use frame_support_procedural::task_index; - - /// Allows defining an iterator over available work items for a task. - /// - /// This attribute is attached to a function inside an `impl` block annotated with - /// [`pallet::tasks_experimental`](`tasks_experimental`). - /// - /// It takes an iterator as input that yields a tuple with same types as the function - /// arguments. - pub use frame_support_procedural::task_list; - - /// Allows defining the weight of a task. - /// - /// This attribute is attached to a function inside an `impl` block annotated with - /// [`pallet::tasks_experimental`](`tasks_experimental`) define the weight of a given work - /// item. - /// - /// It takes a closure as input, which should return a `Weight` value. - pub use frame_support_procedural::task_weight; - - /// Allows you to define some service work that can be recognized by a script or an - /// off-chain worker. - /// - /// Such a script can then create and submit all such work items at any given time. - /// - /// These work items are defined as instances of the [`Task`](frame_support::traits::Task) - /// trait. [`pallet:tasks_experimental`](`tasks_experimental`) when attached to an `impl` - /// block inside a pallet, will generate an enum `Task` whose variants are mapped to - /// functions inside this `impl` block. - /// - /// Each such function must have the following set of attributes: - /// - /// * [`pallet::task_list`](`task_list`) - /// * [`pallet::task_condition`](`task_condition`) - /// * [`pallet::task_weight`](`task_weight`) - /// * [`pallet::task_index`](`task_index`) - /// - /// All of such Tasks are then aggregated into a `RuntimeTask` by - /// [`construct_runtime`](frame_support::construct_runtime). - /// - /// Finally, the `RuntimeTask` can then used by a script or off-chain worker to create and - /// submit such tasks via an extrinsic defined in `frame_system` called `do_task`. - /// - /// When submitted as unsigned transactions (for example via an off-chain workder), note - /// that the tasks will be executed in a random order. - /// - /// ## Example - #[doc = docify::embed!("src/tests/tasks.rs", tasks_example)] - /// Now, this can be executed as follows: - #[doc = docify::embed!("src/tests/tasks.rs", tasks_work)] - pub use frame_support_procedural::tasks_experimental; + pub use frame_support_procedural::{ + task_condition, task_index, task_list, task_weight, tasks_experimental, + }; /// Allows a pallet to declare a type as an origin. /// diff --git a/substrate/frame/support/src/tests/mod.rs b/substrate/frame/support/src/tests/mod.rs index 34652231e3bc..a9f8ebdd63f2 100644 --- a/substrate/frame/support/src/tests/mod.rs +++ b/substrate/frame/support/src/tests/mod.rs @@ -16,7 +16,6 @@ // limitations under the License. use super::*; -use frame_support_procedural::import_section; use sp_io::{MultiRemovalResults, TestExternalities}; use sp_metadata_ir::{ PalletStorageMetadataIR, StorageEntryMetadataIR, StorageEntryModifierIR, StorageEntryTypeIR, @@ -26,12 +25,8 @@ use sp_runtime::{generic, traits::BlakeTwo256, BuildStorage}; pub use self::frame_system::{pallet_prelude::*, Config, Pallet}; -mod inject_runtime_type; -mod runtime; mod storage_alias; -mod tasks; -#[import_section(tasks::tasks_example)] #[pallet] pub mod frame_system { #[allow(unused)]