Allow to name the generic for storages in #[pallet::storage] (#8751)

* implement named generic for storages

* fix error message on unexpected name for generic
This commit is contained in:
Guillaume Thiolliere
2021-05-19 09:11:11 +02:00
committed by GitHub
parent ed39290f91
commit 0b30049417
18 changed files with 746 additions and 136 deletions
@@ -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,112 @@ 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);
let default_max_values: 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, max_values } => {
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));
let max_values = max_values.unwrap_or_else(|| default_max_values.clone());
args.args.push(syn::GenericArgument::Type(max_values));
}
StorageGenerics::DoubleMap {
hasher1, key1, hasher2, key2, value, query_kind, on_empty, max_values,
} => {
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));
let max_values = max_values.unwrap_or_else(|| default_max_values.clone());
args.args.push(syn::GenericArgument::Type(max_values));
}
StorageGenerics::NMap { keygen, value, query_kind, on_empty, max_values, } => {
args.args.push(syn::GenericArgument::Type(keygen));
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));
let max_values = max_values.unwrap_or_else(|| default_max_values.clone());
args.args.push(syn::GenericArgument::Type(max_values));
}
}
} 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| {
@@ -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,17 +52,17 @@ 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
},
NMap {
keys: Vec<syn::Type>,
keygen: syn::GenericArgument,
value: syn::GenericArgument,
keygen: syn::Type,
value: syn::Type,
},
}
@@ -98,41 +99,402 @@ pub struct StorageDef {
pub attr_span: proc_macro2::Span,
/// The `cfg` attributes.
pub cfg_attrs: Vec<syn::Attribute>,
/// If generics are named (e.g. `StorageValue<Value = u32, ..>`) then this contains all the
/// generics of the storage.
/// If generics are not named, this is none.
pub named_generics: Option<StorageGenerics>,
}
/// In `Foo<A, B, C>` 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<syn::GenericArgument> {
if let syn::PathArguments::AngleBracketed(args) = &segment.arguments {
if arg_pos < args.args.len() {
Ok(args.args[arg_pos].clone())
} 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))
/// 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<syn::Type>,
on_empty: Option<syn::Type>,
max_values: Option<syn::Type>,
},
Map {
hasher: syn::Type,
key: syn::Type,
value: syn::Type,
query_kind: Option<syn::Type>,
on_empty: Option<syn::Type>,
max_values: Option<syn::Type>,
},
Value {
value: syn::Type,
query_kind: Option<syn::Type>,
on_empty: Option<syn::Type>,
},
NMap {
keygen: syn::Type,
value: syn::Type,
query_kind: Option<syn::Type>,
on_empty: Option<syn::Type>,
max_values: Option<syn::Type>,
},
}
impl StorageGenerics {
/// Return the metadata from the defined generics
fn metadata(&self) -> syn::Result<Metadata> {
let res = 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 },
Self::NMap { keygen, value, .. } => Metadata::NMap {
keys: collect_keys(&keygen)?,
keygen,
value,
},
};
Ok(res)
}
/// Return the query kind from the defined generics
fn query_kind(&self) -> Option<syn::Type> {
match &self {
Self::DoubleMap { query_kind, .. }
| Self::Map { query_kind, .. }
| Self::Value { query_kind, .. }
| Self::NMap { query_kind, .. }
=> query_kind.clone(),
}
}
}
enum StorageKind {
Value,
Map,
DoubleMap,
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<String, syn::Binding>,
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::<String>(),
&optional_generics.iter().map(|name| format!("`{}`, ", name)).collect::<String>(),
);
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 {
let msg = format!("pallet::storage unexpected number of generic argument, expected at \
least {} args, found none", arg_pos + 1);
Ok(())
}
}
/// Returns `(named generics, metadata, query kind)`
fn process_named_generics(
storage: &StorageKind,
args_span: proc_macro2::Span,
args: &[syn::Binding],
) -> syn::Result<(Option<StorageGenerics>, Metadata, Option<syn::Type>)> {
let mut parsed = HashMap::<String, syn::Binding>::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());
}
let generics = match storage {
StorageKind::Value => {
check_generics(
&parsed,
&["Value"],
&["QueryKind", "OnEmpty"],
"StorageValue",
args_span,
)?;
StorageGenerics::Value {
value: parsed.remove("Value")
.map(|binding| binding.ty)
.expect("checked above as mandatory generic"),
query_kind: parsed.remove("QueryKind")
.map(|binding| binding.ty),
on_empty: parsed.remove("OnEmpty")
.map(|binding| binding.ty),
}
}
StorageKind::Map => {
check_generics(
&parsed,
&["Hasher", "Key", "Value"],
&["QueryKind", "OnEmpty", "MaxValues"],
"StorageMap",
args_span,
)?;
StorageGenerics::Map {
hasher: parsed.remove("Hasher")
.map(|binding| binding.ty)
.expect("checked above as mandatory generic"),
key: parsed.remove("Key")
.map(|binding| binding.ty)
.expect("checked above as mandatory generic"),
value: parsed.remove("Value")
.map(|binding| binding.ty)
.expect("checked above as mandatory generic"),
query_kind: parsed.remove("QueryKind").map(|binding| binding.ty),
on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty),
max_values: parsed.remove("MaxValues").map(|binding| binding.ty),
}
}
StorageKind::DoubleMap => {
check_generics(
&parsed,
&["Hasher1", "Key1", "Hasher2", "Key2", "Value"],
&["QueryKind", "OnEmpty", "MaxValues"],
"StorageDoubleMap",
args_span,
)?;
StorageGenerics::DoubleMap {
hasher1: parsed.remove("Hasher1")
.map(|binding| binding.ty)
.expect("checked above as mandatory generic"),
key1: parsed.remove("Key1")
.map(|binding| binding.ty)
.expect("checked above as mandatory generic"),
hasher2: parsed.remove("Hasher2")
.map(|binding| binding.ty)
.expect("checked above as mandatory generic"),
key2: parsed.remove("Key2")
.map(|binding| binding.ty)
.expect("checked above as mandatory generic"),
value: parsed.remove("Value")
.map(|binding| binding.ty)
.expect("checked above as mandatory generic"),
query_kind: parsed.remove("QueryKind").map(|binding| binding.ty),
on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty),
max_values: parsed.remove("MaxValues").map(|binding| binding.ty),
}
}
StorageKind::NMap => {
check_generics(
&parsed,
&["Key", "Value"],
&["QueryKind", "OnEmpty", "MaxValues"],
"StorageNMap",
args_span,
)?;
StorageGenerics::NMap {
keygen: parsed.remove("Key")
.map(|binding| binding.ty)
.expect("checked above as mandatory generic"),
value: parsed.remove("Value")
.map(|binding| binding.ty)
.expect("checked above as mandatory generic"),
query_kind: parsed.remove("QueryKind").map(|binding| binding.ty),
on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty),
max_values: parsed.remove("MaxValues").map(|binding| binding.ty),
}
}
};
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<StorageGenerics>, Metadata, Option<syn::Type>)> {
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::<syn::Token![_]>(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(),
),
StorageKind::NMap => {
let keygen = retrieve_arg(1)?;
let keys = collect_keys(&keygen)?;
(
None,
Metadata::NMap {
keys,
keygen,
value: retrieve_arg(2)?,
},
retrieve_arg(3).ok(),
)
},
};
Ok(res)
}
/// Returns `(named generics, metadata, query kind)`
fn process_generics(
segment: &syn::PathSegment,
) -> syn::Result<(Option<StorageGenerics>, Metadata, Option<syn::Type>)> {
let storage_kind = match &*segment.ident.to_string() {
"StorageValue" => StorageKind::Value,
"StorageMap" => StorageKind::Map,
"StorageDoubleMap" => StorageKind::DoubleMap,
"StorageNMap" => StorageKind::NMap,
found => {
let msg = format!(
"Invalid pallet::storage, expected ident: `StorageValue` or \
`StorageMap` or `StorageDoubleMap` or `StorageNMap` 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::<Vec<_>>();
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::<Vec<_>>();
process_named_generics(&storage_kind, args_span, &args)
} else {
let msg = "Invalid pallet::storage, invalid generic declaration for storage. Expect only \
type generics or binding generics, e.g. `<Name1 = Gen1, Name2 = Gen2, ..>` or \
`<Gen1, Gen2, ..>`.";
Err(syn::Error::new(segment.span(), msg))
}
}
/// Parse the 2nd type argument to `StorageNMap` and return its keys.
fn collect_keys(keygen: &syn::GenericArgument) -> syn::Result<Vec<syn::Type>> {
if let syn::GenericArgument::Type(syn::Type::Tuple(tup)) = keygen {
fn collect_keys(keygen: &syn::Type) -> syn::Result<Vec<syn::Type>> {
if let syn::Type::Tuple(tup) = keygen {
tup
.elems
.iter()
.map(extract_key)
.collect::<syn::Result<Vec<_>>>()
} else if let syn::GenericArgument::Type(ty) = keygen {
Ok(vec![extract_key(ty)?])
} else {
let msg = format!("Invalid pallet::storage, expected tuple of Key structs or Key struct");
Err(syn::Error::new(keygen.span(), msg))
Ok(vec![extract_key(keygen)?])
}
}
@@ -187,7 +549,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<PalletStorageAttr> = helper::take_item_pallet_attrs(&mut item.attrs)?;
@@ -217,55 +579,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)?,
}
}
"StorageNMap" => {
query_kind = retrieve_arg(&typ.path.segments[0], 3);
let keygen = retrieve_arg(&typ.path.segments[0], 1)?;
let keys = collect_keys(&keygen)?;
Metadata::NMap {
keys,
keygen,
value: retrieve_arg(&typ.path.segments[0], 2)?,
}
}
found => {
let msg = format!(
"Invalid pallet::storage, expected ident: `StorageValue` or \
`StorageMap` or `StorageDoubleMap` or `StorageNMap` 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,
@@ -279,16 +600,6 @@ impl StorageDef {
return Err(syn::Error::new(getter.unwrap().span(), msg));
}
let prefix_arg = retrieve_arg(&typ.path.segments[0], 0)?;
syn::parse2::<syn::Token![_]>(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,
@@ -301,6 +612,7 @@ impl StorageDef {
query_kind,
where_clause,
cfg_attrs,
named_generics,
})
}
}
+35 -10
View File
@@ -1586,11 +1586,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::<Pallet<..>>()`
/// and the name of the storage type.
@@ -1604,6 +1621,12 @@ pub mod pallet_prelude {
/// ```ignore
/// #[pallet::storage]
/// #[pallet::getter(fn my_storage)]
/// pub(super) type MyStorage<T> = StorageMap<Hasher = Blake2_128Concat, Key = u32, Value = u32>;
/// ```
/// or
/// ```ignore
/// #[pallet::storage]
/// #[pallet::getter(fn my_storage)]
/// pub(super) type MyStorage<T> = StorageMap<_, Blake2_128Concat, u32, u32>;
/// ```
///
@@ -1613,7 +1636,7 @@ pub mod pallet_prelude {
/// ```ignore
/// #[cfg(feature = "my-feature")]
/// #[pallet::storage]
/// pub(super) type MyStorage<T> = StorageValue<_, u32>;
/// pub(super) type MyStorage<T> = StorageValue<Value = u32>;
/// ```
///
/// All the `cfg` attributes are automatically copied to the items generated for the storage, i.e. the
@@ -1630,10 +1653,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:
@@ -1915,12 +1939,13 @@ pub mod pallet_prelude {
/// // storage item. Thus generic hasher is supported.
/// #[pallet::storage]
/// pub(super) type MyStorageValue<T: Config> =
/// StorageValue<_, T::Balance, ValueQuery, MyDefault<T>>;
/// StorageValue<Value = T::Balance, QueryKind = ValueQuery, OnEmpty = MyDefault<T>>;
///
/// // Another storage declaration
/// #[pallet::storage]
/// #[pallet::getter(fn my_storage)]
/// pub(super) type MyStorage<T> = StorageMap<_, Blake2_128Concat, u32, u32>;
/// pub(super) type MyStorage<T> =
/// StorageMap<Hasher = Blake2_128Concat, Key = u32, Value = u32>;
///
/// // Declare the genesis config (optional).
/// //
@@ -2057,12 +2082,12 @@ pub mod pallet_prelude {
///
/// #[pallet::storage]
/// pub(super) type MyStorageValue<T: Config<I>, I: 'static = ()> =
/// StorageValue<_, T::Balance, ValueQuery, MyDefault<T, I>>;
/// StorageValue<Value = T::Balance, QueryKind = ValueQuery, OnEmpty = MyDefault<T, I>>;
///
/// #[pallet::storage]
/// #[pallet::getter(fn my_storage)]
/// pub(super) type MyStorage<T, I = ()> =
/// StorageMap<_, Blake2_128Concat, u32, u32>;
/// StorageMap<Hasher = Blake2_128Concat, Key = u32, Value = u32>;
///
/// #[pallet::genesis_config]
/// #[derive(Default)]
@@ -2234,7 +2259,7 @@ pub mod pallet_prelude {
/// ```ignore
/// #[pallet::type_value] fn MyStorageOnEmpty() -> u32 { 3u32 }
/// #[pallet::storage]
/// pub(super) type MyStorage<T> = StorageValue<u32, ValueQuery, MyStorageOnEmpty>;
/// pub(super) type MyStorage<T> = StorageValue<_, u32, ValueQuery, MyStorageOnEmpty>;
/// ```
///
/// NOTE: `decl_storage` also generates functions `assimilate_storage` and `build_storage`
+11 -12
View File
@@ -196,7 +196,7 @@ pub mod pallet {
StorageValue<_, <T::AccountId as SomeAssociation2>::_2>;
#[pallet::storage]
pub type Value<T> = StorageValue<_, u32>;
pub type Value<T> = StorageValue<Value = u32>;
#[pallet::type_value]
pub fn MyDefault<T: Config>() -> u16
@@ -211,14 +211,19 @@ pub mod pallet {
StorageMap<_, Blake2_128Concat, u8, u16, ValueQuery, MyDefault<T>>;
#[pallet::storage]
pub type Map2<T> = StorageMap<_, Twox64Concat, u16, u32, OptionQuery, GetDefault, ConstU32<3>>;
pub type Map2<T> = StorageMap<
Hasher = Twox64Concat, Key = u16, Value = u32, MaxValues = ConstU32<3>
>;
#[pallet::storage]
pub type DoubleMap<T> = StorageDoubleMap<_, Blake2_128Concat, u8, Twox64Concat, u16, u32>;
#[pallet::storage]
pub type DoubleMap2<T> = StorageDoubleMap<
_, Twox64Concat, u16, Blake2_128Concat, u32, u64, OptionQuery, GetDefault, ConstU32<5>,
Hasher1 = Twox64Concat, Key1 = u16,
Hasher2 = Blake2_128Concat, Key2 = u32,
Value = u64,
MaxValues = ConstU32<5>,
>;
#[pallet::storage]
@@ -228,15 +233,9 @@ pub mod pallet {
#[pallet::storage]
#[pallet::getter(fn nmap2)]
pub type NMap2<T> = StorageNMap<
_,
(
NMapKey<Twox64Concat, u16>,
NMapKey<Blake2_128Concat, u32>,
),
u64,
OptionQuery,
GetDefault,
ConstU32<11>,
Key = (NMapKey<Twox64Concat, u16>, NMapKey<Blake2_128Concat, u32>),
Value = u64,
MaxValues = ConstU32<11>,
>;
#[pallet::storage]
@@ -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<T>(core::marker::PhantomData<T>);
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
#[pallet::call]
impl<T: Config> Pallet<T> {}
struct Bar;
#[pallet::storage]
type Foo<T> = StorageValue<Value = Bar>;
}
fn main() {
}
@@ -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<T>, 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<T>, 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<T>, Bar>`
= note: required by `frame_support::storage::types::StorageValueMetadata::NAME`
@@ -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<T>(core::marker::PhantomData<T>);
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
#[pallet::call]
impl<T: Config> Pallet<T> {}
struct Bar;
#[pallet::storage]
type Foo<T> = StorageValue<_, Bar>;
}
fn main() {
}
@@ -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<T>, 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<T>, 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<T>, Bar>`
= note: required by `frame_support::storage::types::StorageValueMetadata::NAME`
@@ -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<T> = StorageValue<u8, u8>;
@@ -1,4 +1,4 @@
error: Invalid pallet::storage, expected ident: `StorageValue` or `StorageMap` or `StorageDoubleMap` or `StorageNMap` in order to expand metadata, found `u8`
error: Invalid pallet::storage, expected ident: `StorageValue` or `StorageMap` or `StorageDoubleMap` or `StorageNMap` in order to expand metadata, found `u8`.
--> $DIR/storage_not_storage_type.rs:19:16
|
19 | type Foo<T> = u8;
@@ -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<T>(core::marker::PhantomData<T>);
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
#[pallet::call]
impl<T: Config> Pallet<T> {}
#[pallet::storage]
type Foo<T> = StorageValue<Value = u32, Value = u32>;
}
fn main() {
}
@@ -0,0 +1,11 @@
error: Invalid pallet::storage, Duplicated named generic
--> $DIR/storage_value_duplicate_named_generic.rs:19:42
|
19 | type Foo<T> = StorageValue<Value = u32, Value = u32>;
| ^^^^^
error: Invalid pallet::storage, Duplicated named generic
--> $DIR/storage_value_duplicate_named_generic.rs:19:29
|
19 | type Foo<T> = StorageValue<Value = u32, Value = u32>;
| ^^^^^
@@ -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<T>(core::marker::PhantomData<T>);
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
#[pallet::call]
impl<T: Config> Pallet<T> {}
#[pallet::storage]
type Foo<T> = StorageValue<u32, QueryKind = OptionQuery>;
}
fn main() {
}
@@ -0,0 +1,5 @@
error: Invalid pallet::storage, invalid generic declaration for storage. Expect only type generics or binding generics, e.g. `<Name1 = Gen1, Name2 = Gen2, ..>` or `<Gen1, Gen2, ..>`.
--> $DIR/storage_value_generic_named_and_unnamed.rs:19:16
|
19 | type Foo<T> = StorageValue<u32, QueryKind = OptionQuery>;
| ^^^^^^^^^^^^
@@ -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<T> = StorageValue;
@@ -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<T>(core::marker::PhantomData<T>);
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}
#[pallet::call]
impl<T: Config> Pallet<T> {}
#[pallet::storage]
type Foo<T> = StorageValue<P = u32>;
}
fn main() {
}
@@ -0,0 +1,11 @@
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<T> = StorageValue<P = u32>;
| ^
error: Invalid pallet::storage, cannot find `Value` generic, required for `StorageValue`.
--> $DIR/storage_value_unexpected_named_generic.rs:19:28
|
19 | type Foo<T> = StorageValue<P = u32>;
| ^
@@ -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 {}