referrerpolicy=no-referrer-when-downgrade

frame_support_procedural/runtime/
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
18//! Implementation of `runtime`.
19//!
20//! `runtime` implementation is recursive and can generate code which will call itself
21//! in order to get all the pallet parts for each pallet.
22//!
23//! Pallets can define their parts:
24//!  - Implicitly: `pub type System = frame_system;`
25//!  - Explicitly: `pub type System = frame_system + Pallet + Call;`
26//!
27//! The `runtime` transitions from the implicit definition to the explicit one.
28//! From the explicit state, Substrate expands the pallets with additional information
29//! that is to be included in the runtime metadata.
30//!
31//! Pallets must provide the `tt_default_parts_v2` macro for these transitions.
32//! These are automatically implemented by the `#[pallet::pallet]` macro.
33//!
34//! This macro also generates the following enums for ease of decoding if the respective type
35//! is defined inside `#[runtime::derive]`:
36//!  - `enum RuntimeCall`: This type contains the information needed to decode extrinsics.
37//!  - `enum RuntimeEvent`: This type contains the information needed to decode events.
38//!  - `enum RuntimeError`: While this cannot be used directly to decode `sp_runtime::DispatchError`
39//!    from the chain, it contains the information needed to decode the
40//!    `sp_runtime::DispatchError::Module`.
41//!
42//! # State Transitions
43//!
44//! ```ignore
45//!  +----------+
46//!  | Implicit |
47//!  +----------+           
48//!      |                  
49//!      v                  
50//!  +----------+
51//!  | Explicit |
52//!  +----------+
53//! ```
54//!
55//! The `runtime` macro transforms the implicit declaration of each pallet
56//! `System: frame_system` to an explicit one `System: frame_system + Pallet + Call` using the
57//! `tt_default_parts_v2` macro.
58//!
59//! The `tt_default_parts_v2` macro exposes a plus separated list of pallet parts. For example, the
60//! `Event` part is exposed only if the pallet implements an event via `#[pallet::event]` macro.
61//! The tokens generated by this macro are `+ Pallet + Call` for our example.
62//!
63//! The `match_and_insert` macro takes in 3 arguments:
64//!  - target: This is the `TokenStream` that contains the `runtime` macro.
65//!  - pattern: The pattern to match against in the target stream.
66//!  - tokens: The tokens to added after the pattern match.
67//!
68//! The `runtime` macro uses the `tt_call` to get the default pallet parts via
69//! the `tt_default_parts_v2` macro defined by each pallet. The pallet parts are then returned as
70//! input to the `match_and_replace` macro.
71//! The `match_and_replace` then will modify the `runtime` to expand the implicit
72//! definition to the explicit one.
73//!
74//! For example,
75//!
76//! ```ignore
77//! #[frame_support::runtime]
78//! mod runtime {
79//! 	//...
80//!
81//!     #[runtime::pallet_index(0)]
82//! 	pub type System = frame_system; // Implicit definition of parts
83//!
84//!     #[runtime::pallet_index(1)]
85//! 	pub type Balances = pallet_balances; // Implicit definition of parts
86//! }
87//! ```
88//! This call has some implicit pallet parts, thus it will expand to:
89//! ```ignore
90//! frame_support::__private::tt_call! {
91//! 	macro = [{ pallet_balances::tt_default_parts_v2 }]
92//! 	~~> frame_support::match_and_insert! {
93//! 		target = [{
94//! 			frame_support::__private::tt_call! {
95//! 				macro = [{ frame_system::tt_default_parts_v2 }]
96//! 				~~> frame_support::match_and_insert! {
97//! 					target = [{
98//!                         #[frame_support::runtime]
99//!                         mod runtime {
100//! 	                        //...
101//!
102//!                             #[runtime::pallet_index(0)]
103//! 		                    pub type System = frame_system;
104//!                             
105//! 							#[runtime::pallet_index(1)]
106//! 		                    pub type Balances = pallet_balances;
107//!                         }
108//! 					}]
109//! 					pattern = [{ System = frame_system }]
110//! 				}
111//! 			}
112//! 		}]
113//! 		pattern = [{ Balances = pallet_balances }]
114//! 	}
115//! }
116//! ```
117//! `tt_default_parts_v2` must be defined. It returns the pallet parts inside some tokens, and
118//! then `tt_call` will pipe the returned pallet parts into the input of `match_and_insert`.
119//! Thus `match_and_insert` will initially receive the following inputs:
120//! ```ignore
121//! frame_support::match_and_insert! {
122//! 	target = [{
123//! 		frame_support::match_and_insert! {
124//! 			target = [{
125//!                  #[frame_support::runtime]
126//!                  mod runtime {
127//! 	                 //...
128//!
129//!                      #[runtime::pallet_index(0)]
130//! 		             pub type System = frame_system;
131//!
132//!                      #[runtime::pallet_index(1)]
133//! 		             pub type Balances = pallet_balances;
134//!                  }
135//! 			}]
136//! 			pattern = [{ System = frame_system }]
137//! 			tokens = [{ ::{+ Pallet + Call} }]
138//! 		}
139//! 	}]
140//! 	pattern = [{ Balances = pallet_balances }]
141//! 	tokens = [{ ::{+ Pallet + Call} }]
142//! }
143//! ```
144//! After dealing with `pallet_balances`, the inner `match_and_insert` will expand to:
145//! ```ignore
146//! frame_support::match_and_insert! {
147//! 	target = [{
148//! 		#[frame_support::runtime]
149//!         mod runtime {
150//! 	        //...
151//!
152//!             #[runtime::pallet_index(0)]
153//! 		    pub type System = frame_system; // Implicit definition of parts
154//!
155//!             #[runtime::pallet_index(1)]
156//! 		    pub type Balances = pallet_balances + Pallet + Call; // Explicit definition of parts
157//!         }
158//! 	}]
159//! 	pattern = [{ System = frame_system }]
160//! 	tokens = [{ ::{+ Pallet + Call} }]
161//! }
162//! ```
163//!
164//! Which will then finally expand to the following:
165//! ```ignore
166//! #[frame_support::runtime]
167//! mod runtime {
168//! 	//...
169//!
170//!     #[runtime::pallet_index(0)]
171//! 	pub type System = frame_system + Pallet + Call;
172//!
173//!     #[runtime::pallet_index(1)]
174//! 	pub type Balances = pallet_balances + Pallet + Call;
175//! }
176//! ```
177//!
178//! This call has no implicit pallet parts, thus it will expand to the runtime construction:
179//! ```ignore
180//! pub struct Runtime { ... }
181//! pub struct Call { ... }
182//! impl Call ...
183//! pub enum Origin { ... }
184//! ...
185//! ```
186//!
187//! Visualizing the entire flow of `#[frame_support::runtime]`, it would look like the following:
188//!
189//! ```ignore
190//! +----------------------+     +------------------------+     +-------------------+
191//! |                      |     | (defined in pallet)    |     |                   |
192//! | runtime              | --> |  tt_default_parts_v2!  | --> | match_and_insert! |
193//! | w/ no pallet parts   |     |                        |     |                   |
194//! +----------------------+     +------------------------+     +-------------------+
195//!
196//!     +----------------------+
197//!     |                      |
198//! --> | runtime              |
199//!     |  w/ pallet parts     |
200//!     +----------------------+
201//! ```
202
203pub use parse::Def;
204use proc_macro::TokenStream;
205use syn::spanned::Spanned;
206
207mod expand;
208mod parse;
209
210mod keyword {
211	syn::custom_keyword!(legacy_ordering);
212}
213
214pub fn runtime(attr: TokenStream, tokens: TokenStream) -> TokenStream {
215	let mut legacy_ordering = false;
216	if !attr.is_empty() {
217		if let Ok(_) = syn::parse::<keyword::legacy_ordering>(attr.clone()) {
218			legacy_ordering = true;
219		} else {
220			let msg = "Invalid runtime macro call: unexpected attribute. Macro call must be \
221				bare, such as `#[frame_support::runtime]` or `#[runtime]`, or must specify the \
222				`legacy_ordering` attribute, such as `#[frame_support::runtime(legacy_ordering)]` or \
223				#[runtime(legacy_ordering)].";
224			let span = proc_macro2::TokenStream::from(attr).span();
225			return syn::Error::new(span, msg).to_compile_error().into()
226		}
227	}
228
229	let item = syn::parse_macro_input!(tokens as syn::ItemMod);
230	match parse::Def::try_from(item) {
231		Ok(def) => expand::expand(def, legacy_ordering).into(),
232		Err(e) => e.to_compile_error().into(),
233	}
234}