mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-09 20:11:09 +00:00
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:
committed by
GitHub
parent
ed39290f91
commit
0b30049417
@@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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]
|
||||
|
||||
+25
@@ -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() {
|
||||
}
|
||||
+33
@@ -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`
|
||||
+25
@@ -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() {
|
||||
}
|
||||
+33
@@ -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() {
|
||||
}
|
||||
+11
@@ -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>;
|
||||
| ^^^^^
|
||||
+23
@@ -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() {
|
||||
}
|
||||
+5
@@ -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;
|
||||
|
||||
+23
@@ -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() {
|
||||
}
|
||||
+11
@@ -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 {}
|
||||
|
||||
Reference in New Issue
Block a user