diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index b8aa5674ddde5..25df8410b02d1 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -468,7 +468,8 @@ pub fn construct_runtime(input: TokenStream) -> TokenStream { /// storage types. This is equivalent to specifying `#[pallet::unbounded]` on all storage type /// definitions. /// * Storage hashers no longer need to be specified and can be replaced by `_`. In dev mode, these -/// will be replaced by `Blake2_128Concat`. +/// will be replaced by `Blake2_128Concat`. In case of explicit key-binding, `Hasher` can simply +/// be ignored when in `dev_mode`. /// /// Note that the `dev_mode` argument can only be supplied to the `#[pallet]` or /// `#[frame_support::pallet]` attribute macro that encloses your pallet module. This argument diff --git a/frame/support/procedural/src/pallet/parse/storage.rs b/frame/support/procedural/src/pallet/parse/storage.rs index 16c0851ce7a04..12e06b214b6b6 100644 --- a/frame/support/procedural/src/pallet/parse/storage.rs +++ b/frame/support/procedural/src/pallet/parse/storage.rs @@ -332,6 +332,7 @@ fn process_named_generics( storage: &StorageKind, args_span: proc_macro2::Span, args: &[syn::AssocType], + dev_mode: bool, ) -> syn::Result<(Option, Metadata, Option, bool)> { let mut parsed = HashMap::::new(); @@ -346,6 +347,14 @@ fn process_named_generics( parsed.insert(arg.ident.to_string(), arg.clone()); } + let mut map_mandatory_generics = vec!["Key", "Value"]; + let mut map_optional_generics = vec!["QueryKind", "OnEmpty", "MaxValues"]; + if dev_mode { + map_optional_generics.push("Hasher"); + } else { + map_mandatory_generics.push("Hasher"); + } + let generics = match storage { StorageKind::Value => { check_generics( @@ -368,8 +377,8 @@ fn process_named_generics( StorageKind::Map => { check_generics( &parsed, - &["Hasher", "Key", "Value"], - &["QueryKind", "OnEmpty", "MaxValues"], + &map_mandatory_generics, + &map_optional_generics, "StorageMap", args_span, )?; @@ -378,7 +387,7 @@ fn process_named_generics( hasher: parsed .remove("Hasher") .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), + .unwrap_or(syn::parse_quote!(Blake2_128Concat)), key: parsed .remove("Key") .map(|binding| binding.ty) @@ -395,8 +404,8 @@ fn process_named_generics( StorageKind::CountedMap => { check_generics( &parsed, - &["Hasher", "Key", "Value"], - &["QueryKind", "OnEmpty", "MaxValues"], + &map_mandatory_generics, + &map_optional_generics, "CountedStorageMap", args_span, )?; @@ -405,7 +414,7 @@ fn process_named_generics( hasher: parsed .remove("Hasher") .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), + .unwrap_or(syn::Type::Verbatim(quote::quote! { Blake2_128Concat })), key: parsed .remove("Key") .map(|binding| binding.ty) @@ -420,10 +429,17 @@ fn process_named_generics( } }, StorageKind::DoubleMap => { + let mut double_map_mandatory_generics = vec!["Key1", "Key2", "Value"]; + if dev_mode { + map_optional_generics.extend(["Hasher1", "Hasher2"]); + } else { + double_map_mandatory_generics.extend(["Hasher1", "Hasher2"]); + } + check_generics( &parsed, - &["Hasher1", "Key1", "Hasher2", "Key2", "Value"], - &["QueryKind", "OnEmpty", "MaxValues"], + &double_map_mandatory_generics, + &map_optional_generics, "StorageDoubleMap", args_span, )?; @@ -432,7 +448,7 @@ fn process_named_generics( hasher1: parsed .remove("Hasher1") .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), + .unwrap_or(syn::parse_quote!(Blake2_128Concat)), key1: parsed .remove("Key1") .map(|binding| binding.ty) @@ -440,7 +456,7 @@ fn process_named_generics( hasher2: parsed .remove("Hasher2") .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), + .unwrap_or(syn::parse_quote!(Blake2_128Concat)), key2: parsed .remove("Key2") .map(|binding| binding.ty) @@ -619,7 +635,7 @@ fn process_generics( _ => unreachable!("It is asserted above that all generics are bindings"), }) .collect::>(); - process_named_generics(&storage_kind, args_span, &args) + process_named_generics(&storage_kind, args_span, &args, dev_mode) } else { let msg = "Invalid pallet::storage, invalid generic declaration for storage. Expect only \ type generics or binding generics, e.g. `` or \ diff --git a/frame/support/test/tests/pallet_ui/non_dev_mode_storage_map_explicit_key_default_hasher.rs b/frame/support/test/tests/pallet_ui/non_dev_mode_storage_map_explicit_key_default_hasher.rs new file mode 100644 index 0000000000000..7d8be8ec00174 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/non_dev_mode_storage_map_explicit_key_default_hasher.rs @@ -0,0 +1,33 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +pub use pallet::*; + +#[frame_support::pallet] +pub mod pallet { + use frame_support::pallet_prelude::*; + + // The struct on which we build all of our Pallet logic. + #[pallet::pallet] + pub struct Pallet(_); + + // Your Pallet's configuration trait, representing custom external types and interfaces. + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::storage] + type MyStorage = StorageValue<_, Vec>; + + #[pallet::storage] + type MyStorageMap = StorageMap; + + #[pallet::storage] + type MyStorageDoubleMap = StorageDoubleMap; + + #[pallet::storage] + type MyCountedStorageMap = CountedStorageMap; + + // Your Pallet's internal functions. + impl Pallet {} +} + +fn main() {} diff --git a/frame/support/test/tests/pallet_ui/non_dev_mode_storage_map_explicit_key_default_hasher.stderr b/frame/support/test/tests/pallet_ui/non_dev_mode_storage_map_explicit_key_default_hasher.stderr new file mode 100644 index 0000000000000..68751470a3e2f --- /dev/null +++ b/frame/support/test/tests/pallet_ui/non_dev_mode_storage_map_explicit_key_default_hasher.stderr @@ -0,0 +1,11 @@ +error: Invalid pallet::storage, cannot find `Hasher` generic, required for `StorageMap`. + --> tests/pallet_ui/non_dev_mode_storage_map_explicit_key_default_hasher.rs:21:43 + | +21 | type MyStorageMap = StorageMap; + | ^ + +error[E0432]: unresolved import `pallet` + --> tests/pallet_ui/non_dev_mode_storage_map_explicit_key_default_hasher.rs:3:9 + | +3 | pub use pallet::*; + | ^^^^^^ help: a similar path exists: `test_pallet::pallet` diff --git a/frame/support/test/tests/pallet_ui/pass/dev_mode_valid.rs b/frame/support/test/tests/pallet_ui/pass/dev_mode_valid.rs index 483ed9579bd0a..28b901213943c 100644 --- a/frame/support/test/tests/pallet_ui/pass/dev_mode_valid.rs +++ b/frame/support/test/tests/pallet_ui/pass/dev_mode_valid.rs @@ -35,6 +35,15 @@ pub mod pallet { #[pallet::storage] type MyCountedStorageMap = CountedStorageMap<_, _, u32, u64>; + #[pallet::storage] + pub type MyStorageMap2 = StorageMap; + + #[pallet::storage] + type MyStorageDoubleMap2 = StorageDoubleMap; + + #[pallet::storage] + type MyCountedStorageMap2 = CountedStorageMap; + // Your Pallet's callable functions. #[pallet::call] impl Pallet {