referrerpolicy=no-referrer-when-downgrade

frame_support_procedural/runtime/parse/
mod.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
18pub mod helper;
19pub mod pallet;
20pub mod pallet_decl;
21pub mod runtime_struct;
22pub mod runtime_types;
23
24use crate::construct_runtime::parse::Pallet;
25use pallet_decl::PalletDeclaration;
26use proc_macro2::TokenStream as TokenStream2;
27use quote::ToTokens;
28use std::collections::HashMap;
29use syn::{spanned::Spanned, Ident, Token};
30
31use frame_support_procedural_tools::syn_ext as ext;
32use runtime_types::RuntimeType;
33
34mod keyword {
35	use syn::custom_keyword;
36
37	custom_keyword!(runtime);
38	custom_keyword!(derive);
39	custom_keyword!(pallet_index);
40	custom_keyword!(disable_call);
41	custom_keyword!(disable_unsigned);
42}
43
44enum RuntimeAttr {
45	Runtime(proc_macro2::Span),
46	Derive(proc_macro2::Span, Vec<RuntimeType>),
47	PalletIndex(proc_macro2::Span, u8),
48	DisableCall(proc_macro2::Span),
49	DisableUnsigned(proc_macro2::Span),
50}
51
52impl RuntimeAttr {
53	fn span(&self) -> proc_macro2::Span {
54		match self {
55			Self::Runtime(span) => *span,
56			Self::Derive(span, _) => *span,
57			Self::PalletIndex(span, _) => *span,
58			Self::DisableCall(span) => *span,
59			Self::DisableUnsigned(span) => *span,
60		}
61	}
62}
63
64impl syn::parse::Parse for RuntimeAttr {
65	fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
66		input.parse::<syn::Token![#]>()?;
67		let content;
68		syn::bracketed!(content in input);
69		content.parse::<keyword::runtime>()?;
70		content.parse::<syn::Token![::]>()?;
71
72		let lookahead = content.lookahead1();
73		if lookahead.peek(keyword::runtime) {
74			Ok(RuntimeAttr::Runtime(content.parse::<keyword::runtime>()?.span()))
75		} else if lookahead.peek(keyword::derive) {
76			let _ = content.parse::<keyword::derive>();
77			let derive_content;
78			syn::parenthesized!(derive_content in content);
79			let runtime_types =
80				derive_content.parse::<ext::Punctuated<RuntimeType, Token![,]>>()?;
81			let runtime_types = runtime_types.inner.into_iter().collect();
82			Ok(RuntimeAttr::Derive(derive_content.span(), runtime_types))
83		} else if lookahead.peek(keyword::pallet_index) {
84			let _ = content.parse::<keyword::pallet_index>();
85			let pallet_index_content;
86			syn::parenthesized!(pallet_index_content in content);
87			let pallet_index = pallet_index_content.parse::<syn::LitInt>()?;
88			if !pallet_index.suffix().is_empty() {
89				let msg = "Number literal must not have a suffix";
90				return Err(syn::Error::new(pallet_index.span(), msg));
91			}
92			Ok(RuntimeAttr::PalletIndex(pallet_index.span(), pallet_index.base10_parse()?))
93		} else if lookahead.peek(keyword::disable_call) {
94			Ok(RuntimeAttr::DisableCall(content.parse::<keyword::disable_call>()?.span()))
95		} else if lookahead.peek(keyword::disable_unsigned) {
96			Ok(RuntimeAttr::DisableUnsigned(content.parse::<keyword::disable_unsigned>()?.span()))
97		} else {
98			Err(lookahead.error())
99		}
100	}
101}
102
103#[derive(Debug, Clone)]
104pub enum AllPalletsDeclaration {
105	Implicit(ImplicitAllPalletsDeclaration),
106	Explicit(ExplicitAllPalletsDeclaration),
107}
108
109/// Declaration of a runtime with some pallet with implicit declaration of parts.
110#[derive(Debug, Clone)]
111pub struct ImplicitAllPalletsDeclaration {
112	pub pallet_decls: Vec<PalletDeclaration>,
113	pub pallet_count: usize,
114}
115
116/// Declaration of a runtime with all pallet having explicit declaration of parts.
117#[derive(Debug, Clone)]
118pub struct ExplicitAllPalletsDeclaration {
119	pub name: Ident,
120	pub pallets: Vec<Pallet>,
121}
122
123pub struct Def {
124	pub input: TokenStream2,
125	pub runtime_struct: runtime_struct::RuntimeStructDef,
126	pub pallets: AllPalletsDeclaration,
127	pub runtime_types: Vec<RuntimeType>,
128}
129
130impl Def {
131	pub fn try_from(mut item: syn::ItemMod) -> syn::Result<Self> {
132		let input: TokenStream2 = item.to_token_stream().into();
133		let item_span = item.span();
134		let items = &mut item
135			.content
136			.as_mut()
137			.ok_or_else(|| {
138				let msg = "Invalid runtime definition, expected mod to be inlined.";
139				syn::Error::new(item_span, msg)
140			})?
141			.1;
142
143		let mut runtime_struct = None;
144		let mut runtime_types = None;
145
146		let mut indices = HashMap::new();
147		let mut names = HashMap::new();
148
149		let mut pallet_decls = vec![];
150		let mut pallets = vec![];
151
152		for item in items.iter_mut() {
153			let mut pallet_index_and_item = None;
154
155			let mut disable_call = false;
156			let mut disable_unsigned = false;
157
158			while let Some(runtime_attr) =
159				helper::take_first_item_runtime_attr::<RuntimeAttr>(item)?
160			{
161				match runtime_attr {
162					RuntimeAttr::Runtime(_) if runtime_struct.is_none() => {
163						let p = runtime_struct::RuntimeStructDef::try_from(item)?;
164						runtime_struct = Some(p);
165					},
166					RuntimeAttr::Derive(_, types) if runtime_types.is_none() => {
167						runtime_types = Some(types);
168					},
169					RuntimeAttr::PalletIndex(span, index) => {
170						pallet_index_and_item = if let syn::Item::Type(item) = item {
171							Some((index, item.clone()))
172						} else {
173							let msg = "Invalid runtime::pallet_index, expected type definition";
174							return Err(syn::Error::new(span, msg));
175						};
176					},
177					RuntimeAttr::DisableCall(_) => disable_call = true,
178					RuntimeAttr::DisableUnsigned(_) => disable_unsigned = true,
179					attr => {
180						let msg = "Invalid duplicated attribute";
181						return Err(syn::Error::new(attr.span(), msg));
182					},
183				}
184			}
185
186			if let Some((pallet_index, pallet_item)) = pallet_index_and_item {
187				match *pallet_item.ty.clone() {
188					syn::Type::Path(ref path) => {
189						let pallet_decl =
190							PalletDeclaration::try_from(item.span(), &pallet_item, &path.path)?;
191
192						if let Some(used_pallet) =
193							names.insert(pallet_decl.name.clone(), pallet_decl.name.span())
194						{
195							let msg = "Two pallets with the same name!";
196
197							let mut err = syn::Error::new(used_pallet, &msg);
198							err.combine(syn::Error::new(pallet_decl.name.span(), &msg));
199							return Err(err);
200						}
201
202						pallet_decls.push(pallet_decl);
203					},
204					syn::Type::TraitObject(syn::TypeTraitObject { bounds, .. }) => {
205						let pallet = Pallet::try_from(
206							item.span(),
207							&pallet_item,
208							pallet_index,
209							disable_call,
210							disable_unsigned,
211							&bounds,
212						)?;
213
214						if let Some(used_pallet) = indices.insert(pallet.index, pallet.name.clone())
215						{
216							let msg = format!(
217								"Pallet indices are conflicting: Both pallets {} and {} are at index {}",
218								used_pallet, pallet.name, pallet.index,
219							);
220							let mut err = syn::Error::new(used_pallet.span(), &msg);
221							err.combine(syn::Error::new(pallet.name.span(), msg));
222							return Err(err);
223						}
224
225						pallets.push(pallet);
226					},
227					_ => continue,
228				}
229			} else {
230				if let syn::Item::Type(item) = item {
231					let msg = "Missing pallet index for pallet declaration. Please add `#[runtime::pallet_index(...)]`";
232					return Err(syn::Error::new(item.span(), &msg));
233				}
234			}
235		}
236
237		let name = item.ident.clone();
238		let decl_count = pallet_decls.len();
239		let pallets = if decl_count > 0 {
240			AllPalletsDeclaration::Implicit(ImplicitAllPalletsDeclaration {
241				pallet_decls,
242				pallet_count: decl_count.saturating_add(pallets.len()),
243			})
244		} else {
245			AllPalletsDeclaration::Explicit(ExplicitAllPalletsDeclaration { name, pallets })
246		};
247
248		let def = Def {
249			input,
250			runtime_struct: runtime_struct.ok_or_else(|| {
251				syn::Error::new(item_span,
252					"Missing Runtime. Please add a struct inside the module and annotate it with `#[runtime::runtime]`"
253				)
254			})?,
255			pallets,
256			runtime_types: runtime_types.ok_or_else(|| {
257				syn::Error::new(item_span,
258					"Missing Runtime Types. Please annotate the runtime struct with `#[runtime::derive]`"
259				)
260			})?,
261		};
262
263		Ok(def)
264	}
265}
266
267#[test]
268fn runtime_parsing_works() {
269	let def = Def::try_from(syn::parse_quote! {
270		#[runtime::runtime]
271		mod runtime {
272			#[runtime::derive(RuntimeCall, RuntimeEvent)]
273			#[runtime::runtime]
274			pub struct Runtime;
275
276			#[runtime::pallet_index(0)]
277			pub type System = frame_system::Pallet<Runtime>;
278
279			#[runtime::pallet_index(1)]
280			pub type Pallet1 = pallet1<Instance1>;
281		}
282	})
283	.expect("Failed to parse runtime definition");
284
285	assert_eq!(def.runtime_struct.ident, "Runtime");
286}