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