referrerpolicy=no-referrer-when-downgrade

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	let mut query = None;
186
187	for runtime_type in runtime_types.iter() {
188		match runtime_type {
189			RuntimeType::RuntimeCall(_) => {
190				dispatch =
191					Some(expand::expand_outer_dispatch(&name, system_pallet, &pallets, &scrate));
192			},
193			RuntimeType::RuntimeEvent(_) => {
194				outer_event = Some(expand::expand_outer_enum(
195					&name,
196					&pallets,
197					&scrate,
198					expand::OuterEnumType::Event,
199				)?);
200			},
201			RuntimeType::RuntimeError(_) => {
202				outer_error = Some(expand::expand_outer_enum(
203					&name,
204					&pallets,
205					&scrate,
206					expand::OuterEnumType::Error,
207				)?);
208			},
209			RuntimeType::RuntimeOrigin(_) => {
210				outer_origin =
211					Some(expand::expand_outer_origin(&name, system_pallet, &pallets, &scrate)?);
212			},
213			RuntimeType::RuntimeFreezeReason(_) => {
214				freeze_reason = Some(expand::expand_outer_freeze_reason(&pallets, &scrate));
215			},
216			RuntimeType::RuntimeHoldReason(_) => {
217				hold_reason = Some(expand::expand_outer_hold_reason(&pallets, &scrate));
218			},
219			RuntimeType::RuntimeSlashReason(_) => {
220				slash_reason = Some(expand::expand_outer_slash_reason(&pallets, &scrate));
221			},
222			RuntimeType::RuntimeLockId(_) => {
223				lock_id = Some(expand::expand_outer_lock_id(&pallets, &scrate));
224			},
225			RuntimeType::RuntimeTask(_) => {
226				task = Some(expand::expand_outer_task(&name, &pallets, &scrate));
227			},
228			RuntimeType::RuntimeViewFunction(_) => {
229				query = Some(expand::expand_outer_query(&name, &pallets, &scrate));
230			},
231		}
232	}
233
234	let all_pallets = decl_all_pallets(&name, pallets.iter(), &features);
235	let pallet_to_index = decl_pallet_runtime_setup(&name, &pallets, &scrate);
236
237	let metadata = expand::expand_runtime_metadata(
238		&name,
239		&pallets,
240		&scrate,
241		&unchecked_extrinsic,
242		&system_pallet.path,
243	);
244	let outer_config: TokenStream2 = expand::expand_outer_config(&name, &pallets, &scrate);
245	let inherent =
246		expand::expand_outer_inherent(&name, &block, &unchecked_extrinsic, &pallets, &scrate);
247	let validate_unsigned = expand::expand_outer_validate_unsigned(&name, &pallets, &scrate);
248	let integrity_test = decl_integrity_test(&scrate);
249	let static_assertions = decl_static_assertions(&name, &pallets, &scrate);
250
251	let res = quote!(
252		#scrate_decl
253
254		// Prevent UncheckedExtrinsic to print unused warning.
255		const _: () = {
256			#[allow(unused)]
257			type __HiddenUseOfUncheckedExtrinsic = #unchecked_extrinsic;
258		};
259
260		#[derive(
261			Clone, Copy, PartialEq, Eq, #scrate::sp_runtime::RuntimeDebug,
262			#scrate::__private::scale_info::TypeInfo
263		)]
264		pub struct #name;
265		impl #scrate::sp_runtime::traits::GetRuntimeBlockType for #name {
266			type RuntimeBlock = #block;
267		}
268
269		// Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata.
270		// The function is implemented by calling `impl_runtime_apis!`.
271		//
272		// However, the `runtime` may be used without calling `impl_runtime_apis!`.
273		// Rely on the `Deref` trait to differentiate between a runtime that implements
274		// APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro runtime).
275		//
276		// Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function.
277		// `InternalConstructRuntime` is implemented by the `runtime` for Runtime references (`& Runtime`),
278		// while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`).
279		//
280		// Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!`
281		// when both macros are called; and will resolve an empty `runtime_metadata` when only the `runtime`
282		// is used.
283
284		#[doc(hidden)]
285		trait InternalConstructRuntime {
286			#[inline(always)]
287			fn runtime_metadata(&self) -> #scrate::__private::Vec<#scrate::__private::metadata_ir::RuntimeApiMetadataIR> {
288				Default::default()
289			}
290		}
291		#[doc(hidden)]
292		impl InternalConstructRuntime for &#name {}
293
294		#outer_event
295
296		#outer_error
297
298		#outer_origin
299
300		#all_pallets
301
302		#pallet_to_index
303
304		#dispatch
305
306		#task
307
308		#query
309
310		#metadata
311
312		#outer_config
313
314		#inherent
315
316		#validate_unsigned
317
318		#freeze_reason
319
320		#hold_reason
321
322		#lock_id
323
324		#slash_reason
325
326		#integrity_test
327
328		#static_assertions
329	);
330
331	Ok(res)
332}