From 0f674bc288bcb93b1c01a133f9497ccc8d31e20c Mon Sep 17 00:00:00 2001 From: thiolliere Date: Thu, 6 May 2021 15:55:29 +0200 Subject: [PATCH 1/2] implement named generic for storages --- .../procedural/src/pallet/expand/storage.rs | 100 +++-- .../procedural/src/pallet/parse/storage.rs | 352 ++++++++++++++---- frame/support/src/lib.rs | 45 ++- frame/support/test/tests/pallet.rs | 10 +- ...storage_ensure_span_are_ok_on_wrong_gen.rs | 25 ++ ...age_ensure_span_are_ok_on_wrong_gen.stderr | 33 ++ .../storage_invalid_first_generic.stderr | 2 +- .../pallet_ui/storage_not_storage_type.stderr | 2 +- .../storage_value_duplicate_named_generic.rs | 23 ++ ...orage_value_duplicate_named_generic.stderr | 11 + ...storage_value_generic_named_and_unnamed.rs | 23 ++ ...age_value_generic_named_and_unnamed.stderr | 5 + .../pallet_ui/storage_value_no_generic.stderr | 2 +- .../storage_value_unexpected_named_generic.rs | 23 ++ ...rage_value_unexpected_named_generic.stderr | 5 + .../tests/pallet_ui/storage_wrong_item.stderr | 2 +- 16 files changed, 555 insertions(+), 108 deletions(-) create mode 100644 frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen.rs create mode 100644 frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen.stderr create mode 100644 frame/support/test/tests/pallet_ui/storage_value_duplicate_named_generic.rs create mode 100644 frame/support/test/tests/pallet_ui/storage_value_duplicate_named_generic.stderr create mode 100644 frame/support/test/tests/pallet_ui/storage_value_generic_named_and_unnamed.rs create mode 100644 frame/support/test/tests/pallet_ui/storage_value_generic_named_and_unnamed.stderr create mode 100644 frame/support/test/tests/pallet_ui/storage_value_unexpected_named_generic.rs create mode 100644 frame/support/test/tests/pallet_ui/storage_value_unexpected_named_generic.stderr diff --git a/frame/support/procedural/src/pallet/expand/storage.rs b/frame/support/procedural/src/pallet/expand/storage.rs index 86fb84b339b24..50610a0118d61 100644 --- a/frame/support/procedural/src/pallet/expand/storage.rs +++ b/frame/support/procedural/src/pallet/expand/storage.rs @@ -16,7 +16,7 @@ // limitations under the License. use crate::pallet::Def; -use crate::pallet::parse::storage::{Metadata, QueryKind}; +use crate::pallet::parse::storage::{Metadata, QueryKind, StorageGenerics}; use frame_support_procedural_tools::clean_type_string; /// Generate the prefix_ident related the the storage. @@ -25,50 +25,96 @@ fn prefix_ident(storage_ident: &syn::Ident) -> syn::Ident { syn::Ident::new(&format!("_GeneratedPrefixForStorage{}", storage_ident), storage_ident.span()) } -/// * generate StoragePrefix structs (e.g. for a storage `MyStorage` a struct with the name -/// `_GeneratedPrefixForStorage$NameOfStorage` is generated) and implements StorageInstance trait. -/// * replace the first generic `_` by the generated prefix structure -/// * generate metadatas -pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { +/// * if generics are unnamed: replace the first generic `_` by the generated prefix structure +/// * if generics are named: reorder the generic, remove their name, and add the missing ones. +/// * Add `#[allow(type_alias_bounds)]` +pub fn process_generics(def: &mut Def) { let frame_support = &def.frame_support; - let frame_system = &def.frame_system; - let pallet_ident = &def.pallet_struct.pallet; - - // Replace first arg `_` by the generated prefix structure. - // Add `#[allow(type_alias_bounds)]` for storage_def in def.storages.iter_mut() { let item = &mut def.item.content.as_mut().expect("Checked by def").1[storage_def.index]; - let typ_item = if let syn::Item::Type(t) = item { - t - } else { - unreachable!("Checked by def"); + let typ_item = match item { + syn::Item::Type(t) => t, + _ => unreachable!("Checked by def"), }; typ_item.attrs.push(syn::parse_quote!(#[allow(type_alias_bounds)])); - let typ_path = if let syn::Type::Path(p) = &mut *typ_item.ty { - p - } else { - unreachable!("Checked by def"); + let typ_path = match &mut *typ_item.ty { + syn::Type::Path(p) => p, + _ => unreachable!("Checked by def"), }; - let args = if let syn::PathArguments::AngleBracketed(args) = - &mut typ_path.path.segments[0].arguments - { - args - } else { - unreachable!("Checked by def"); + let args = match &mut typ_path.path.segments[0].arguments { + syn::PathArguments::AngleBracketed(args) => args, + _ => unreachable!("Checked by def"), }; + let prefix_ident = prefix_ident(&storage_def.ident); let type_use_gen = if def.config.has_instance { quote::quote_spanned!(storage_def.attr_span => T, I) } else { quote::quote_spanned!(storage_def.attr_span => T) }; - let prefix_ident = prefix_ident(&storage_def.ident); - args.args[0] = syn::parse_quote!( #prefix_ident<#type_use_gen> ); + + let default_query_kind: syn::Type = + syn::parse_quote!(#frame_support::storage::types::OptionQuery); + let default_on_empty: syn::Type = + syn::parse_quote!(#frame_support::traits::GetDefault); + + if let Some(named_generics) = storage_def.named_generics.clone() { + args.args.clear(); + args.args.push(syn::parse_quote!( #prefix_ident<#type_use_gen> )); + match named_generics { + StorageGenerics::Value { value, query_kind, on_empty } => { + args.args.push(syn::GenericArgument::Type(value)); + let query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone()); + args.args.push(syn::GenericArgument::Type(query_kind)); + let on_empty = on_empty.unwrap_or_else(|| default_on_empty.clone()); + args.args.push(syn::GenericArgument::Type(on_empty)); + } + StorageGenerics::Map { hasher, key, value, query_kind, on_empty } => { + args.args.push(syn::GenericArgument::Type(hasher)); + args.args.push(syn::GenericArgument::Type(key)); + args.args.push(syn::GenericArgument::Type(value)); + let query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone()); + args.args.push(syn::GenericArgument::Type(query_kind)); + let on_empty = on_empty.unwrap_or_else(|| default_on_empty.clone()); + args.args.push(syn::GenericArgument::Type(on_empty)); + } + StorageGenerics::DoubleMap { + hasher1, key1, hasher2, key2, value, query_kind, on_empty, + } => { + args.args.push(syn::GenericArgument::Type(hasher1)); + args.args.push(syn::GenericArgument::Type(key1)); + args.args.push(syn::GenericArgument::Type(hasher2)); + args.args.push(syn::GenericArgument::Type(key2)); + args.args.push(syn::GenericArgument::Type(value)); + let query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone()); + args.args.push(syn::GenericArgument::Type(query_kind)); + let on_empty = on_empty.unwrap_or_else(|| default_on_empty.clone()); + args.args.push(syn::GenericArgument::Type(on_empty)); + } + } + } else { + args.args[0] = syn::parse_quote!( #prefix_ident<#type_use_gen> ); + } } +} + +/// * generate StoragePrefix structs (e.g. for a storage `MyStorage` a struct with the name +/// `_GeneratedPrefixForStorage$NameOfStorage` is generated) and implements StorageInstance trait. +/// * if generics are unnamed: replace the first generic `_` by the generated prefix structure +/// * if generics are named: reorder the generic, remove their name, and add the missing ones. +/// * Add `#[allow(type_alias_bounds)]` on storages type alias +/// * generate metadatas +pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { + process_generics(def); + + let frame_support = &def.frame_support; + let frame_system = &def.frame_system; + let pallet_ident = &def.pallet_struct.pallet; + let entries = def.storages.iter() .map(|storage| { diff --git a/frame/support/procedural/src/pallet/parse/storage.rs b/frame/support/procedural/src/pallet/parse/storage.rs index 41ef337b76615..205b4c1e0613c 100644 --- a/frame/support/procedural/src/pallet/parse/storage.rs +++ b/frame/support/procedural/src/pallet/parse/storage.rs @@ -18,6 +18,7 @@ use super::helper; use syn::spanned::Spanned; use quote::ToTokens; +use std::collections::HashMap; /// List of additional token to be used for parsing. mod keyword { @@ -51,12 +52,12 @@ impl syn::parse::Parse for PalletStorageAttr { /// The value and key types used by storages. Needed to expand metadata. pub enum Metadata{ - Value { value: syn::GenericArgument }, - Map { value: syn::GenericArgument, key: syn::GenericArgument }, + Value { value: syn::Type }, + Map { value: syn::Type, key: syn::Type }, DoubleMap { - value: syn::GenericArgument, - key1: syn::GenericArgument, - key2: syn::GenericArgument + value: syn::Type, + key1: syn::Type, + key2: syn::Type }, } @@ -93,24 +94,287 @@ pub struct StorageDef { pub attr_span: proc_macro2::Span, /// The `cfg` attributes. pub cfg_attrs: Vec, + /// If generics are named (e.g. `StorageValue`) then this contains all the + /// generics of the storage. + /// If generics are not named, this is none. + pub named_generics: Option, } -/// In `Foo` retrieve the argument at given position, i.e. A is argument at position 0. -fn retrieve_arg( - segment: &syn::PathSegment, - arg_pos: usize, -) -> syn::Result { - if let syn::PathArguments::AngleBracketed(args) = &segment.arguments { - if arg_pos < args.args.len() { - Ok(args.args[arg_pos].clone()) + +/// The parsed generic from the +#[derive(Clone)] +pub enum StorageGenerics { + DoubleMap { + hasher1: syn::Type, + key1: syn::Type, + hasher2: syn::Type, + key2: syn::Type, + value: syn::Type, + query_kind: Option, + on_empty: Option, + }, + Map { + hasher: syn::Type, + key: syn::Type, + value: syn::Type, + query_kind: Option, + on_empty: Option, + }, + Value { + value: syn::Type, + query_kind: Option, + on_empty: Option, + }, +} + +impl StorageGenerics { + /// Return the metadata from the defined generics + fn metadata(&self) -> Metadata { + match self.clone() { + Self::DoubleMap { value, key1, key2, .. } => Metadata::DoubleMap { value, key1, key2 }, + Self::Map { value, key, .. } => Metadata::Map { value, key }, + Self::Value { value, .. } => Metadata::Value { value }, + } + } + + /// Return the query kind from the defined generics + fn query_kind(&self) -> Option { + match &self { + Self::DoubleMap { query_kind, .. } + | Self::Map { query_kind, .. } + | Self::Value { query_kind, .. } + => query_kind.clone(), + } + } +} + +enum StorageKind { + Value, + Map, + DoubleMap +} + +/// Returns `(named generics, metadata, query kind)` +fn process_named_generics( + storage: &StorageKind, + args_span: proc_macro2::Span, + args: &[syn::Binding], +) -> syn::Result<(Option, Metadata, Option)> { + let mut parsed = HashMap::::new(); + + // Ensure no duplicate. + for arg in args { + if let Some(other) = parsed.get(&arg.ident.to_string()) { + let msg = "Invalid pallet::storage, Duplicated named generic"; + let mut err = syn::Error::new(arg.ident.span(), msg); + err.combine(syn::Error::new(other.ident.span(), msg)); + return Err(err); + } + parsed.insert(arg.ident.to_string(), arg.clone()); + } + + // Error on unfound mandatory generic + let unfound_error = |storage_name: &str, generic_name: &str| { + let msg = format!("Invalid `{}`, cannot find `{}` generic", storage_name, generic_name); + syn::Error::new(args_span, msg) + }; + + let unexpected_remainings = |storage_name: &str, unexpected: HashMap<_, syn::Binding>| { + if unexpected.len() != 0 { + let mut unexpected = unexpected.values(); + let msg = |gen| { + format!( + "Invalid pallet::storage, Unexpected generic `{}` for `{}`.", + gen, storage_name, + ) + }; + + let first = unexpected.next().expect("not empty as checked above"); + + let mut error = syn::Error::new(first.ident.span(), msg(first.ident.clone())); + for other in unexpected { + error.combine(syn::Error::new(other.ident.span(), msg(other.ident.clone()))); + } + Err(error) } else { - let msg = format!("pallet::storage unexpected number of generic argument, expected at \ - least {} args, found {}", arg_pos + 1, args.args.len()); - Err(syn::Error::new(args.span(), msg)) + Ok(()) + } + }; + + let generics = match storage { + StorageKind::Value => { + let generics = StorageGenerics::Value { + value: parsed.remove("Value") + .map(|binding| binding.ty) + .ok_or_else(|| unfound_error("StorageValue", "Value"))?, + query_kind: parsed.remove("QueryKind") + .map(|binding| binding.ty), + on_empty: parsed.remove("OnEmpty") + .map(|binding| binding.ty), + }; + unexpected_remainings("StorageValue", parsed)?; + generics + } + StorageKind::Map => { + let generics = StorageGenerics::Map { + hasher: parsed.remove("Hasher") + .map(|binding| binding.ty) + .ok_or_else(|| unfound_error("StorageMap", "Hasher"))?, + key: parsed.remove("Key") + .map(|binding| binding.ty) + .ok_or_else(|| unfound_error("StorageMap", "Key"))?, + value: parsed.remove("Value") + .map(|binding| binding.ty) + .ok_or_else(|| unfound_error("StorageMap", "Value"))?, + query_kind: parsed.remove("QueryKind") + .map(|binding| binding.ty), + on_empty: parsed.remove("OnEmpty") + .map(|binding| binding.ty), + }; + unexpected_remainings("StorageMap", parsed)?; + generics + } + StorageKind::DoubleMap => { + let generics = StorageGenerics::DoubleMap { + hasher1: parsed.remove("Hasher1") + .map(|binding| binding.ty) + .ok_or_else(|| unfound_error("StorageDoubleMap", "Hasher1"))?, + key1: parsed.remove("Key1") + .map(|binding| binding.ty) + .ok_or_else(|| unfound_error("StorageDoubleMap", "Key1"))?, + hasher2: parsed.remove("Hasher2") + .map(|binding| binding.ty) + .ok_or_else(|| unfound_error("StorageDoubleMap", "Hasher2"))?, + key2: parsed.remove("Key2") + .map(|binding| binding.ty) + .ok_or_else(|| unfound_error("StorageDoubleMap", "Key2"))?, + value: parsed.remove("Value") + .map(|binding| binding.ty) + .ok_or_else(|| unfound_error("StorageDoubleMap", "Value"))?, + query_kind: parsed.remove("QueryKind") + .map(|binding| binding.ty), + on_empty: parsed.remove("OnEmpty") + .map(|binding| binding.ty), + }; + unexpected_remainings("StorageDoubleMap", parsed)?; + generics } + }; + + let metadata = generics.metadata(); + let query_kind = generics.query_kind(); + + Ok((Some(generics), metadata, query_kind)) +} + +/// Returns `(named generics, metadata, query kind)` +fn process_unnamed_generics( + storage: &StorageKind, + args_span: proc_macro2::Span, + args: &[syn::Type], +) -> syn::Result<(Option, Metadata, Option)> { + let retrieve_arg = |arg_pos| { + args.get(arg_pos) + .cloned() + .ok_or_else(|| { + let msg = format!( + "Invalid pallet::storage, unexpected number of generic argument, \ + expect at least {} args, found {}.", + arg_pos + 1, + args.len(), + ); + syn::Error::new(args_span, msg) + }) + }; + + let prefix_arg = retrieve_arg(0)?; + syn::parse2::(prefix_arg.to_token_stream()) + .map_err(|e| { + let msg = "Invalid pallet::storage, for unnamed generic arguments the type \ + first generic argument must be `_`, the argument is then replaced by macro."; + let mut err = syn::Error::new(prefix_arg.span(), msg); + err.combine(e); + err + })?; + + let res = match storage { + StorageKind::Value => ( + None, + Metadata::Value { value: retrieve_arg(1)? }, + retrieve_arg(2).ok(), + ), + StorageKind::Map => ( + None, + Metadata::Map { + key: retrieve_arg(2)?, + value: retrieve_arg(3)?, + }, + retrieve_arg(4).ok(), + ), + StorageKind::DoubleMap => ( + None, + Metadata::DoubleMap { + key1: retrieve_arg(2)?, + key2: retrieve_arg(4)?, + value: retrieve_arg(5)?, + }, + retrieve_arg(6).ok(), + ), + }; + + Ok(res) +} + +/// Returns `(named generics, metadata, query kind)` +fn process_generics( + segment: &syn::PathSegment, +) -> syn::Result<(Option, Metadata, Option)> { + let storage_kind = match &*segment.ident.to_string() { + "StorageValue" => StorageKind::Value, + "StorageMap" => StorageKind::Map, + "StorageDoubleMap" => StorageKind::DoubleMap, + found => { + let msg = format!( + "Invalid pallet::storage, expected ident: `StorageValue` or \ + `StorageMap` or `StorageDoubleMap` in order to expand metadata, found \ + `{}`.", + found, + ); + return Err(syn::Error::new(segment.ident.span(), msg)); + } + }; + + let args_span = segment.arguments.span(); + + let args = match &segment.arguments { + syn::PathArguments::AngleBracketed(args) if args.args.len() != 0 => args, + _ => { + let msg = "Invalid pallet::storage, invalid number of generic generic arguments, \ + expect more that 0 generic arguments."; + return Err(syn::Error::new(segment.span(), msg)); + } + }; + + if args.args.iter().all(|gen| matches!(gen, syn::GenericArgument::Type(_))) { + let args = args.args.iter() + .map(|gen| match gen { + syn::GenericArgument::Type(gen) => gen.clone(), + _ => unreachable!("It is asserted above that all generics are types"), + }) + .collect::>(); + process_unnamed_generics(&storage_kind, args_span, &args) + } else if args.args.iter().all(|gen| matches!(gen, syn::GenericArgument::Binding(_))) { + let args = args.args.iter() + .map(|gen| match gen { + syn::GenericArgument::Binding(gen) => gen.clone(), + _ => unreachable!("It is asserted above that all generics are bindings"), + }) + .collect::>(); + process_named_generics(&storage_kind, args_span, &args) } else { - let msg = format!("pallet::storage unexpected number of generic argument, expected at \ - least {} args, found none", arg_pos + 1); + let msg = "Invalid pallet::storage, invalid generic declaration for storage. Expect only \ + type generics or binding generics, e.g. `` or \ + ``."; Err(syn::Error::new(segment.span(), msg)) } } @@ -124,7 +388,7 @@ impl StorageDef { let item = if let syn::Item::Type(item) = item { item } else { - return Err(syn::Error::new(item.span(), "Invalid pallet::storage, expected item type")); + return Err(syn::Error::new(item.span(), "Invalid pallet::storage, expect item type.")); }; let mut attrs: Vec = helper::take_item_pallet_attrs(&mut item.attrs)?; @@ -154,45 +418,14 @@ impl StorageDef { return Err(syn::Error::new(item.ty.span(), msg)); } - let query_kind; - let metadata = match &*typ.path.segments[0].ident.to_string() { - "StorageValue" => { - query_kind = retrieve_arg(&typ.path.segments[0], 2); - Metadata::Value { - value: retrieve_arg(&typ.path.segments[0], 1)?, - } - } - "StorageMap" => { - query_kind = retrieve_arg(&typ.path.segments[0], 4); - Metadata::Map { - key: retrieve_arg(&typ.path.segments[0], 2)?, - value: retrieve_arg(&typ.path.segments[0], 3)?, - } - } - "StorageDoubleMap" => { - query_kind = retrieve_arg(&typ.path.segments[0], 6); - Metadata::DoubleMap { - key1: retrieve_arg(&typ.path.segments[0], 2)?, - key2: retrieve_arg(&typ.path.segments[0], 4)?, - value: retrieve_arg(&typ.path.segments[0], 5)?, - } - } - found => { - let msg = format!( - "Invalid pallet::storage, expected ident: `StorageValue` or \ - `StorageMap` or `StorageDoubleMap` in order to expand metadata, found \ - `{}`", - found, - ); - return Err(syn::Error::new(item.ty.span(), msg)); - } - }; + let (named_generics, metadata, query_kind) = process_generics(&typ.path.segments[0])?; + let query_kind = query_kind .map(|query_kind| match query_kind { - syn::GenericArgument::Type(syn::Type::Path(path)) + syn::Type::Path(path) if path.path.segments.last().map_or(false, |s| s.ident == "OptionQuery") => Some(QueryKind::OptionQuery), - syn::GenericArgument::Type(syn::Type::Path(path)) + syn::Type::Path(path) if path.path.segments.last().map_or(false, |s| s.ident == "ValueQuery") => Some(QueryKind::ValueQuery), _ => None, @@ -206,16 +439,6 @@ impl StorageDef { return Err(syn::Error::new(getter.unwrap().span(), msg)); } - let prefix_arg = retrieve_arg(&typ.path.segments[0], 0)?; - syn::parse2::(prefix_arg.to_token_stream()) - .map_err(|e| { - let msg = "Invalid use of `#[pallet::storage]`, the type first generic argument \ - must be `_`, the final argument is automatically set by macro."; - let mut err = syn::Error::new(prefix_arg.span(), msg); - err.combine(e); - err - })?; - Ok(StorageDef { attr_span, index, @@ -228,6 +451,7 @@ impl StorageDef { query_kind, where_clause, cfg_attrs, + named_generics, }) } } diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 77163755ac56f..17180adb2f5c1 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -1562,11 +1562,28 @@ pub mod pallet_prelude { /// #[pallet::storage] /// #[pallet::getter(fn $getter_name)] // optional /// $vis type $StorageName<$some_generic> $optional_where_clause +/// = $StorageType<$generic_name = $some_generics, $other_name = $some_other, ...>; +/// ``` +/// or with unnamed generic +/// ```ignore +/// #[pallet::storage] +/// #[pallet::getter(fn $getter_name)] // optional +/// $vis type $StorageName<$some_generic> $optional_where_clause /// = $StorageType<_, $some_generics, ...>; /// ``` /// I.e. it must be a type alias, with generics: `T` or `T: Config`, aliased type must be one /// of `StorageValue`, `StorageMap` or `StorageDoubleMap` (defined in frame_support). -/// Their first generic must be `_` as it is written by the macro itself. +/// The generic arguments of the storage type can be given in two manner: named and unnamed. +/// For named generic argument: the name for each argument is the one as define on the storage +/// struct: +/// * [`pallet_prelude::StorageValue`] expect `Value` and optionally `QueryKind` and `OnEmpty`, +/// * [`pallet_prelude::StorageMap`] expect `Hasher`, `Key`, `Value` and optionally `QueryKind` and +/// `OnEmpty`, +/// * [`pallet_prelude::StorageDoubleMap`] expect `Hasher1`, `Key1`, `Hasher2`, `Key2`, `Value` and +/// optionally `QueryKind` and `OnEmpty`. +/// +/// For unnamed generic argument: Their first generic must be `_` as it is replaced by the macro +/// and other generic must declared as a normal declaration of type generic in rust. /// /// The Prefix generic written by the macro is generated using `PalletInfo::name::>()` /// and the name of the storage type. @@ -1580,6 +1597,12 @@ pub mod pallet_prelude { /// ```ignore /// #[pallet::storage] /// #[pallet::getter(fn my_storage)] +/// pub(super) type MyStorage = StorageMap; +/// ``` +/// or +/// ```ignore +/// #[pallet::storage] +/// #[pallet::getter(fn my_storage)] /// pub(super) type MyStorage = StorageMap<_, Blake2_128Concat, u32, u32>; /// ``` /// @@ -1589,7 +1612,7 @@ pub mod pallet_prelude { /// ```ignore /// #[cfg(feature = "my-feature")] /// #[pallet::storage] -/// pub(super) type MyStorage = StorageValue<_, u32>; +/// pub(super) type MyStorage = StorageValue; /// ``` /// /// All the `cfg` attributes are automatically copied to the items generated for the storage, i.e. the @@ -1606,10 +1629,11 @@ pub mod pallet_prelude { /// ### Macro expansion /// /// For each storage item the macro generates a struct named -/// `_GeneratedPrefixForStorage$NameOfStorage`, and implements [`StorageInstance`](traits::StorageInstance) -/// on it using the pallet and storage name. It then uses it as the first generic of the aliased -/// type. +/// `_GeneratedPrefixForStorage$NameOfStorage`, and implements +/// [`StorageInstance`](traits::StorageInstance) on it using the pallet and storage name. It then +/// uses it as the first generic of the aliased type. /// +/// For named generic, the macro will reorder the generics, and remove the names. /// /// The macro implements the function `storage_metadata` on `Pallet` implementing the metadata for /// all storage items based on their kind: @@ -1891,12 +1915,13 @@ pub mod pallet_prelude { /// // storage item. Thus generic hasher is supported. /// #[pallet::storage] /// pub(super) type MyStorageValue = -/// StorageValue<_, T::Balance, ValueQuery, MyDefault>; +/// StorageValue>; /// /// // Another storage declaration /// #[pallet::storage] /// #[pallet::getter(fn my_storage)] -/// pub(super) type MyStorage = StorageMap<_, Blake2_128Concat, u32, u32>; +/// pub(super) type MyStorage = +/// StorageMap; /// /// // Declare the genesis config (optional). /// // @@ -2033,12 +2058,12 @@ pub mod pallet_prelude { /// /// #[pallet::storage] /// pub(super) type MyStorageValue, I: 'static = ()> = -/// StorageValue<_, T::Balance, ValueQuery, MyDefault>; +/// StorageValue>; /// /// #[pallet::storage] /// #[pallet::getter(fn my_storage)] /// pub(super) type MyStorage = -/// StorageMap<_, Blake2_128Concat, u32, u32>; +/// StorageMap; /// /// #[pallet::genesis_config] /// #[derive(Default)] @@ -2210,7 +2235,7 @@ pub mod pallet_prelude { /// ```ignore /// #[pallet::type_value] fn MyStorageOnEmpty() -> u32 { 3u32 } /// #[pallet::storage] -/// pub(super) type MyStorage = StorageValue; +/// pub(super) type MyStorage = StorageValue<_, u32, ValueQuery, MyStorageOnEmpty>; /// ``` /// /// NOTE: `decl_storage` also generates functions `assimilate_storage` and `build_storage` diff --git a/frame/support/test/tests/pallet.rs b/frame/support/test/tests/pallet.rs index 3bde38c78e2c1..860286f5d38aa 100644 --- a/frame/support/test/tests/pallet.rs +++ b/frame/support/test/tests/pallet.rs @@ -194,7 +194,7 @@ pub mod pallet { StorageValue<_, ::_2>; #[pallet::storage] - pub type Value = StorageValue<_, u32>; + pub type Value = StorageValue; #[pallet::type_value] pub fn MyDefault() -> u16 @@ -209,13 +209,17 @@ pub mod pallet { StorageMap<_, Blake2_128Concat, u8, u16, ValueQuery, MyDefault>; #[pallet::storage] - pub type Map2 = StorageMap<_, Twox64Concat, u16, u32>; + pub type Map2 = StorageMap; #[pallet::storage] pub type DoubleMap = StorageDoubleMap<_, Blake2_128Concat, u8, Twox64Concat, u16, u32>; #[pallet::storage] - pub type DoubleMap2 = StorageDoubleMap<_, Twox64Concat, u16, Blake2_128Concat, u32, u64>; + pub type DoubleMap2 = StorageDoubleMap< + Hasher1 = Twox64Concat, Key1 = u16, + Hasher2 = Blake2_128Concat, Key2 = u32, + Value = u64 + >; #[pallet::storage] #[pallet::getter(fn conditional_value)] diff --git a/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen.rs b/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen.rs new file mode 100644 index 0000000000000..30b6d651f3b89 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen.rs @@ -0,0 +1,25 @@ +#[frame_support::pallet] +mod pallet { + use frame_support::pallet_prelude::{Hooks, StorageValue}; + use frame_system::pallet_prelude::BlockNumberFor; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::hooks] + impl Hooks> for Pallet {} + + #[pallet::call] + impl Pallet {} + + struct Bar; + + #[pallet::storage] + type Foo = StorageValue; +} + +fn main() { +} diff --git a/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen.stderr b/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen.stderr new file mode 100644 index 0000000000000..e2802b5e545f7 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen.stderr @@ -0,0 +1,33 @@ +error[E0277]: the trait bound `Bar: WrapperTypeDecode` is not satisfied + --> $DIR/storage_ensure_span_are_ok_on_wrong_gen.rs:20:12 + | +20 | #[pallet::storage] + | ^^^^^^^ the trait `WrapperTypeDecode` is not implemented for `Bar` + | + = note: required because of the requirements on the impl of `Decode` for `Bar` + = note: required because of the requirements on the impl of `FullCodec` for `Bar` + = note: required because of the requirements on the impl of `StorageValueMetadata` for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>` + = note: required by `frame_support::storage::types::StorageValueMetadata::NAME` + +error[E0277]: the trait bound `Bar: EncodeLike` is not satisfied + --> $DIR/storage_ensure_span_are_ok_on_wrong_gen.rs:20:12 + | +20 | #[pallet::storage] + | ^^^^^^^ the trait `EncodeLike` is not implemented for `Bar` + | + = note: required because of the requirements on the impl of `FullEncode` for `Bar` + = note: required because of the requirements on the impl of `FullCodec` for `Bar` + = note: required because of the requirements on the impl of `StorageValueMetadata` for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>` + = note: required by `frame_support::storage::types::StorageValueMetadata::NAME` + +error[E0277]: the trait bound `Bar: WrapperTypeEncode` is not satisfied + --> $DIR/storage_ensure_span_are_ok_on_wrong_gen.rs:20:12 + | +20 | #[pallet::storage] + | ^^^^^^^ the trait `WrapperTypeEncode` is not implemented for `Bar` + | + = note: required because of the requirements on the impl of `pallet::_::_parity_scale_codec::Encode` for `Bar` + = note: required because of the requirements on the impl of `FullEncode` for `Bar` + = note: required because of the requirements on the impl of `FullCodec` for `Bar` + = note: required because of the requirements on the impl of `StorageValueMetadata` for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>` + = note: required by `frame_support::storage::types::StorageValueMetadata::NAME` diff --git a/frame/support/test/tests/pallet_ui/storage_invalid_first_generic.stderr b/frame/support/test/tests/pallet_ui/storage_invalid_first_generic.stderr index d332e6c2d3d1b..b37f7e57f3552 100644 --- a/frame/support/test/tests/pallet_ui/storage_invalid_first_generic.stderr +++ b/frame/support/test/tests/pallet_ui/storage_invalid_first_generic.stderr @@ -1,4 +1,4 @@ -error: Invalid use of `#[pallet::storage]`, the type first generic argument must be `_`, the final argument is automatically set by macro. +error: Invalid pallet::storage, for unnamed generic arguments the type first generic argument must be `_`, the argument is then replaced by macro. --> $DIR/storage_invalid_first_generic.rs:19:29 | 19 | type Foo = StorageValue; diff --git a/frame/support/test/tests/pallet_ui/storage_not_storage_type.stderr b/frame/support/test/tests/pallet_ui/storage_not_storage_type.stderr index ec4bde22ac7a8..30885bbe388b0 100644 --- a/frame/support/test/tests/pallet_ui/storage_not_storage_type.stderr +++ b/frame/support/test/tests/pallet_ui/storage_not_storage_type.stderr @@ -1,4 +1,4 @@ -error: Invalid pallet::storage, expected ident: `StorageValue` or `StorageMap` or `StorageDoubleMap` in order to expand metadata, found `u8` +error: Invalid pallet::storage, expected ident: `StorageValue` or `StorageMap` or `StorageDoubleMap` in order to expand metadata, found `u8`. --> $DIR/storage_not_storage_type.rs:19:16 | 19 | type Foo = u8; diff --git a/frame/support/test/tests/pallet_ui/storage_value_duplicate_named_generic.rs b/frame/support/test/tests/pallet_ui/storage_value_duplicate_named_generic.rs new file mode 100644 index 0000000000000..1f076b1ecbfc6 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/storage_value_duplicate_named_generic.rs @@ -0,0 +1,23 @@ +#[frame_support::pallet] +mod pallet { + use frame_support::pallet_prelude::{Hooks, StorageValue}; + use frame_system::pallet_prelude::BlockNumberFor; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::hooks] + impl Hooks> for Pallet {} + + #[pallet::call] + impl Pallet {} + + #[pallet::storage] + type Foo = StorageValue; +} + +fn main() { +} diff --git a/frame/support/test/tests/pallet_ui/storage_value_duplicate_named_generic.stderr b/frame/support/test/tests/pallet_ui/storage_value_duplicate_named_generic.stderr new file mode 100644 index 0000000000000..3def9061fec8a --- /dev/null +++ b/frame/support/test/tests/pallet_ui/storage_value_duplicate_named_generic.stderr @@ -0,0 +1,11 @@ +error: Invalid pallet::storage, Duplicated named generic + --> $DIR/storage_value_duplicate_named_generic.rs:19:42 + | +19 | type Foo = StorageValue; + | ^^^^^ + +error: Invalid pallet::storage, Duplicated named generic + --> $DIR/storage_value_duplicate_named_generic.rs:19:29 + | +19 | type Foo = StorageValue; + | ^^^^^ diff --git a/frame/support/test/tests/pallet_ui/storage_value_generic_named_and_unnamed.rs b/frame/support/test/tests/pallet_ui/storage_value_generic_named_and_unnamed.rs new file mode 100644 index 0000000000000..fd0ea4794bc43 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/storage_value_generic_named_and_unnamed.rs @@ -0,0 +1,23 @@ +#[frame_support::pallet] +mod pallet { + use frame_support::pallet_prelude::{Hooks, StorageValue, OptionQuery}; + use frame_system::pallet_prelude::BlockNumberFor; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::hooks] + impl Hooks> for Pallet {} + + #[pallet::call] + impl Pallet {} + + #[pallet::storage] + type Foo = StorageValue; +} + +fn main() { +} diff --git a/frame/support/test/tests/pallet_ui/storage_value_generic_named_and_unnamed.stderr b/frame/support/test/tests/pallet_ui/storage_value_generic_named_and_unnamed.stderr new file mode 100644 index 0000000000000..61c01943cc3f5 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/storage_value_generic_named_and_unnamed.stderr @@ -0,0 +1,5 @@ +error: Invalid pallet::storage, invalid generic declaration for storage. Expect only type generics or binding generics, e.g. `` or ``. + --> $DIR/storage_value_generic_named_and_unnamed.rs:19:16 + | +19 | type Foo = StorageValue; + | ^^^^^^^^^^^^ diff --git a/frame/support/test/tests/pallet_ui/storage_value_no_generic.stderr b/frame/support/test/tests/pallet_ui/storage_value_no_generic.stderr index 894f7095b2b5a..f7449c5ffda7d 100644 --- a/frame/support/test/tests/pallet_ui/storage_value_no_generic.stderr +++ b/frame/support/test/tests/pallet_ui/storage_value_no_generic.stderr @@ -1,4 +1,4 @@ -error: pallet::storage unexpected number of generic argument, expected at least 2 args, found none +error: Invalid pallet::storage, invalid number of generic generic arguments, expect more that 0 generic arguments. --> $DIR/storage_value_no_generic.rs:19:16 | 19 | type Foo = StorageValue; diff --git a/frame/support/test/tests/pallet_ui/storage_value_unexpected_named_generic.rs b/frame/support/test/tests/pallet_ui/storage_value_unexpected_named_generic.rs new file mode 100644 index 0000000000000..a3e54448e42ad --- /dev/null +++ b/frame/support/test/tests/pallet_ui/storage_value_unexpected_named_generic.rs @@ -0,0 +1,23 @@ +#[frame_support::pallet] +mod pallet { + use frame_support::pallet_prelude::{Hooks, StorageValue}; + use frame_system::pallet_prelude::BlockNumberFor; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::hooks] + impl Hooks> for Pallet {} + + #[pallet::call] + impl Pallet {} + + #[pallet::storage] + type Foo = StorageValue

; +} + +fn main() { +} diff --git a/frame/support/test/tests/pallet_ui/storage_value_unexpected_named_generic.stderr b/frame/support/test/tests/pallet_ui/storage_value_unexpected_named_generic.stderr new file mode 100644 index 0000000000000..393b5aa5e211a --- /dev/null +++ b/frame/support/test/tests/pallet_ui/storage_value_unexpected_named_generic.stderr @@ -0,0 +1,5 @@ +error: Invalid `StorageValue`, cannot find `Value` generic + --> $DIR/storage_value_unexpected_named_generic.rs:19:28 + | +19 | type Foo = StorageValue

; + | ^ diff --git a/frame/support/test/tests/pallet_ui/storage_wrong_item.stderr b/frame/support/test/tests/pallet_ui/storage_wrong_item.stderr index 8cc180b5bfe49..d875d8acec66f 100644 --- a/frame/support/test/tests/pallet_ui/storage_wrong_item.stderr +++ b/frame/support/test/tests/pallet_ui/storage_wrong_item.stderr @@ -1,4 +1,4 @@ -error: Invalid pallet::storage, expected item type +error: Invalid pallet::storage, expect item type. --> $DIR/storage_wrong_item.rs:19:2 | 19 | impl Foo {} From c9042c8786eaaafe7c7fa0ead7dba2147431e22f Mon Sep 17 00:00:00 2001 From: thiolliere Date: Tue, 18 May 2021 16:07:21 +0200 Subject: [PATCH 2/2] fix error message on unexpected name for generic --- .../procedural/src/pallet/parse/storage.rs | 166 ++++++++++++------ ...ensure_span_are_ok_on_wrong_gen_unnamed.rs | 25 +++ ...re_span_are_ok_on_wrong_gen_unnamed.stderr | 33 ++++ ...rage_value_unexpected_named_generic.stderr | 8 +- 4 files changed, 176 insertions(+), 56 deletions(-) create mode 100644 frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen_unnamed.rs create mode 100644 frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen_unnamed.stderr diff --git a/frame/support/procedural/src/pallet/parse/storage.rs b/frame/support/procedural/src/pallet/parse/storage.rs index 239331eb069d4..59bfacfc5302a 100644 --- a/frame/support/procedural/src/pallet/parse/storage.rs +++ b/frame/support/procedural/src/pallet/parse/storage.rs @@ -174,6 +174,66 @@ enum StorageKind { NMap, } +/// Check the generics in the `map` contains the generics in `gen` may contains generics in +/// `optional_gen`, and doesn't contains any other. +fn check_generics( + map: &HashMap, + mandatory_generics: &[&str], + optional_generics: &[&str], + storage_type_name: &str, + args_span: proc_macro2::Span, +) -> syn::Result<()> { + let mut errors = vec![]; + + let expectation = { + let mut e = format!( + "`{}` expect generics {}and optional generics {}", + storage_type_name, + mandatory_generics.iter().map(|name| format!("`{}`, ", name)).collect::(), + &optional_generics.iter().map(|name| format!("`{}`, ", name)).collect::(), + ); + e.pop(); + e.pop(); + e.push_str("."); + e + }; + + for (gen_name, gen_binding) in map { + if !mandatory_generics.contains(&gen_name.as_str()) + && !optional_generics.contains(&gen_name.as_str()) + { + let msg = format!( + "Invalid pallet::storage, Unexpected generic `{}` for `{}`. {}", + gen_name, + storage_type_name, + expectation, + ); + errors.push(syn::Error::new(gen_binding.span(), msg)); + } + } + + for mandatory_generic in mandatory_generics { + if !map.contains_key(&mandatory_generic.to_string()) { + let msg = format!( + "Invalid pallet::storage, cannot find `{}` generic, required for `{}`.", + mandatory_generic, + storage_type_name + ); + errors.push(syn::Error::new(args_span, msg)); + } + } + + let mut errors = errors.drain(..); + if let Some(mut error) = errors.next() { + for other_error in errors { + error.combine(other_error); + } + Err(error) + } else { + Ok(()) + } +} + /// Returns `(named generics, metadata, query kind)` fn process_named_generics( storage: &StorageKind, @@ -193,107 +253,103 @@ fn process_named_generics( parsed.insert(arg.ident.to_string(), arg.clone()); } - // Error on unfound mandatory generic - let unfound_error = |storage_name: &str, generic_name: &str| { - let msg = format!("Invalid `{}`, cannot find `{}` generic", storage_name, generic_name); - syn::Error::new(args_span, msg) - }; - - let unexpected_remainings = |storage_name: &str, unexpected: HashMap<_, syn::Binding>| { - if unexpected.len() != 0 { - let mut unexpected = unexpected.values(); - let msg = |gen| { - format!( - "Invalid pallet::storage, Unexpected generic `{}` for `{}`.", - gen, storage_name, - ) - }; - - let first = unexpected.next().expect("not empty as checked above"); - - let mut error = syn::Error::new(first.ident.span(), msg(first.ident.clone())); - for other in unexpected { - error.combine(syn::Error::new(other.ident.span(), msg(other.ident.clone()))); - } - Err(error) - } else { - Ok(()) - } - }; - let generics = match storage { StorageKind::Value => { - let generics = StorageGenerics::Value { + check_generics( + &parsed, + &["Value"], + &["QueryKind", "OnEmpty"], + "StorageValue", + args_span, + )?; + + StorageGenerics::Value { value: parsed.remove("Value") .map(|binding| binding.ty) - .ok_or_else(|| unfound_error("StorageValue", "Value"))?, + .expect("checked above as mandatory generic"), query_kind: parsed.remove("QueryKind") .map(|binding| binding.ty), on_empty: parsed.remove("OnEmpty") .map(|binding| binding.ty), - }; - unexpected_remainings("StorageValue", parsed)?; - generics + } } StorageKind::Map => { - let generics = StorageGenerics::Map { + check_generics( + &parsed, + &["Hasher", "Key", "Value"], + &["QueryKind", "OnEmpty"], + "StorageMap", + args_span, + )?; + + StorageGenerics::Map { hasher: parsed.remove("Hasher") .map(|binding| binding.ty) - .ok_or_else(|| unfound_error("StorageMap", "Hasher"))?, + .expect("checked above as mandatory generic"), key: parsed.remove("Key") .map(|binding| binding.ty) - .ok_or_else(|| unfound_error("StorageMap", "Key"))?, + .expect("checked above as mandatory generic"), value: parsed.remove("Value") .map(|binding| binding.ty) - .ok_or_else(|| unfound_error("StorageMap", "Value"))?, + .expect("checked above as mandatory generic"), query_kind: parsed.remove("QueryKind") .map(|binding| binding.ty), on_empty: parsed.remove("OnEmpty") .map(|binding| binding.ty), - }; - unexpected_remainings("StorageMap", parsed)?; - generics + } } StorageKind::DoubleMap => { - let generics = StorageGenerics::DoubleMap { + check_generics( + &parsed, + &["Hasher1", "Key1", "Hasher2", "Key2", "Value"], + &["QueryKind", "OnEmpty"], + "StorageDoubleMap", + args_span, + )?; + + StorageGenerics::DoubleMap { hasher1: parsed.remove("Hasher1") .map(|binding| binding.ty) - .ok_or_else(|| unfound_error("StorageDoubleMap", "Hasher1"))?, + .expect("checked above as mandatory generic"), key1: parsed.remove("Key1") .map(|binding| binding.ty) - .ok_or_else(|| unfound_error("StorageDoubleMap", "Key1"))?, + .expect("checked above as mandatory generic"), hasher2: parsed.remove("Hasher2") .map(|binding| binding.ty) - .ok_or_else(|| unfound_error("StorageDoubleMap", "Hasher2"))?, + .expect("checked above as mandatory generic"), key2: parsed.remove("Key2") .map(|binding| binding.ty) - .ok_or_else(|| unfound_error("StorageDoubleMap", "Key2"))?, + .expect("checked above as mandatory generic"), value: parsed.remove("Value") .map(|binding| binding.ty) - .ok_or_else(|| unfound_error("StorageDoubleMap", "Value"))?, + .expect("checked above as mandatory generic"), query_kind: parsed.remove("QueryKind") .map(|binding| binding.ty), on_empty: parsed.remove("OnEmpty") .map(|binding| binding.ty), - }; - unexpected_remainings("StorageDoubleMap", parsed)?; - generics + } } StorageKind::NMap => { - let generics = StorageGenerics::NMap { + check_generics( + &parsed, + &["Key", "Value"], + &["QueryKind", "OnEmpty"], + "StorageNMap", + args_span, + )?; + + StorageGenerics::NMap { keygen: parsed.remove("Key") .map(|binding| binding.ty) - .ok_or_else(|| unfound_error("StorageDoubleMap", "Key"))?, + .expect("checked above as mandatory generic"), value: parsed.remove("Value") .map(|binding| binding.ty) - .ok_or_else(|| unfound_error("StorageDoubleMap", "Value"))?, + .expect("checked above as mandatory generic"), query_kind: parsed.remove("QueryKind") .map(|binding| binding.ty), on_empty: parsed.remove("OnEmpty") .map(|binding| binding.ty), - }; - unexpected_remainings("StorageDoubleMap", parsed)?; - generics + } } }; diff --git a/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen_unnamed.rs b/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen_unnamed.rs new file mode 100644 index 0000000000000..ddb19121660da --- /dev/null +++ b/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen_unnamed.rs @@ -0,0 +1,25 @@ +#[frame_support::pallet] +mod pallet { + use frame_support::pallet_prelude::{Hooks, StorageValue}; + use frame_system::pallet_prelude::BlockNumberFor; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(core::marker::PhantomData); + + #[pallet::hooks] + impl Hooks> for Pallet {} + + #[pallet::call] + impl Pallet {} + + struct Bar; + + #[pallet::storage] + type Foo = StorageValue<_, Bar>; +} + +fn main() { +} diff --git a/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen_unnamed.stderr b/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen_unnamed.stderr new file mode 100644 index 0000000000000..e54a8c227eea2 --- /dev/null +++ b/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen_unnamed.stderr @@ -0,0 +1,33 @@ +error[E0277]: the trait bound `Bar: WrapperTypeDecode` is not satisfied + --> $DIR/storage_ensure_span_are_ok_on_wrong_gen_unnamed.rs:20:12 + | +20 | #[pallet::storage] + | ^^^^^^^ the trait `WrapperTypeDecode` is not implemented for `Bar` + | + = note: required because of the requirements on the impl of `Decode` for `Bar` + = note: required because of the requirements on the impl of `FullCodec` for `Bar` + = note: required because of the requirements on the impl of `StorageValueMetadata` for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>` + = note: required by `frame_support::storage::types::StorageValueMetadata::NAME` + +error[E0277]: the trait bound `Bar: EncodeLike` is not satisfied + --> $DIR/storage_ensure_span_are_ok_on_wrong_gen_unnamed.rs:20:12 + | +20 | #[pallet::storage] + | ^^^^^^^ the trait `EncodeLike` is not implemented for `Bar` + | + = note: required because of the requirements on the impl of `FullEncode` for `Bar` + = note: required because of the requirements on the impl of `FullCodec` for `Bar` + = note: required because of the requirements on the impl of `StorageValueMetadata` for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>` + = note: required by `frame_support::storage::types::StorageValueMetadata::NAME` + +error[E0277]: the trait bound `Bar: WrapperTypeEncode` is not satisfied + --> $DIR/storage_ensure_span_are_ok_on_wrong_gen_unnamed.rs:20:12 + | +20 | #[pallet::storage] + | ^^^^^^^ the trait `WrapperTypeEncode` is not implemented for `Bar` + | + = note: required because of the requirements on the impl of `pallet::_::_parity_scale_codec::Encode` for `Bar` + = note: required because of the requirements on the impl of `FullEncode` for `Bar` + = note: required because of the requirements on the impl of `FullCodec` for `Bar` + = note: required because of the requirements on the impl of `StorageValueMetadata` for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo, Bar>` + = note: required by `frame_support::storage::types::StorageValueMetadata::NAME` diff --git a/frame/support/test/tests/pallet_ui/storage_value_unexpected_named_generic.stderr b/frame/support/test/tests/pallet_ui/storage_value_unexpected_named_generic.stderr index 393b5aa5e211a..f03b71ff5eb6e 100644 --- a/frame/support/test/tests/pallet_ui/storage_value_unexpected_named_generic.stderr +++ b/frame/support/test/tests/pallet_ui/storage_value_unexpected_named_generic.stderr @@ -1,4 +1,10 @@ -error: Invalid `StorageValue`, cannot find `Value` generic +error: Invalid pallet::storage, Unexpected generic `P` for `StorageValue`. `StorageValue` expect generics `Value`, and optional generics `QueryKind`, `OnEmpty`. + --> $DIR/storage_value_unexpected_named_generic.rs:19:29 + | +19 | type Foo = StorageValue

; + | ^ + +error: Invalid pallet::storage, cannot find `Value` generic, required for `StorageValue`. --> $DIR/storage_value_unexpected_named_generic.rs:19:28 | 19 | type Foo = StorageValue

;