frame_support_procedural/runtime/parse/
mod.rs
1pub 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#[derive(Debug, Clone)]
111pub struct ImplicitAllPalletsDeclaration {
112 pub pallet_decls: Vec<PalletDeclaration>,
113 pub pallet_count: usize,
114}
115
116#[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}