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}