frame_support_procedural/construct_runtime/
mod.rs1pub(crate) mod expand;
212pub(crate) mod parse;
213
214use crate::pallet::parse::helper::two128_str;
215use cfg_expr::Predicate;
216use frame_support_procedural_tools::{
217 generate_access_from_frame_or_crate, generate_crate_access, generate_hidden_includes,
218};
219use itertools::Itertools;
220use parse::{ExplicitRuntimeDeclaration, ImplicitRuntimeDeclaration, Pallet, RuntimeDeclaration};
221use proc_macro::TokenStream;
222use proc_macro2::TokenStream as TokenStream2;
223use quote::quote;
224use std::{collections::HashSet, str::FromStr};
225use syn::{spanned::Spanned, Ident, Result};
226
227const SYSTEM_PALLET_NAME: &str = "System";
229
230pub fn construct_runtime(input: TokenStream) -> TokenStream {
233 let input_copy = input.clone();
234 let definition = syn::parse_macro_input!(input as RuntimeDeclaration);
235
236 let (check_pallet_number_res, res) = match definition {
237 RuntimeDeclaration::Implicit(implicit_def) => (
238 check_pallet_number(input_copy.clone().into(), implicit_def.pallets.len()),
239 construct_runtime_implicit_to_explicit(input_copy.into(), implicit_def),
240 ),
241 RuntimeDeclaration::Explicit(explicit_decl) => (
242 check_pallet_number(input_copy.clone().into(), explicit_decl.pallets.len()),
243 construct_runtime_explicit_to_explicit_expanded(input_copy.into(), explicit_decl),
244 ),
245 RuntimeDeclaration::ExplicitExpanded(explicit_decl) => (
246 check_pallet_number(input_copy.into(), explicit_decl.pallets.len()),
247 construct_runtime_final_expansion(explicit_decl),
248 ),
249 };
250
251 let res = res.unwrap_or_else(|e| e.to_compile_error());
252
253 let res = if let Err(error) = check_pallet_number_res {
257 let error = error.to_compile_error();
258
259 quote! {
260 #error
261
262 #res
263 }
264 } else {
265 res
266 };
267
268 let res = expander::Expander::new("construct_runtime")
269 .dry(std::env::var("EXPAND_MACROS").is_err())
270 .verbose(true)
271 .write_to_out_dir(res)
272 .expect("Does not fail because of IO in OUT_DIR; qed");
273
274 res.into()
275}
276
277fn construct_runtime_implicit_to_explicit(
285 input: TokenStream2,
286 definition: ImplicitRuntimeDeclaration,
287) -> Result<TokenStream2> {
288 let frame_support = generate_access_from_frame_or_crate("frame-support")?;
289 let mut expansion = quote::quote!(
290 #frame_support::construct_runtime! { #input }
291 );
292 for pallet in definition.pallets.iter().filter(|pallet| pallet.pallet_parts.is_none()) {
293 let pallet_path = &pallet.path;
294 let pallet_name = &pallet.name;
295 let pallet_instance = pallet.instance.as_ref().map(|instance| quote::quote!(::<#instance>));
296 expansion = quote::quote!(
297 #frame_support::__private::tt_call! {
298 macro = [{ #pallet_path::tt_default_parts }]
299 your_tt_return = [{ #frame_support::__private::tt_return }]
300 ~~> #frame_support::match_and_insert! {
301 target = [{ #expansion }]
302 pattern = [{ #pallet_name: #pallet_path #pallet_instance }]
303 }
304 }
305 );
306 }
307
308 Ok(expansion)
309}
310
311fn construct_runtime_explicit_to_explicit_expanded(
321 input: TokenStream2,
322 definition: ExplicitRuntimeDeclaration,
323) -> Result<TokenStream2> {
324 let frame_support = generate_access_from_frame_or_crate("frame-support")?;
325 let mut expansion = quote::quote!(
326 #frame_support::construct_runtime! { #input }
327 );
328 for pallet in definition.pallets.iter().filter(|pallet| !pallet.is_expanded) {
329 let pallet_path = &pallet.path;
330 let pallet_name = &pallet.name;
331 let pallet_instance = pallet.instance.as_ref().map(|instance| quote::quote!(::<#instance>));
332 expansion = quote::quote!(
333 #frame_support::__private::tt_call! {
334 macro = [{ #pallet_path::tt_extra_parts }]
335 your_tt_return = [{ #frame_support::__private::tt_return }]
336 ~~> #frame_support::match_and_insert! {
337 target = [{ #expansion }]
338 pattern = [{ #pallet_name: #pallet_path #pallet_instance }]
339 }
340 }
341 );
342 }
343
344 Ok(expansion)
345}
346
347fn construct_runtime_final_expansion(
349 definition: ExplicitRuntimeDeclaration,
350) -> Result<TokenStream2> {
351 let ExplicitRuntimeDeclaration { name, pallets, pallets_token, where_section } = definition;
352
353 let system_pallet =
354 pallets.iter().find(|decl| decl.name == SYSTEM_PALLET_NAME).ok_or_else(|| {
355 syn::Error::new(
356 pallets_token.span.join(),
357 "`System` pallet declaration is missing. \
358 Please add this line: `System: frame_system,`",
359 )
360 })?;
361 if !system_pallet.cfg_pattern.is_empty() {
362 return Err(syn::Error::new(
363 system_pallet.name.span(),
364 "`System` pallet declaration is feature gated, please remove any `#[cfg]` attributes",
365 ))
366 }
367
368 let features = pallets
369 .iter()
370 .filter_map(|decl| {
371 (!decl.cfg_pattern.is_empty()).then(|| {
372 decl.cfg_pattern.iter().flat_map(|attr| {
373 attr.predicates().filter_map(|pred| match pred {
374 Predicate::Feature(feat) => Some(feat),
375 Predicate::Test => Some("test"),
376 _ => None,
377 })
378 })
379 })
380 })
381 .flatten()
382 .collect::<HashSet<_>>();
383
384 let hidden_crate_name = "construct_runtime";
385 let scrate = generate_crate_access(hidden_crate_name, "frame-support");
386 let scrate_decl = generate_hidden_includes(hidden_crate_name, "frame-support");
387
388 let frame_system = generate_access_from_frame_or_crate("frame-system")?;
389 let block = quote!(<#name as #frame_system::Config>::Block);
390 let unchecked_extrinsic = quote!(<#block as #scrate::sp_runtime::traits::Block>::Extrinsic);
391
392 let outer_event =
393 expand::expand_outer_enum(&name, &pallets, &scrate, expand::OuterEnumType::Event)?;
394 let outer_error =
395 expand::expand_outer_enum(&name, &pallets, &scrate, expand::OuterEnumType::Error)?;
396
397 let outer_origin = expand::expand_outer_origin(&name, system_pallet, &pallets, &scrate)?;
398 let all_pallets = decl_all_pallets(&name, pallets.iter(), &features);
399 let pallet_to_index = decl_pallet_runtime_setup(&name, &pallets, &scrate);
400
401 let dispatch = expand::expand_outer_dispatch(&name, system_pallet, &pallets, &scrate);
402 let tasks = expand::expand_outer_task(&name, &pallets, &scrate);
403 let query = expand::expand_outer_query(&name, &pallets, &scrate);
404 let metadata = expand::expand_runtime_metadata(
405 &name,
406 &pallets,
407 &scrate,
408 &unchecked_extrinsic,
409 &system_pallet.path,
410 );
411 let outer_config = expand::expand_outer_config(&name, &pallets, &scrate);
412 let inherent =
413 expand::expand_outer_inherent(&name, &block, &unchecked_extrinsic, &pallets, &scrate);
414 let validate_unsigned = expand::expand_outer_validate_unsigned(&name, &pallets, &scrate);
415 let freeze_reason = expand::expand_outer_freeze_reason(&pallets, &scrate);
416 let hold_reason = expand::expand_outer_hold_reason(&pallets, &scrate);
417 let lock_id = expand::expand_outer_lock_id(&pallets, &scrate);
418 let slash_reason = expand::expand_outer_slash_reason(&pallets, &scrate);
419 let integrity_test = decl_integrity_test(&scrate);
420 let static_assertions = decl_static_assertions(&name, &pallets, &scrate);
421
422 let warning = where_section.map_or(None, |where_section| {
423 Some(
424 proc_macro_warning::Warning::new_deprecated("WhereSection")
425 .old("use a `where` clause in `construct_runtime`")
426 .new(
427 "use `frame_system::Config` to set the `Block` type and delete this clause.
428 It is planned to be removed in December 2023",
429 )
430 .help_links(&["https://github.com/paritytech/substrate/pull/14437"])
431 .span(where_section.span)
432 .build_or_panic(),
433 )
434 });
435
436 let res = quote!(
437 #warning
438
439 #scrate_decl
440
441 const _: () = {
443 #[allow(unused)]
444 type __hidden_use_of_unchecked_extrinsic = #unchecked_extrinsic;
445 };
446
447 #[derive(
448 Clone, Copy, PartialEq, Eq, #scrate::sp_runtime::RuntimeDebug,
449 #scrate::__private::scale_info::TypeInfo
450 )]
451 pub struct #name;
452 impl #scrate::sp_runtime::traits::GetRuntimeBlockType for #name {
453 type RuntimeBlock = #block;
454 }
455
456 #[doc(hidden)]
471 trait InternalConstructRuntime {
472 #[inline(always)]
473 fn runtime_metadata(&self) -> #scrate::__private::Vec<#scrate::__private::metadata_ir::RuntimeApiMetadataIR> {
474 Default::default()
475 }
476 }
477 #[doc(hidden)]
478 impl InternalConstructRuntime for &#name {}
479
480 use #scrate::__private::metadata_ir::InternalImplRuntimeApis;
481
482 #outer_event
483
484 #outer_error
485
486 #outer_origin
487
488 #all_pallets
489
490 #pallet_to_index
491
492 #dispatch
493
494 #tasks
495
496 #query
497
498 #metadata
499
500 #outer_config
501
502 #inherent
503
504 #validate_unsigned
505
506 #freeze_reason
507
508 #hold_reason
509
510 #lock_id
511
512 #slash_reason
513
514 #integrity_test
515
516 #static_assertions
517 );
518
519 Ok(res)
520}
521
522pub(crate) fn decl_all_pallets<'a>(
523 runtime: &'a Ident,
524 pallet_declarations: impl Iterator<Item = &'a Pallet>,
525 features: &HashSet<&str>,
526) -> TokenStream2 {
527 let mut types = TokenStream2::new();
528
529 let mut features_to_names = features
531 .iter()
532 .map(|f| *f)
533 .powerset()
534 .map(|feat| (HashSet::from_iter(feat), Vec::new()))
535 .collect::<Vec<(HashSet<_>, Vec<_>)>>();
536
537 for pallet_declaration in pallet_declarations {
538 let type_name = &pallet_declaration.name;
539 let pallet = &pallet_declaration.path;
540 let docs = &pallet_declaration.docs;
541 let mut generics = vec![quote!(#runtime)];
542 generics.extend(pallet_declaration.instance.iter().map(|name| quote!(#pallet::#name)));
543 let mut attrs = Vec::new();
544 for cfg in &pallet_declaration.cfg_pattern {
545 let feat = format!("#[cfg({})]\n", cfg.original());
546 attrs.extend(TokenStream2::from_str(&feat).expect("was parsed successfully; qed"));
547 }
548 let type_decl = quote!(
549 #( #[doc = #docs] )*
550 #(#attrs)*
551 pub type #type_name = #pallet::Pallet <#(#generics),*>;
552 );
553 types.extend(type_decl);
554
555 if pallet_declaration.cfg_pattern.is_empty() {
556 for (_, names) in features_to_names.iter_mut() {
557 names.push(&pallet_declaration.name);
558 }
559 } else {
560 for (feature_set, names) in &mut features_to_names {
561 let is_feature_active = pallet_declaration.cfg_pattern.iter().all(|expr| {
565 expr.eval(|pred| match pred {
566 Predicate::Feature(f) => feature_set.contains(f),
567 Predicate::Test => feature_set.contains(&"test"),
568 _ => false,
569 })
570 });
571
572 if is_feature_active {
573 names.push(&pallet_declaration.name);
574 }
575 }
576 }
577 }
578
579 let mut all_features = features_to_names
581 .iter()
582 .flat_map(|f| f.0.iter().cloned())
583 .collect::<HashSet<_>>();
584 let attribute_to_names = features_to_names
585 .into_iter()
586 .map(|(mut features, names)| {
587 if features.is_empty() {
591 let test_cfg = all_features.remove("test").then_some(quote!(test)).into_iter();
592 let features = all_features.iter();
593 let attr = quote!(#[cfg(all( #(not(#test_cfg)),* #(not(feature = #features)),* ))]);
594
595 (attr, names)
596 } else {
597 let test_cfg = features.remove("test").then_some(quote!(test)).into_iter();
598 let disabled_features = all_features.difference(&features);
599 let features = features.iter();
600 let attr = quote!(#[cfg(all( #(#test_cfg,)* #(feature = #features,)* #(not(feature = #disabled_features)),* ))]);
601
602 (attr, names)
603 }
604 })
605 .collect::<Vec<_>>();
606
607 let all_pallets_without_system = attribute_to_names.iter().map(|(attr, names)| {
608 let names = names.iter().filter(|n| **n != SYSTEM_PALLET_NAME);
609 quote! {
610 #attr
611 pub type AllPalletsWithoutSystem = ( #(#names,)* );
614 }
615 });
616
617 let all_pallets_with_system = attribute_to_names.iter().map(|(attr, names)| {
618 quote! {
619 #attr
620 pub type AllPalletsWithSystem = ( #(#names,)* );
622 }
623 });
624
625 quote!(
626 #types
627
628 #( #all_pallets_with_system )*
629
630 #( #all_pallets_without_system )*
631 )
632}
633
634pub(crate) fn decl_pallet_runtime_setup(
635 runtime: &Ident,
636 pallet_declarations: &[Pallet],
637 scrate: &TokenStream2,
638) -> TokenStream2 {
639 let names = pallet_declarations.iter().map(|d| &d.name).collect::<Vec<_>>();
640 let name_strings = pallet_declarations.iter().map(|d| d.name.to_string());
641 let name_hashes = pallet_declarations.iter().map(|d| two128_str(&d.name.to_string()));
642 let module_names = pallet_declarations.iter().map(|d| d.path.module_name());
643 let indices = pallet_declarations.iter().map(|pallet| pallet.index as usize);
644 let pallet_structs = pallet_declarations
645 .iter()
646 .map(|pallet| {
647 let path = &pallet.path;
648 match pallet.instance.as_ref() {
649 Some(inst) => quote!(#path::Pallet<#runtime, #path::#inst>),
650 None => quote!(#path::Pallet<#runtime>),
651 }
652 })
653 .collect::<Vec<_>>();
654 let pallet_attrs = pallet_declarations
655 .iter()
656 .map(|pallet| pallet.get_attributes())
657 .collect::<Vec<_>>();
658
659 quote!(
660 pub struct PalletInfo;
663
664 impl #scrate::traits::PalletInfo for PalletInfo {
665
666 fn index<P: 'static>() -> Option<usize> {
667 let type_id = core::any::TypeId::of::<P>();
668 #(
669 #pallet_attrs
670 if type_id == core::any::TypeId::of::<#names>() {
671 return Some(#indices)
672 }
673 )*
674
675 None
676 }
677
678 fn name<P: 'static>() -> Option<&'static str> {
679 let type_id = core::any::TypeId::of::<P>();
680 #(
681 #pallet_attrs
682 if type_id == core::any::TypeId::of::<#names>() {
683 return Some(#name_strings)
684 }
685 )*
686
687 None
688 }
689
690 fn name_hash<P: 'static>() -> Option<[u8; 16]> {
691 let type_id = core::any::TypeId::of::<P>();
692 #(
693 #pallet_attrs
694 if type_id == core::any::TypeId::of::<#names>() {
695 return Some(#name_hashes)
696 }
697 )*
698
699 None
700 }
701
702 fn module_name<P: 'static>() -> Option<&'static str> {
703 let type_id = core::any::TypeId::of::<P>();
704 #(
705 #pallet_attrs
706 if type_id == core::any::TypeId::of::<#names>() {
707 return Some(#module_names)
708 }
709 )*
710
711 None
712 }
713
714 fn crate_version<P: 'static>() -> Option<#scrate::traits::CrateVersion> {
715 let type_id = core::any::TypeId::of::<P>();
716 #(
717 #pallet_attrs
718 if type_id == core::any::TypeId::of::<#names>() {
719 return Some(
720 <#pallet_structs as #scrate::traits::PalletInfoAccess>::crate_version()
721 )
722 }
723 )*
724
725 None
726 }
727 }
728 )
729}
730
731pub(crate) fn decl_integrity_test(scrate: &TokenStream2) -> TokenStream2 {
732 quote!(
733 #[cfg(test)]
734 mod __construct_runtime_integrity_test {
735 use super::*;
736
737 #[test]
738 pub fn runtime_integrity_tests() {
739 #scrate::__private::sp_tracing::try_init_simple();
740 <AllPalletsWithSystem as #scrate::traits::IntegrityTest>::integrity_test();
741 }
742 }
743 )
744}
745
746pub(crate) fn decl_static_assertions(
747 runtime: &Ident,
748 pallet_decls: &[Pallet],
749 scrate: &TokenStream2,
750) -> TokenStream2 {
751 let error_encoded_size_check = pallet_decls.iter().map(|decl| {
752 let path = &decl.path;
753 let assert_message = format!(
754 "The maximum encoded size of the error type in the `{}` pallet exceeds \
755 `MAX_MODULE_ERROR_ENCODED_SIZE`",
756 decl.name,
757 );
758
759 quote! {
760 #[allow(deprecated)]
761 #scrate::__private::tt_call! {
762 macro = [{ #path::tt_error_token }]
763 your_tt_return = [{ #scrate::__private::tt_return }]
764 ~~> #scrate::assert_error_encoded_size! {
765 path = [{ #path }]
766 runtime = [{ #runtime }]
767 assert_message = [{ #assert_message }]
768 }
769 }
770 }
771 });
772
773 quote! {
774 #(#error_encoded_size_check)*
775 }
776}
777
778pub(crate) fn check_pallet_number(input: TokenStream2, pallet_num: usize) -> Result<()> {
779 let max_pallet_num = {
780 if cfg!(feature = "tuples-96") {
781 96
782 } else if cfg!(feature = "tuples-128") {
783 128
784 } else {
785 64
786 }
787 };
788
789 if pallet_num > max_pallet_num {
790 let no_feature = max_pallet_num == 128;
791 return Err(syn::Error::new(
792 input.span(),
793 format!(
794 "{} To increase this limit, enable the tuples-{} feature of [frame_support]. {}",
795 "The number of pallets exceeds the maximum number of tuple elements.",
796 max_pallet_num + 32,
797 if no_feature {
798 "If the feature does not exist - it needs to be implemented."
799 } else {
800 ""
801 },
802 ),
803 ))
804 }
805
806 Ok(())
807}