frame_support_procedural/pallet/expand/
error.rs1use 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
29pub 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 "e::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 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}