referrerpolicy=no-referrer-when-downgrade

frame_support_procedural/pallet/expand/
storage.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18use crate::{
19	counter_prefix,
20	deprecation::extract_or_return_allow_attrs,
21	pallet::{
22		parse::{
23			helper::two128_str,
24			storage::{Metadata, QueryKind, StorageDef, StorageGenerics},
25		},
26		Def,
27	},
28};
29use quote::ToTokens;
30use std::{collections::HashMap, ops::IndexMut};
31use syn::spanned::Spanned;
32
33/// Generate the prefix_ident related to the storage.
34/// prefix_ident is used for the prefix struct to be given to storage as first generic param.
35fn prefix_ident(storage: &StorageDef) -> syn::Ident {
36	let storage_ident = &storage.ident;
37	syn::Ident::new(&format!("_GeneratedPrefixForStorage{}", storage_ident), storage_ident.span())
38}
39
40/// Generate the counter_prefix_ident related to the storage.
41/// counter_prefix_ident is used for the prefix struct to be given to counted storage map.
42fn counter_prefix_ident(storage_ident: &syn::Ident) -> syn::Ident {
43	syn::Ident::new(
44		&format!("_GeneratedCounterPrefixForStorage{}", storage_ident),
45		storage_ident.span(),
46	)
47}
48
49/// Check for duplicated storage prefixes. This step is necessary since users can specify an
50/// alternative storage prefix using the #[pallet::storage_prefix] syntax, and we need to ensure
51/// that the prefix specified by the user is not a duplicate of an existing one.
52fn check_prefix_duplicates(
53	storage_def: &StorageDef,
54	// A hashmap of all already used prefix and their associated error if duplication
55	used_prefixes: &mut HashMap<String, syn::Error>,
56) -> syn::Result<()> {
57	let prefix = storage_def.prefix();
58	let dup_err = syn::Error::new(
59		storage_def.prefix_span(),
60		format!("Duplicate storage prefixes found for `{}`", prefix),
61	);
62
63	if let Some(other_dup_err) = used_prefixes.insert(prefix.clone(), dup_err.clone()) {
64		let mut err = dup_err;
65		err.combine(other_dup_err);
66		return Err(err);
67	}
68
69	if let Metadata::CountedMap { .. } = storage_def.metadata {
70		let counter_prefix = counter_prefix(&prefix);
71		let counter_dup_err = syn::Error::new(
72			storage_def.prefix_span(),
73			format!(
74				"Duplicate storage prefixes found for `{}`, used for counter associated to \
75				counted storage map",
76				counter_prefix,
77			),
78		);
79
80		if let Some(other_dup_err) = used_prefixes.insert(counter_prefix, counter_dup_err.clone()) {
81			let mut err = counter_dup_err;
82			err.combine(other_dup_err);
83			return Err(err);
84		}
85	}
86
87	Ok(())
88}
89
90pub struct ResultOnEmptyStructMetadata {
91	/// The Rust ident that is going to be used as the name of the OnEmpty struct.
92	pub name: syn::Ident,
93	/// The path to the error type being returned by the ResultQuery.
94	pub error_path: syn::Path,
95	/// The visibility of the OnEmpty struct.
96	pub visibility: syn::Visibility,
97	/// The type of the storage item.
98	pub value_ty: syn::Type,
99	/// The name of the pallet error enum variant that is going to be returned.
100	pub variant_name: syn::Ident,
101	/// The span used to report compilation errors about the OnEmpty struct.
102	pub span: proc_macro2::Span,
103}
104
105///
106/// * if generics are unnamed: replace the first generic `_` by the generated prefix structure
107/// * if generics are named: reorder the generic, remove their name, and add the missing ones.
108/// * Add `#[allow(type_alias_bounds)]`
109pub fn process_generics(def: &mut Def) -> syn::Result<Vec<ResultOnEmptyStructMetadata>> {
110	let frame_support = &def.frame_support;
111	let mut on_empty_struct_metadata = Vec::new();
112
113	for storage_def in def.storages.iter_mut() {
114		let item = &mut def.item.content.as_mut().expect("Checked by def").1[storage_def.index];
115
116		let typ_item = match item {
117			syn::Item::Type(t) => t,
118			_ => unreachable!("Checked by def"),
119		};
120
121		typ_item.attrs.push(syn::parse_quote!(#[allow(type_alias_bounds)]));
122
123		let typ_path = match &mut *typ_item.ty {
124			syn::Type::Path(p) => p,
125			_ => unreachable!("Checked by def"),
126		};
127
128		let args = match &mut typ_path.path.segments[0].arguments {
129			syn::PathArguments::AngleBracketed(args) => args,
130			_ => unreachable!("Checked by def"),
131		};
132
133		let prefix_ident = prefix_ident(storage_def);
134		let type_use_gen = if def.config.has_instance {
135			quote::quote_spanned!(storage_def.attr_span => T, I)
136		} else {
137			quote::quote_spanned!(storage_def.attr_span => T)
138		};
139
140		let default_query_kind: syn::Type =
141			syn::parse_quote!(#frame_support::storage::types::OptionQuery);
142		let mut default_on_empty = |value_ty: syn::Type| -> syn::Type {
143			if let Some(QueryKind::ResultQuery(error_path, variant_name)) =
144				storage_def.query_kind.as_ref()
145			{
146				let on_empty_ident =
147					quote::format_ident!("__Frame_Internal_Get{}Result", storage_def.ident);
148				on_empty_struct_metadata.push(ResultOnEmptyStructMetadata {
149					name: on_empty_ident.clone(),
150					visibility: storage_def.vis.clone(),
151					value_ty,
152					error_path: error_path.clone(),
153					variant_name: variant_name.clone(),
154					span: storage_def.attr_span,
155				});
156				return syn::parse_quote!(#on_empty_ident);
157			}
158			syn::parse_quote!(#frame_support::traits::GetDefault)
159		};
160		let default_max_values: syn::Type = syn::parse_quote!(#frame_support::traits::GetDefault);
161
162		let set_result_query_type_parameter = |query_type: &mut syn::Type| -> syn::Result<()> {
163			if let Some(QueryKind::ResultQuery(error_path, _)) = storage_def.query_kind.as_ref() {
164				if let syn::Type::Path(syn::TypePath { path: syn::Path { segments, .. }, .. }) =
165					query_type
166				{
167					if let Some(seg) = segments.last_mut() {
168						if let syn::PathArguments::AngleBracketed(
169							syn::AngleBracketedGenericArguments { args, .. },
170						) = &mut seg.arguments
171						{
172							args.clear();
173							args.push(syn::GenericArgument::Type(syn::parse_quote!(#error_path)));
174						}
175					}
176				} else {
177					let msg = format!(
178						"Invalid pallet::storage, unexpected type for query, expected ResultQuery \
179						with 1 type parameter, found `{}`",
180						query_type.to_token_stream().to_string()
181					);
182					return Err(syn::Error::new(query_type.span(), msg));
183				}
184			}
185			Ok(())
186		};
187
188		if let Some(named_generics) = storage_def.named_generics.clone() {
189			args.args.clear();
190			args.args.push(syn::parse_quote!( #prefix_ident<#type_use_gen> ));
191			match named_generics {
192				StorageGenerics::Value { value, query_kind, on_empty } => {
193					args.args.push(syn::GenericArgument::Type(value.clone()));
194					let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone());
195					set_result_query_type_parameter(&mut query_kind)?;
196					args.args.push(syn::GenericArgument::Type(query_kind));
197					let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value));
198					args.args.push(syn::GenericArgument::Type(on_empty));
199				},
200				StorageGenerics::Map { hasher, key, value, query_kind, on_empty, max_values } |
201				StorageGenerics::CountedMap {
202					hasher,
203					key,
204					value,
205					query_kind,
206					on_empty,
207					max_values,
208				} => {
209					args.args.push(syn::GenericArgument::Type(hasher));
210					args.args.push(syn::GenericArgument::Type(key));
211					args.args.push(syn::GenericArgument::Type(value.clone()));
212					let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone());
213					set_result_query_type_parameter(&mut query_kind)?;
214					args.args.push(syn::GenericArgument::Type(query_kind));
215					let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value));
216					args.args.push(syn::GenericArgument::Type(on_empty));
217					let max_values = max_values.unwrap_or_else(|| default_max_values.clone());
218					args.args.push(syn::GenericArgument::Type(max_values));
219				},
220				StorageGenerics::DoubleMap {
221					hasher1,
222					key1,
223					hasher2,
224					key2,
225					value,
226					query_kind,
227					on_empty,
228					max_values,
229				} => {
230					args.args.push(syn::GenericArgument::Type(hasher1));
231					args.args.push(syn::GenericArgument::Type(key1));
232					args.args.push(syn::GenericArgument::Type(hasher2));
233					args.args.push(syn::GenericArgument::Type(key2));
234					args.args.push(syn::GenericArgument::Type(value.clone()));
235					let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone());
236					set_result_query_type_parameter(&mut query_kind)?;
237					args.args.push(syn::GenericArgument::Type(query_kind));
238					let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value));
239					args.args.push(syn::GenericArgument::Type(on_empty));
240					let max_values = max_values.unwrap_or_else(|| default_max_values.clone());
241					args.args.push(syn::GenericArgument::Type(max_values));
242				},
243				StorageGenerics::NMap { keygen, value, query_kind, on_empty, max_values } |
244				StorageGenerics::CountedNMap {
245					keygen,
246					value,
247					query_kind,
248					on_empty,
249					max_values,
250				} => {
251					args.args.push(syn::GenericArgument::Type(keygen));
252					args.args.push(syn::GenericArgument::Type(value.clone()));
253					let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone());
254					set_result_query_type_parameter(&mut query_kind)?;
255					args.args.push(syn::GenericArgument::Type(query_kind));
256					let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value));
257					args.args.push(syn::GenericArgument::Type(on_empty));
258					let max_values = max_values.unwrap_or_else(|| default_max_values.clone());
259					args.args.push(syn::GenericArgument::Type(max_values));
260				},
261			}
262		} else {
263			args.args[0] = syn::parse_quote!( #prefix_ident<#type_use_gen> );
264
265			let (value_idx, query_idx, on_empty_idx) = match storage_def.metadata {
266				Metadata::Value { .. } => (1, 2, 3),
267				Metadata::NMap { .. } | Metadata::CountedNMap { .. } => (2, 3, 4),
268				Metadata::Map { .. } | Metadata::CountedMap { .. } => (3, 4, 5),
269				Metadata::DoubleMap { .. } => (5, 6, 7),
270			};
271
272			if storage_def.use_default_hasher {
273				let hasher_indices: Vec<usize> = match storage_def.metadata {
274					Metadata::Map { .. } | Metadata::CountedMap { .. } => vec![1],
275					Metadata::DoubleMap { .. } => vec![1, 3],
276					_ => vec![],
277				};
278				for hasher_idx in hasher_indices {
279					args.args[hasher_idx] = syn::GenericArgument::Type(
280						syn::parse_quote!(#frame_support::Blake2_128Concat),
281					);
282				}
283			}
284
285			if query_idx < args.args.len() {
286				if let syn::GenericArgument::Type(query_kind) = args.args.index_mut(query_idx) {
287					set_result_query_type_parameter(query_kind)?;
288				}
289			} else if let Some(QueryKind::ResultQuery(error_path, _)) =
290				storage_def.query_kind.as_ref()
291			{
292				args.args.push(syn::GenericArgument::Type(syn::parse_quote!(#error_path)))
293			}
294
295			// Here, we only need to check if OnEmpty is *not* specified, and if so, then we have to
296			// generate a default OnEmpty struct for it.
297			if on_empty_idx >= args.args.len() &&
298				matches!(storage_def.query_kind.as_ref(), Some(QueryKind::ResultQuery(_, _)))
299			{
300				let value_ty = match args.args[value_idx].clone() {
301					syn::GenericArgument::Type(ty) => ty,
302					_ => unreachable!(),
303				};
304				let on_empty = default_on_empty(value_ty);
305				args.args.push(syn::GenericArgument::Type(on_empty));
306			}
307		}
308	}
309
310	Ok(on_empty_struct_metadata)
311}
312
313fn augment_final_docs(def: &mut Def) {
314	// expand the docs with a new line showing the storage type (value, map, double map, etc), and
315	// the key/value type(s).
316	let mut push_string_literal = |doc_line: &str, storage: &mut StorageDef| {
317		let item = &mut def.item.content.as_mut().expect("Checked by def").1[storage.index];
318		let typ_item = match item {
319			syn::Item::Type(t) => t,
320			_ => unreachable!("Checked by def"),
321		};
322		typ_item.attrs.push(syn::parse_quote!(#[doc = ""]));
323		typ_item.attrs.push(syn::parse_quote!(#[doc = #doc_line]));
324	};
325	def.storages.iter_mut().for_each(|storage| match &storage.metadata {
326		Metadata::Value { value } => {
327			let doc_line = format!(
328				"Storage type is [`StorageValue`] with value type `{}`.",
329				value.to_token_stream()
330			);
331			push_string_literal(&doc_line, storage);
332		},
333		Metadata::Map { key, value } => {
334			let doc_line = format!(
335				"Storage type is [`StorageMap`] with key type `{}` and value type `{}`.",
336				key.to_token_stream(),
337				value.to_token_stream()
338			);
339			push_string_literal(&doc_line, storage);
340		},
341		Metadata::DoubleMap { key1, key2, value } => {
342			let doc_line = format!(
343				"Storage type is [`StorageDoubleMap`] with key1 type {}, key2 type {} and value type {}.",
344				key1.to_token_stream(),
345				key2.to_token_stream(),
346				value.to_token_stream()
347			);
348			push_string_literal(&doc_line, storage);
349		},
350		Metadata::NMap { keys, value, .. } => {
351			let doc_line = format!(
352				"Storage type is [`StorageNMap`] with keys type ({}) and value type {}.",
353				keys.iter()
354					.map(|k| k.to_token_stream().to_string())
355					.collect::<Vec<_>>()
356					.join(", "),
357				value.to_token_stream()
358			);
359			push_string_literal(&doc_line, storage);
360		},
361		Metadata::CountedNMap { keys, value, .. } => {
362			let doc_line = format!(
363				"Storage type is [`CountedStorageNMap`] with keys type ({}) and value type {}.",
364				keys.iter()
365					.map(|k| k.to_token_stream().to_string())
366					.collect::<Vec<_>>()
367					.join(", "),
368				value.to_token_stream()
369			);
370			push_string_literal(&doc_line, storage);
371		},
372		Metadata::CountedMap { key, value } => {
373			let doc_line = format!(
374				"Storage type is [`CountedStorageMap`] with key type {} and value type {}.",
375				key.to_token_stream(),
376				value.to_token_stream()
377			);
378			push_string_literal(&doc_line, storage);
379		},
380	});
381}
382
383///
384/// * generate StoragePrefix structs (e.g. for a storage `MyStorage` a struct with the name
385///   `_GeneratedPrefixForStorage$NameOfStorage` is generated) and implements StorageInstance trait.
386/// * if generics are unnamed: replace the first generic `_` by the generated prefix structure
387/// * if generics are named: reorder the generic, remove their name, and add the missing ones.
388/// * Add `#[allow(type_alias_bounds)]` on storages type alias
389/// * generate metadatas
390pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream {
391	let on_empty_struct_metadata = match process_generics(def) {
392		Ok(idents) => idents,
393		Err(e) => return e.into_compile_error(),
394	};
395
396	augment_final_docs(def);
397
398	// Check for duplicate prefixes
399	let mut prefix_set = HashMap::new();
400	let mut errors = def
401		.storages
402		.iter()
403		.filter_map(|storage_def| check_prefix_duplicates(storage_def, &mut prefix_set).err());
404	if let Some(mut final_error) = errors.next() {
405		errors.for_each(|error| final_error.combine(error));
406		return final_error.into_compile_error();
407	}
408
409	let frame_support = &def.frame_support;
410	let frame_system = &def.frame_system;
411	let pallet_ident = &def.pallet_struct.pallet;
412	let mut entries_builder = vec![];
413	for storage in def.storages.iter() {
414		let no_docs = vec![];
415		let docs = if cfg!(feature = "no-metadata-docs") { &no_docs } else { &storage.docs };
416
417		let ident = &storage.ident;
418		let gen = &def.type_use_generics(storage.attr_span);
419		let full_ident = quote::quote_spanned!(storage.attr_span => #ident<#gen> );
420
421		let cfg_attrs = &storage.cfg_attrs;
422		let deprecation = match crate::deprecation::get_deprecation(
423			&quote::quote! { #frame_support },
424			&storage.attrs,
425		) {
426			Ok(deprecation) => deprecation,
427			Err(e) => return e.into_compile_error(),
428		};
429
430		// Extracts #[allow] attributes, necessary so that we don't run into compiler warnings
431		let maybe_allow_attrs: Vec<syn::Attribute> =
432			extract_or_return_allow_attrs(&storage.attrs).collect();
433
434		entries_builder.push(quote::quote_spanned!(storage.attr_span =>
435			#(#cfg_attrs)*
436			#(#maybe_allow_attrs)*
437			(|entries: &mut #frame_support::__private::Vec<_>| {
438				{
439					<#full_ident as #frame_support::storage::StorageEntryMetadataBuilder>::build_metadata(
440						#deprecation,
441						#frame_support::__private::vec![
442							#( #docs, )*
443						],
444						entries,
445					);
446				}
447			})
448		))
449	}
450
451	let getters = def.storages.iter().map(|storage| {
452		if let Some(getter) = &storage.getter {
453			let completed_where_clause =
454				super::merge_where_clauses(&[&storage.where_clause, &def.config.where_clause]);
455			// Extracts #[allow] attributes, necessary so that we don't run into compiler warnings
456			let maybe_allow_attrs: Vec<syn::Attribute> =
457				extract_or_return_allow_attrs(&storage.attrs).collect();
458			let ident = &storage.ident;
459			let gen = &def.type_use_generics(storage.attr_span);
460			let type_impl_gen = &def.type_impl_generics(storage.attr_span);
461			let type_use_gen = &def.type_use_generics(storage.attr_span);
462			let full_ident = quote::quote_spanned!(storage.attr_span => #ident<#gen> );
463
464			let cfg_attrs = &storage.cfg_attrs;
465
466			// If the storage item is public, link it and otherwise just mention it.
467			//
468			// We can not just copy the docs from a non-public type as it may links to internal
469			// types which makes the compiler very unhappy :(
470			let getter_doc_line = if matches!(storage.vis, syn::Visibility::Public(_)) {
471				format!("An auto-generated getter for [`{}`].", storage.ident)
472			} else {
473				format!("An auto-generated getter for `{}`.", storage.ident)
474			};
475
476			match &storage.metadata {
477				Metadata::Value { value } => {
478					let query = match storage.query_kind.as_ref().expect("Checked by def") {
479						QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span =>
480							Option<#value>
481						),
482						QueryKind::ResultQuery(error_path, _) => {
483							quote::quote_spanned!(storage.attr_span =>
484								Result<#value, #error_path>
485							)
486						},
487						QueryKind::ValueQuery => quote::quote!(#value),
488					};
489					quote::quote_spanned!(storage.attr_span =>
490						#(#cfg_attrs)*
491						impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause {
492							#[doc = #getter_doc_line]
493							#(#maybe_allow_attrs)*
494							pub fn #getter() -> #query {
495								<
496									#full_ident as #frame_support::storage::StorageValue<#value>
497								>::get()
498							}
499						}
500					)
501				},
502				Metadata::Map { key, value } => {
503					let query = match storage.query_kind.as_ref().expect("Checked by def") {
504						QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span =>
505							Option<#value>
506						),
507						QueryKind::ResultQuery(error_path, _) => {
508							quote::quote_spanned!(storage.attr_span =>
509								Result<#value, #error_path>
510							)
511						},
512						QueryKind::ValueQuery => quote::quote!(#value),
513					};
514					quote::quote_spanned!(storage.attr_span =>
515						#(#cfg_attrs)*
516						impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause {
517							#[doc = #getter_doc_line]
518							#(#maybe_allow_attrs)*
519							pub fn #getter<KArg>(k: KArg) -> #query where
520								KArg: #frame_support::__private::codec::EncodeLike<#key>,
521							{
522								<
523									#full_ident as #frame_support::storage::StorageMap<#key, #value>
524								>::get(k)
525							}
526						}
527					)
528				},
529				Metadata::CountedMap { key, value } => {
530					let query = match storage.query_kind.as_ref().expect("Checked by def") {
531						QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span =>
532							Option<#value>
533						),
534						QueryKind::ResultQuery(error_path, _) => {
535							quote::quote_spanned!(storage.attr_span =>
536								Result<#value, #error_path>
537							)
538						},
539						QueryKind::ValueQuery => quote::quote!(#value),
540					};
541					quote::quote_spanned!(storage.attr_span =>
542						#(#cfg_attrs)*
543						impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause {
544							#[doc = #getter_doc_line]
545							#(#maybe_allow_attrs)*
546							pub fn #getter<KArg>(k: KArg) -> #query where
547								KArg: #frame_support::__private::codec::EncodeLike<#key>,
548							{
549								// NOTE: we can't use any trait here because CountedStorageMap
550								// doesn't implement any.
551								<#full_ident>::get(k)
552							}
553						}
554					)
555				},
556				Metadata::DoubleMap { key1, key2, value } => {
557					let query = match storage.query_kind.as_ref().expect("Checked by def") {
558						QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span =>
559							Option<#value>
560						),
561						QueryKind::ResultQuery(error_path, _) => {
562							quote::quote_spanned!(storage.attr_span =>
563								Result<#value, #error_path>
564							)
565						},
566						QueryKind::ValueQuery => quote::quote!(#value),
567					};
568					quote::quote_spanned!(storage.attr_span =>
569						#(#cfg_attrs)*
570						impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause {
571							#[doc = #getter_doc_line]
572							#(#maybe_allow_attrs)*
573							pub fn #getter<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> #query where
574								KArg1: #frame_support::__private::codec::EncodeLike<#key1>,
575								KArg2: #frame_support::__private::codec::EncodeLike<#key2>,
576							{
577								<
578									#full_ident as
579									#frame_support::storage::StorageDoubleMap<#key1, #key2, #value>
580								>::get(k1, k2)
581							}
582						}
583					)
584				},
585				Metadata::NMap { keygen, value, .. } => {
586					let query = match storage.query_kind.as_ref().expect("Checked by def") {
587						QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span =>
588							Option<#value>
589						),
590						QueryKind::ResultQuery(error_path, _) => {
591							quote::quote_spanned!(storage.attr_span =>
592								Result<#value, #error_path>
593							)
594						},
595						QueryKind::ValueQuery => quote::quote!(#value),
596					};
597					quote::quote_spanned!(storage.attr_span =>
598						#(#cfg_attrs)*
599						impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause {
600							#[doc = #getter_doc_line]
601							#(#maybe_allow_attrs)*
602							pub fn #getter<KArg>(key: KArg) -> #query
603							where
604								KArg: #frame_support::storage::types::EncodeLikeTuple<
605									<#keygen as #frame_support::storage::types::KeyGenerator>::KArg
606								>
607									+ #frame_support::storage::types::TupleToEncodedIter,
608							{
609								<
610									#full_ident as
611									#frame_support::storage::StorageNMap<#keygen, #value>
612								>::get(key)
613							}
614						}
615					)
616				},
617				Metadata::CountedNMap { keygen, value, .. } => {
618					let query = match storage.query_kind.as_ref().expect("Checked by def") {
619						QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span =>
620							Option<#value>
621						),
622						QueryKind::ResultQuery(error_path, _) => {
623							quote::quote_spanned!(storage.attr_span =>
624								Result<#value, #error_path>
625							)
626						},
627						QueryKind::ValueQuery => quote::quote!(#value),
628					};
629					quote::quote_spanned!(storage.attr_span =>
630						#(#cfg_attrs)*
631						impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause {
632							#[doc = #getter_doc_line]
633							#(#maybe_allow_attrs)*
634							pub fn #getter<KArg>(key: KArg) -> #query
635							where
636								KArg: #frame_support::storage::types::EncodeLikeTuple<
637									<#keygen as #frame_support::storage::types::KeyGenerator>::KArg
638								>
639									+ #frame_support::storage::types::TupleToEncodedIter,
640							{
641								// NOTE: we can't use any trait here because CountedStorageNMap
642								// doesn't implement any.
643								<#full_ident>::get(key)
644							}
645						}
646					)
647				},
648			}
649		} else {
650			Default::default()
651		}
652	});
653
654	let prefix_structs = def.storages.iter().map(|storage_def| {
655		let type_impl_gen = &def.type_impl_generics(storage_def.attr_span);
656		let type_use_gen = &def.type_use_generics(storage_def.attr_span);
657		let prefix_struct_ident = prefix_ident(storage_def);
658		let prefix_struct_vis = &storage_def.vis;
659		let prefix_struct_const = storage_def.prefix();
660		let config_where_clause = &def.config.where_clause;
661
662		let cfg_attrs = &storage_def.cfg_attrs;
663
664		let maybe_counter = match storage_def.metadata {
665			Metadata::CountedMap { .. } => {
666				let counter_prefix_struct_ident = counter_prefix_ident(&storage_def.ident);
667				let counter_prefix_struct_const = counter_prefix(&prefix_struct_const);
668				let storage_prefix_hash = two128_str(&counter_prefix_struct_const);
669				quote::quote_spanned!(storage_def.attr_span =>
670					#(#cfg_attrs)*
671					#[doc(hidden)]
672					#prefix_struct_vis struct #counter_prefix_struct_ident<#type_use_gen>(
673						core::marker::PhantomData<(#type_use_gen,)>
674					);
675					#(#cfg_attrs)*
676					impl<#type_impl_gen> #frame_support::traits::StorageInstance
677						for #counter_prefix_struct_ident<#type_use_gen>
678						#config_where_clause
679					{
680						fn pallet_prefix() -> &'static str {
681							<
682								<T as #frame_system::Config>::PalletInfo
683								as #frame_support::traits::PalletInfo
684							>::name::<Pallet<#type_use_gen>>()
685								.expect("No name found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`.")
686						}
687
688						fn pallet_prefix_hash() -> [u8; 16] {
689							<
690								<T as #frame_system::Config>::PalletInfo
691								as #frame_support::traits::PalletInfo
692							>::name_hash::<Pallet<#type_use_gen>>()
693								.expect("No name_hash found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`.")
694						}
695
696						const STORAGE_PREFIX: &'static str = #counter_prefix_struct_const;
697						fn storage_prefix_hash() -> [u8; 16] {
698							#storage_prefix_hash
699						}
700					}
701					#(#cfg_attrs)*
702					impl<#type_impl_gen> #frame_support::storage::types::CountedStorageMapInstance
703						for #prefix_struct_ident<#type_use_gen>
704						#config_where_clause
705					{
706						type CounterPrefix = #counter_prefix_struct_ident<#type_use_gen>;
707					}
708				)
709			},
710			Metadata::CountedNMap { .. } => {
711				let counter_prefix_struct_ident = counter_prefix_ident(&storage_def.ident);
712				let counter_prefix_struct_const = counter_prefix(&prefix_struct_const);
713				let storage_prefix_hash = two128_str(&counter_prefix_struct_const);
714				quote::quote_spanned!(storage_def.attr_span =>
715					#(#cfg_attrs)*
716					#[doc(hidden)]
717					#prefix_struct_vis struct #counter_prefix_struct_ident<#type_use_gen>(
718						core::marker::PhantomData<(#type_use_gen,)>
719					);
720					#(#cfg_attrs)*
721					impl<#type_impl_gen> #frame_support::traits::StorageInstance
722						for #counter_prefix_struct_ident<#type_use_gen>
723						#config_where_clause
724					{
725						fn pallet_prefix() -> &'static str {
726							<
727								<T as #frame_system::Config>::PalletInfo
728								as #frame_support::traits::PalletInfo
729							>::name::<Pallet<#type_use_gen>>()
730								.expect("No name found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`.")
731						}
732						fn pallet_prefix_hash() -> [u8; 16] {
733							<
734								<T as #frame_system::Config>::PalletInfo
735								as #frame_support::traits::PalletInfo
736							>::name_hash::<Pallet<#type_use_gen>>()
737								.expect("No name_hash found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`.")
738						}
739						const STORAGE_PREFIX: &'static str = #counter_prefix_struct_const;
740						fn storage_prefix_hash() -> [u8; 16] {
741							#storage_prefix_hash
742						}
743					}
744					#(#cfg_attrs)*
745					impl<#type_impl_gen> #frame_support::storage::types::CountedStorageNMapInstance
746						for #prefix_struct_ident<#type_use_gen>
747						#config_where_clause
748					{
749						type CounterPrefix = #counter_prefix_struct_ident<#type_use_gen>;
750					}
751				)
752			},
753			_ => proc_macro2::TokenStream::default(),
754		};
755
756		let storage_prefix_hash = two128_str(&prefix_struct_const);
757		quote::quote_spanned!(storage_def.attr_span =>
758			#maybe_counter
759
760			#(#cfg_attrs)*
761			#[doc(hidden)]
762			#prefix_struct_vis struct #prefix_struct_ident<#type_use_gen>(
763				core::marker::PhantomData<(#type_use_gen,)>
764			);
765			#(#cfg_attrs)*
766			impl<#type_impl_gen> #frame_support::traits::StorageInstance
767				for #prefix_struct_ident<#type_use_gen>
768				#config_where_clause
769			{
770				fn pallet_prefix() -> &'static str {
771					<
772						<T as #frame_system::Config>::PalletInfo
773						as #frame_support::traits::PalletInfo
774					>::name::<Pallet<#type_use_gen>>()
775						.expect("No name found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`.")
776				}
777
778				fn pallet_prefix_hash() -> [u8; 16] {
779					<
780						<T as #frame_system::Config>::PalletInfo
781						as #frame_support::traits::PalletInfo
782					>::name_hash::<Pallet<#type_use_gen>>()
783						.expect("No name_hash found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`.")
784				}
785
786				const STORAGE_PREFIX: &'static str = #prefix_struct_const;
787				fn storage_prefix_hash() -> [u8; 16] {
788					#storage_prefix_hash
789				}
790			}
791		)
792	});
793
794	let on_empty_structs = on_empty_struct_metadata.into_iter().map(|metadata| {
795		use crate::pallet::parse::GenericKind;
796		use syn::{GenericArgument, Path, PathArguments, PathSegment, Type, TypePath};
797
798		let ResultOnEmptyStructMetadata {
799			name,
800			visibility,
801			value_ty,
802			error_path,
803			variant_name,
804			span,
805		} = metadata;
806
807		let generic_kind = match error_path.segments.last() {
808			Some(PathSegment { arguments: PathArguments::AngleBracketed(args), .. }) => {
809				let (has_config, has_instance) =
810					args.args.iter().fold((false, false), |(has_config, has_instance), arg| {
811						match arg {
812							GenericArgument::Type(Type::Path(TypePath {
813								path: Path { segments, .. },
814								..
815							})) => {
816								let maybe_config =
817									segments.first().map_or(false, |seg| seg.ident == "T");
818								let maybe_instance =
819									segments.first().map_or(false, |seg| seg.ident == "I");
820
821								(has_config || maybe_config, has_instance || maybe_instance)
822							},
823							_ => (has_config, has_instance),
824						}
825					});
826				GenericKind::from_gens(has_config, has_instance).unwrap_or(GenericKind::None)
827			},
828			_ => GenericKind::None,
829		};
830		let type_impl_gen = generic_kind.type_impl_gen(proc_macro2::Span::call_site());
831		let config_where_clause = &def.config.where_clause;
832
833		quote::quote_spanned!(span =>
834			#[doc(hidden)]
835			#[allow(non_camel_case_types)]
836			#visibility struct #name;
837
838			impl<#type_impl_gen> #frame_support::traits::Get<Result<#value_ty, #error_path>>
839				for #name
840				#config_where_clause
841			{
842				#[allow(deprecated)]
843				fn get() -> Result<#value_ty, #error_path> {
844					Err(<#error_path>::#variant_name)
845				}
846			}
847		)
848	});
849
850	// aggregated where clause of all storage types and the whole pallet.
851	let mut where_clauses = vec![&def.config.where_clause];
852	where_clauses.extend(def.storages.iter().map(|storage| &storage.where_clause));
853	let completed_where_clause = super::merge_where_clauses(&where_clauses);
854	let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site());
855	let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site());
856
857	let try_decode_entire_state = {
858		let mut storage_names = def
859			.storages
860			.iter()
861			.filter_map(|storage| {
862				// A little hacky; don't generate for cfg gated storages to not get compile errors
863				// when building "frame-feature-testing" gated storages in the "frame-support-test"
864				// crate.
865				if storage.try_decode && storage.cfg_attrs.is_empty() {
866					let ident = &storage.ident;
867					let gen = &def.type_use_generics(storage.attr_span);
868					Some(quote::quote_spanned!(storage.attr_span => #ident<#gen> ))
869				} else {
870					None
871				}
872			})
873			.collect::<Vec<_>>();
874		storage_names.sort_by_cached_key(|ident| ident.to_string());
875
876		quote::quote!(
877			#frame_support::try_runtime_enabled! {
878				#[allow(deprecated)]
879				impl<#type_impl_gen> #frame_support::traits::TryDecodeEntireStorage
880				for #pallet_ident<#type_use_gen> #completed_where_clause
881				{
882					fn try_decode_entire_state() -> Result<usize, #frame_support::__private::Vec<#frame_support::traits::TryDecodeEntireStorageError>> {
883						let pallet_name = <<T as #frame_system::Config>::PalletInfo	as #frame_support::traits::PalletInfo>
884							::name::<#pallet_ident<#type_use_gen>>()
885							.expect("Every active pallet has a name in the runtime; qed");
886
887						#frame_support::__private::log::debug!(target: "runtime::try-decode-state", "trying to decode pallet: {pallet_name}");
888
889						// NOTE: for now, we have to exclude storage items that are feature gated.
890						let mut errors = #frame_support::__private::Vec::new();
891						let mut decoded = 0usize;
892
893						#(
894							#frame_support::__private::log::debug!(target: "runtime::try-decode-state", "trying to decode storage: \
895							{pallet_name}::{}", stringify!(#storage_names));
896
897							match <#storage_names as #frame_support::traits::TryDecodeEntireStorage>::try_decode_entire_state() {
898								Ok(count) => {
899									decoded += count;
900								},
901								Err(err) => {
902									errors.extend(err);
903								},
904							}
905						)*
906
907						if errors.is_empty() {
908							Ok(decoded)
909						} else {
910							Err(errors)
911						}
912					}
913				}
914			}
915		)
916	};
917
918	quote::quote!(
919		impl<#type_impl_gen> #pallet_ident<#type_use_gen>
920			#completed_where_clause
921		{
922			#[doc(hidden)]
923			pub fn storage_metadata() -> #frame_support::__private::metadata_ir::PalletStorageMetadataIR {
924				#frame_support::__private::metadata_ir::PalletStorageMetadataIR {
925					prefix: <
926						<T as #frame_system::Config>::PalletInfo as
927						#frame_support::traits::PalletInfo
928					>::name::<#pallet_ident<#type_use_gen>>()
929						.expect("No name found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`."),
930					entries: {
931						#[allow(unused_mut)]
932						let mut entries = #frame_support::__private::vec![];
933						#( #entries_builder(&mut entries); )*
934						entries
935					},
936				}
937			}
938		}
939
940		#( #getters )*
941		#( #prefix_structs )*
942		#( #on_empty_structs )*
943
944		#try_decode_entire_state
945	)
946}