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