referrerpolicy=no-referrer-when-downgrade

frame_support_procedural/
pallet_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 frame_support_procedural_tools::generate_access_from_frame_or_crate;
19use quote::ToTokens;
20
21// Derive `PalletError`
22pub fn derive_pallet_error(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
23	let syn::DeriveInput { ident: name, generics, data, .. } = match syn::parse(input) {
24		Ok(input) => input,
25		Err(e) => return e.to_compile_error().into(),
26	};
27
28	let frame_support = match generate_access_from_frame_or_crate("frame-support") {
29		Ok(c) => c,
30		Err(e) => return e.into_compile_error().into(),
31	};
32	let frame_support = &frame_support;
33	let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
34
35	let max_encoded_size = match data {
36		syn::Data::Struct(syn::DataStruct { fields, .. }) => match fields {
37			syn::Fields::Named(syn::FieldsNamed { named: fields, .. }) |
38			syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed: fields, .. }) => {
39				let maybe_field_tys = fields
40					.iter()
41					.map(|f| generate_field_types(f, &frame_support))
42					.collect::<syn::Result<Vec<_>>>();
43				let field_tys = match maybe_field_tys {
44					Ok(tys) => tys.into_iter().flatten(),
45					Err(e) => return e.into_compile_error().into(),
46				};
47				quote::quote! {
48					0_usize
49					#(
50						.saturating_add(<
51							#field_tys as #frame_support::traits::PalletError
52						>::MAX_ENCODED_SIZE)
53					)*
54				}
55			},
56			syn::Fields::Unit => quote::quote!(0),
57		},
58		syn::Data::Enum(syn::DataEnum { variants, .. }) => {
59			let field_tys = variants
60				.iter()
61				.map(|variant| generate_variant_field_types(variant, &frame_support))
62				.collect::<Result<Vec<Option<Vec<proc_macro2::TokenStream>>>, syn::Error>>();
63
64			let field_tys = match field_tys {
65				Ok(tys) => tys.into_iter().flatten().collect::<Vec<_>>(),
66				Err(e) => return e.to_compile_error().into(),
67			};
68
69			// We start with `1`, because the discriminant of an enum is stored as u8
70			if field_tys.is_empty() {
71				quote::quote!(1)
72			} else {
73				let variant_sizes = field_tys.into_iter().map(|variant_field_tys| {
74					quote::quote! {
75						1_usize
76						#(.saturating_add(<
77							#variant_field_tys as #frame_support::traits::PalletError
78						>::MAX_ENCODED_SIZE))*
79					}
80				});
81
82				quote::quote! {{
83					let mut size = 1_usize;
84					let mut tmp = 0_usize;
85					#(
86						tmp = #variant_sizes;
87						size = if tmp > size { tmp } else { size };
88						tmp = 0_usize;
89					)*
90					size
91				}}
92			}
93		},
94		syn::Data::Union(syn::DataUnion { union_token, .. }) => {
95			let msg = "Cannot derive `PalletError` for union; please implement it directly";
96			return syn::Error::new(union_token.span, msg).into_compile_error().into()
97		},
98	};
99
100	quote::quote!(
101		#[allow(deprecated)]
102		const _: () = {
103			impl #impl_generics #frame_support::traits::PalletError
104				for #name #ty_generics #where_clause
105			{
106				const MAX_ENCODED_SIZE: usize = #max_encoded_size;
107			}
108		};
109	)
110	.into()
111}
112
113fn generate_field_types(
114	field: &syn::Field,
115	scrate: &syn::Path,
116) -> syn::Result<Option<proc_macro2::TokenStream>> {
117	let attrs = &field.attrs;
118
119	for attr in attrs {
120		if attr.path().is_ident("codec") {
121			let mut res = None;
122
123			attr.parse_nested_meta(|meta| {
124				if meta.path.is_ident("skip") {
125					res = Some(None);
126				} else if meta.path.is_ident("compact") {
127					let field_ty = &field.ty;
128					res = Some(Some(quote::quote!(#scrate::__private::codec::Compact<#field_ty>)));
129				} else if meta.path.is_ident("compact") {
130					res = Some(Some(meta.value()?.parse()?));
131				}
132
133				Ok(())
134			})?;
135
136			if let Some(v) = res {
137				return Ok(v)
138			}
139		}
140	}
141
142	Ok(Some(field.ty.to_token_stream()))
143}
144
145fn generate_variant_field_types(
146	variant: &syn::Variant,
147	scrate: &syn::Path,
148) -> syn::Result<Option<Vec<proc_macro2::TokenStream>>> {
149	let attrs = &variant.attrs;
150
151	for attr in attrs {
152		if attr.path().is_ident("codec") {
153			let mut skip = false;
154
155			// We ignore the error intentionally as this isn't `codec(skip)` when
156			// `parse_nested_meta` fails.
157			let _ = attr.parse_nested_meta(|meta| {
158				skip = meta.path.is_ident("skip");
159				Ok(())
160			});
161
162			if skip {
163				return Ok(None)
164			}
165		}
166	}
167
168	match &variant.fields {
169		syn::Fields::Named(syn::FieldsNamed { named: fields, .. }) |
170		syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed: fields, .. }) => {
171			let field_tys = fields
172				.iter()
173				.map(|field| generate_field_types(field, scrate))
174				.collect::<syn::Result<Vec<_>>>()?;
175			Ok(Some(field_tys.into_iter().flatten().collect()))
176		},
177		syn::Fields::Unit => Ok(None),
178	}
179}