referrerpolicy=no-referrer-when-downgrade

frame_support_procedural/pallet/expand/
error.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	deprecation::extract_or_return_allow_attrs,
20	pallet::{
21		parse::error::{VariantDef, VariantField},
22		Def,
23	},
24	COUNTER,
25};
26use frame_support_procedural_tools::get_doc_literals;
27use quote::ToTokens;
28use syn::spanned::Spanned;
29
30///
31/// * impl various trait on Error
32pub fn expand_error(def: &mut Def) -> proc_macro2::TokenStream {
33	let count = COUNTER.with(|counter| counter.borrow_mut().inc());
34	let error_token_unique_id =
35		syn::Ident::new(&format!("__tt_error_token_{}", count), def.item.span());
36
37	let frame_support = &def.frame_support;
38	let frame_system = &def.frame_system;
39	let config_where_clause = &def.config.where_clause;
40
41	let error = if let Some(error) = &def.error {
42		error
43	} else {
44		return quote::quote! {
45			#[macro_export]
46			#[doc(hidden)]
47			macro_rules! #error_token_unique_id {
48				{
49					$caller:tt
50					your_tt_return = [{ $my_tt_return:path }]
51				} => {
52					$my_tt_return! {
53						$caller
54					}
55				};
56			}
57
58			pub use #error_token_unique_id as tt_error_token;
59		}
60	};
61
62	let error_ident = &error.error;
63	let type_impl_gen = &def.type_impl_generics(error.attr_span);
64	let type_use_gen = &def.type_use_generics(error.attr_span);
65
66	let phantom_variant: syn::Variant = syn::parse_quote!(
67		#[doc(hidden)]
68		#[codec(skip)]
69		__Ignore(
70			core::marker::PhantomData<(#type_use_gen)>,
71			#frame_support::Never,
72		)
73	);
74
75	let as_str_matches =
76		error
77			.variants
78			.iter()
79			.map(|VariantDef { ident: variant, field: field_ty, cfg_attrs, maybe_allow_attrs }| {
80				let variant_str = variant.to_string();
81				let cfg_attrs = cfg_attrs.iter().map(|attr| attr.to_token_stream());
82				match field_ty {
83					Some(VariantField { is_named: true }) => {
84						quote::quote_spanned!(error.attr_span => #( #cfg_attrs )* #(#maybe_allow_attrs)* Self::#variant { .. } => #variant_str,)
85					},
86					Some(VariantField { is_named: false }) => {
87						quote::quote_spanned!(error.attr_span => #( #cfg_attrs )* #(#maybe_allow_attrs)* Self::#variant(..) => #variant_str,)
88					},
89					None => {
90						quote::quote_spanned!(error.attr_span => #( #cfg_attrs )* #(#maybe_allow_attrs)* Self::#variant => #variant_str,)
91					},
92				}
93			});
94
95	let error_item = {
96		let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[error.index];
97		if let syn::Item::Enum(item) = item {
98			item
99		} else {
100			unreachable!("Checked by error parser")
101		}
102	};
103	error_item.variants.insert(0, phantom_variant);
104
105	let capture_docs = if cfg!(feature = "no-metadata-docs") { "never" } else { "always" };
106
107	let deprecation = match crate::deprecation::get_deprecation_enum(
108		&quote::quote! {#frame_support},
109		error_item.variants.iter().enumerate().map(|(index, item)| {
110			let index = crate::deprecation::variant_index_for_deprecation(index as u8, item);
111
112			(index, item.attrs.as_ref())
113		}),
114	) {
115		Ok(deprecation) => deprecation,
116		Err(e) => return e.into_compile_error(),
117	};
118
119	// derive TypeInfo for error metadata
120	error_item.attrs.push(syn::parse_quote! {
121		#[derive(
122			#frame_support::__private::codec::Encode,
123			#frame_support::__private::codec::Decode,
124			#frame_support::__private::codec::DecodeWithMemTracking,
125			#frame_support::__private::scale_info::TypeInfo,
126			#frame_support::PalletError,
127		)]
128	});
129	error_item.attrs.push(syn::parse_quote!(
130		#[scale_info(skip_type_params(#type_use_gen), capture_docs = #capture_docs)]
131	));
132
133	if get_doc_literals(&error_item.attrs).is_empty() {
134		error_item.attrs.push(syn::parse_quote!(
135			#[doc = "The `Error` enum of this pallet."]
136		));
137	}
138
139	// Extracts #[allow] attributes, necessary so that we don't run into compiler warnings
140	let maybe_allow_attrs: Vec<syn::Attribute> =
141		extract_or_return_allow_attrs(&error_item.attrs).collect();
142
143	quote::quote_spanned!(error.attr_span =>
144		#(#maybe_allow_attrs)*
145		impl<#type_impl_gen> core::fmt::Debug for #error_ident<#type_use_gen>
146			#config_where_clause
147		{
148			fn fmt(&self, f: &mut core::fmt::Formatter<'_>)
149				-> core::fmt::Result
150			{
151				f.write_str(self.as_str())
152			}
153		}
154
155		#(#maybe_allow_attrs)*
156		impl<#type_impl_gen> #error_ident<#type_use_gen> #config_where_clause {
157			#[doc(hidden)]
158			pub fn as_str(&self) -> &'static str {
159				match &self {
160					#(#maybe_allow_attrs)* Self::__Ignore(_, _) => unreachable!("`__Ignore` can never be constructed"),
161					#( #as_str_matches )*
162				}
163			}
164		}
165
166		#(#maybe_allow_attrs)*
167		impl<#type_impl_gen> From<#error_ident<#type_use_gen>> for &'static str
168			#config_where_clause
169		{
170			fn from(err: #error_ident<#type_use_gen>) -> &'static str {
171				err.as_str()
172			}
173		}
174
175		#(#maybe_allow_attrs)*
176		impl<#type_impl_gen> From<#error_ident<#type_use_gen>>
177			for #frame_support::sp_runtime::DispatchError
178			#config_where_clause
179		{
180			fn from(err: #error_ident<#type_use_gen>) -> Self {
181				use #frame_support::__private::codec::Encode;
182				let index = <
183					<T as #frame_system::Config>::PalletInfo
184					as #frame_support::traits::PalletInfo
185				>::index::<Pallet<#type_use_gen>>()
186					.expect("Every active module has an index in the runtime; qed") as u8;
187				let mut encoded = err.encode();
188				encoded.resize(#frame_support::MAX_MODULE_ERROR_ENCODED_SIZE, 0);
189
190				#frame_support::sp_runtime::DispatchError::Module(#frame_support::sp_runtime::ModuleError {
191					index,
192					error: TryInto::try_into(encoded).expect("encoded error is resized to be equal to the maximum encoded error size; qed"),
193					message: Some(err.as_str()),
194				})
195			}
196		}
197
198		#[macro_export]
199		#[doc(hidden)]
200		macro_rules! #error_token_unique_id {
201			{
202				$caller:tt
203				your_tt_return = [{ $my_tt_return:path }]
204			} => {
205				$my_tt_return! {
206					$caller
207					error = [{ #error_ident }]
208				}
209			};
210		}
211
212		pub use #error_token_unique_id as tt_error_token;
213
214		#(#maybe_allow_attrs)*
215		impl<#type_impl_gen> #error_ident<#type_use_gen> #config_where_clause {
216			#[allow(dead_code)]
217			#[doc(hidden)]
218			pub fn error_metadata() -> #frame_support::__private::metadata_ir::PalletErrorMetadataIR {
219				#(#maybe_allow_attrs)*
220				#frame_support::__private::metadata_ir::PalletErrorMetadataIR {
221					ty: #frame_support::__private::scale_info::meta_type::<#error_ident<#type_use_gen>>(),
222					deprecation_info: #deprecation,
223				}
224			}
225		}
226	)
227}