frame_support_procedural/runtime/expand/
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
18use super::parse::runtime_types::RuntimeType;
19use crate::{
20	construct_runtime::{
21		check_pallet_number, decl_all_pallets, decl_integrity_test, decl_pallet_runtime_setup,
22		decl_static_assertions, expand,
23	},
24	runtime::{
25		parse::{
26			AllPalletsDeclaration, ExplicitAllPalletsDeclaration, ImplicitAllPalletsDeclaration,
27		},
28		Def,
29	},
30};
31use cfg_expr::Predicate;
32use frame_support_procedural_tools::{
33	generate_access_from_frame_or_crate, generate_crate_access, generate_hidden_includes,
34};
35use proc_macro2::TokenStream as TokenStream2;
36use quote::quote;
37use std::collections::HashSet;
38use syn::{Ident, Result};
39
40/// The fixed name of the system pallet.
41const SYSTEM_PALLET_NAME: &str = "System";
42
43pub fn expand(def: Def, legacy_ordering: bool) -> TokenStream2 {
44	let input = def.input;
45
46	let (check_pallet_number_res, res) = match def.pallets {
47		AllPalletsDeclaration::Implicit(ref decl) => (
48			check_pallet_number(input.clone(), decl.pallet_count),
49			construct_runtime_implicit_to_explicit(input.into(), decl.clone(), legacy_ordering),
50		),
51		AllPalletsDeclaration::Explicit(ref decl) => (
52			check_pallet_number(input, decl.pallets.len()),
53			construct_runtime_final_expansion(
54				def.runtime_struct.ident.clone(),
55				decl.clone(),
56				def.runtime_types.clone(),
57				legacy_ordering,
58			),
59		),
60	};
61
62	let res = res.unwrap_or_else(|e| e.to_compile_error());
63
64	// We want to provide better error messages to the user and thus, handle the error here
65	// separately. If there is an error, we print the error and still generate all of the code to
66	// get in overall less errors for the user.
67	let res = if let Err(error) = check_pallet_number_res {
68		let error = error.to_compile_error();
69
70		quote! {
71			#error
72
73			#res
74		}
75	} else {
76		res
77	};
78
79	let res = expander::Expander::new("construct_runtime")
80		.dry(std::env::var("EXPAND_MACROS").is_err())
81		.verbose(true)
82		.write_to_out_dir(res)
83		.expect("Does not fail because of IO in OUT_DIR; qed");
84
85	res.into()
86}
87
88fn construct_runtime_implicit_to_explicit(
89	input: TokenStream2,
90	definition: ImplicitAllPalletsDeclaration,
91	legacy_ordering: bool,
92) -> Result<TokenStream2> {
93	let frame_support = generate_access_from_frame_or_crate("frame-support")?;
94	let attr = if legacy_ordering { quote!((legacy_ordering)) } else { quote!() };
95	let mut expansion = quote::quote!(
96		#[#frame_support::runtime #attr]
97		#input
98	);
99	for pallet in definition.pallet_decls.iter() {
100		let pallet_path = &pallet.path;
101		let pallet_name = &pallet.name;
102		let runtime_param = &pallet.runtime_param;
103		let pallet_segment_and_instance = match (&pallet.pallet_segment, &pallet.instance) {
104			(Some(segment), Some(instance)) => quote::quote!(::#segment<#runtime_param, #instance>),
105			(Some(segment), None) => quote::quote!(::#segment<#runtime_param>),
106			(None, Some(instance)) => quote::quote!(<#instance>),
107			(None, None) => quote::quote!(),
108		};
109		expansion = quote::quote!(
110			#frame_support::__private::tt_call! {
111				macro = [{ #pallet_path::tt_default_parts_v2 }]
112				your_tt_return = [{ #frame_support::__private::tt_return }]
113				~~> #frame_support::match_and_insert! {
114					target = [{ #expansion }]
115					pattern = [{ #pallet_name = #pallet_path #pallet_segment_and_instance }]
116				}
117			}
118		);
119	}
120
121	Ok(expansion)
122}
123
124fn construct_runtime_final_expansion(
125	name: Ident,
126	definition: ExplicitAllPalletsDeclaration,
127	runtime_types: Vec<RuntimeType>,
128	legacy_ordering: bool,
129) -> Result<TokenStream2> {
130	let ExplicitAllPalletsDeclaration { mut pallets, name: pallets_name } = definition;
131
132	if !legacy_ordering {
133		// Ensure that order of hooks is based on the pallet index
134		pallets.sort_by_key(|p| p.index);
135	}
136
137	let system_pallet =
138		pallets.iter().find(|decl| decl.name == SYSTEM_PALLET_NAME).ok_or_else(|| {
139			syn::Error::new(
140				pallets_name.span(),
141				"`System` pallet declaration is missing. \
142			 Please add this line: `pub type System = frame_system;`",
143			)
144		})?;
145	if !system_pallet.cfg_pattern.is_empty() {
146		return Err(syn::Error::new(
147			system_pallet.name.span(),
148			"`System` pallet declaration is feature gated, please remove any `#[cfg]` attributes",
149		))
150	}
151
152	let features = pallets
153		.iter()
154		.filter_map(|decl| {
155			(!decl.cfg_pattern.is_empty()).then(|| {
156				decl.cfg_pattern.iter().flat_map(|attr| {
157					attr.predicates().filter_map(|pred| match pred {
158						Predicate::Feature(feat) => Some(feat),
159						Predicate::Test => Some("test"),
160						_ => None,
161					})
162				})
163			})
164		})
165		.flatten()
166		.collect::<HashSet<_>>();
167
168	let hidden_crate_name = "construct_runtime";
169	let scrate = generate_crate_access(hidden_crate_name, "frame-support");
170	let scrate_decl = generate_hidden_includes(hidden_crate_name, "frame-support");
171
172	let frame_system = generate_access_from_frame_or_crate("frame-system")?;
173	let block = quote!(<#name as #frame_system::Config>::Block);
174	let unchecked_extrinsic = quote!(<#block as #scrate::sp_runtime::traits::Block>::Extrinsic);
175
176	let mut dispatch = None;
177	let mut outer_event = None;
178	let mut outer_error = None;
179	let mut outer_origin = None;
180	let mut freeze_reason = None;
181	let mut hold_reason = None;
182	let mut slash_reason = None;
183	let mut lock_id = None;
184	let mut task = None;
185
186	for runtime_type in runtime_types.iter() {
187		match runtime_type {
188			RuntimeType::RuntimeCall(_) => {
189				dispatch =
190					Some(expand::expand_outer_dispatch(&name, system_pallet, &pallets, &scrate));
191			},
192			RuntimeType::RuntimeEvent(_) => {
193				outer_event = Some(expand::expand_outer_enum(
194					&name,
195					&pallets,
196					&scrate,
197					expand::OuterEnumType::Event,
198				)?);
199			},
200			RuntimeType::RuntimeError(_) => {
201				outer_error = Some(expand::expand_outer_enum(
202					&name,
203					&pallets,
204					&scrate,
205					expand::OuterEnumType::Error,
206				)?);
207			},
208			RuntimeType::RuntimeOrigin(_) => {
209				outer_origin =
210					Some(expand::expand_outer_origin(&name, system_pallet, &pallets, &scrate)?);
211			},
212			RuntimeType::RuntimeFreezeReason(_) => {
213				freeze_reason = Some(expand::expand_outer_freeze_reason(&pallets, &scrate));
214			},
215			RuntimeType::RuntimeHoldReason(_) => {
216				hold_reason = Some(expand::expand_outer_hold_reason(&pallets, &scrate));
217			},
218			RuntimeType::RuntimeSlashReason(_) => {
219				slash_reason = Some(expand::expand_outer_slash_reason(&pallets, &scrate));
220			},
221			RuntimeType::RuntimeLockId(_) => {
222				lock_id = Some(expand::expand_outer_lock_id(&pallets, &scrate));
223			},
224			RuntimeType::RuntimeTask(_) => {
225				task = Some(expand::expand_outer_task(&name, &pallets, &scrate));
226			},
227		}
228	}
229
230	let all_pallets = decl_all_pallets(&name, pallets.iter(), &features);
231	let pallet_to_index = decl_pallet_runtime_setup(&name, &pallets, &scrate);
232
233	let metadata = expand::expand_runtime_metadata(
234		&name,
235		&pallets,
236		&scrate,
237		&unchecked_extrinsic,
238		&system_pallet.path,
239	);
240	let outer_config = expand::expand_outer_config(&name, &pallets, &scrate);
241	let inherent =
242		expand::expand_outer_inherent(&name, &block, &unchecked_extrinsic, &pallets, &scrate);
243	let validate_unsigned = expand::expand_outer_validate_unsigned(&name, &pallets, &scrate);
244	let integrity_test = decl_integrity_test(&scrate);
245	let static_assertions = decl_static_assertions(&name, &pallets, &scrate);
246
247	let res = quote!(
248		#scrate_decl
249
250		// Prevent UncheckedExtrinsic to print unused warning.
251		const _: () = {
252			#[allow(unused)]
253			type __HiddenUseOfUncheckedExtrinsic = #unchecked_extrinsic;
254		};
255
256		#[derive(
257			Clone, Copy, PartialEq, Eq, #scrate::sp_runtime::RuntimeDebug,
258			#scrate::__private::scale_info::TypeInfo
259		)]
260		pub struct #name;
261		impl #scrate::sp_runtime::traits::GetRuntimeBlockType for #name {
262			type RuntimeBlock = #block;
263		}
264
265		// Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata.
266		// The function is implemented by calling `impl_runtime_apis!`.
267		//
268		// However, the `runtime` may be used without calling `impl_runtime_apis!`.
269		// Rely on the `Deref` trait to differentiate between a runtime that implements
270		// APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro runtime).
271		//
272		// Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function.
273		// `InternalConstructRuntime` is implemented by the `runtime` for Runtime references (`& Runtime`),
274		// while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`).
275		//
276		// Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!`
277		// when both macros are called; and will resolve an empty `runtime_metadata` when only the `runtime`
278		// is used.
279
280		#[doc(hidden)]
281		trait InternalConstructRuntime {
282			#[inline(always)]
283			fn runtime_metadata(&self) -> #scrate::__private::Vec<#scrate::__private::metadata_ir::RuntimeApiMetadataIR> {
284				Default::default()
285			}
286		}
287		#[doc(hidden)]
288		impl InternalConstructRuntime for &#name {}
289
290		#outer_event
291
292		#outer_error
293
294		#outer_origin
295
296		#all_pallets
297
298		#pallet_to_index
299
300		#dispatch
301
302		#task
303
304		#metadata
305
306		#outer_config
307
308		#inherent
309
310		#validate_unsigned
311
312		#freeze_reason
313
314		#hold_reason
315
316		#lock_id
317
318		#slash_reason
319
320		#integrity_test
321
322		#static_assertions
323	);
324
325	Ok(res)
326}