diff --git a/substrate/bin/node-template/runtime/src/lib.rs b/substrate/bin/node-template/runtime/src/lib.rs index 170874bfcb..525452256b 100644 --- a/substrate/bin/node-template/runtime/src/lib.rs +++ b/substrate/bin/node-template/runtime/src/lib.rs @@ -246,7 +246,7 @@ construct_runtime!( Timestamp: timestamp::{Module, Call, Storage, Inherent}, Aura: aura::{Module, Config, Inherent(Timestamp)}, Grandpa: grandpa::{Module, Call, Storage, Config, Event}, - Indices: indices::{default, Config}, + Indices: indices, Balances: balances::{default, Error}, TransactionPayment: transaction_payment::{Module, Storage}, Sudo: sudo, diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index c4752bae06..8e40573916 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -79,7 +79,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. spec_version: 195, - impl_version: 195, + impl_version: 196, apis: RUNTIME_API_VERSIONS, }; diff --git a/substrate/frame/support/procedural/src/construct_runtime/mod.rs b/substrate/frame/support/procedural/src/construct_runtime/mod.rs new file mode 100644 index 0000000000..a7401557e1 --- /dev/null +++ b/substrate/frame/support/procedural/src/construct_runtime/mod.rs @@ -0,0 +1,350 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +mod parse; + +use frame_support_procedural_tools::syn_ext as ext; +use frame_support_procedural_tools::{generate_crate_access, generate_hidden_includes}; +use parse::{ModuleDeclaration, RuntimeDefinition, WhereSection}; +use proc_macro::TokenStream; +use proc_macro2::{Span, TokenStream as TokenStream2}; +use quote::quote; +use syn::{Ident, Result}; + +pub fn construct_runtime(input: TokenStream) -> TokenStream { + let definition = syn::parse_macro_input!(input as RuntimeDefinition); + construct_runtime_parsed(definition) + .unwrap_or_else(|e| e.to_compile_error()) + .into() +} + +fn construct_runtime_parsed(definition: RuntimeDefinition) -> Result { + let RuntimeDefinition { + name, + where_section: WhereSection { + block, + node_block, + unchecked_extrinsic, + .. + }, + modules: + ext::Braces { + content: ext::Punctuated { inner: modules, .. }, + token: modules_token, + }, + .. + } = definition; + + // Assert we have system module declared + let system_module = match find_system_module(modules.iter()) { + Some(sm) => sm, + None => { + return Err(syn::Error::new( + modules_token.span, + "`System` module declaration is missing. \ + Please add this line: `System: system::{Module, Call, Storage, Config, Event},`", + )) + } + }; + let hidden_crate_name = "construct_runtime"; + let scrate = generate_crate_access(&hidden_crate_name, "frame-support"); + let scrate_decl = generate_hidden_includes(&hidden_crate_name, "frame-support"); + + let all_but_system_modules = modules.iter().filter(|module| module.name != "System"); + + let outer_event = decl_outer_event_or_origin( + &name, + all_but_system_modules.clone(), + &system_module, + &scrate, + DeclOuterKind::Event, + )?; + let outer_origin = decl_outer_event_or_origin( + &name, + all_but_system_modules.clone(), + &system_module, + &scrate, + DeclOuterKind::Origin, + )?; + let all_modules = decl_all_modules(&name, all_but_system_modules); + + let dispatch = decl_outer_dispatch(&name, modules.iter(), &scrate); + let metadata = decl_runtime_metadata(&name, modules.iter(), &scrate); + let outer_config = decl_outer_config(&name, modules.iter(), &scrate); + let inherent = decl_outer_inherent(&block, &unchecked_extrinsic, modules.iter(), &scrate); + let validate_unsigned = decl_validate_unsigned(&name, modules.iter(), &scrate); + + Ok(quote!( + #scrate_decl + + #[derive(Clone, Copy, PartialEq, Eq)] + #[cfg_attr(feature = "std", derive(Debug))] + pub struct #name; + impl #scrate::sr_primitives::traits::GetNodeBlockType for #name { + type NodeBlock = #node_block; + } + impl #scrate::sr_primitives::traits::GetRuntimeBlockType for #name { + type RuntimeBlock = #block; + } + + #outer_event + + #outer_origin + + #all_modules + + #dispatch + + #metadata + + #outer_config + + #inherent + + #validate_unsigned + ) + .into()) +} + +fn decl_validate_unsigned<'a>( + runtime: &'a Ident, + module_declarations: impl Iterator, + scrate: &'a TokenStream2, +) -> TokenStream2 { + let modules_tokens = module_declarations + .filter(|module_declaration| module_declaration.exists_part("ValidateUnsigned")) + .map(|module_declaration| &module_declaration.name); + quote!( + #scrate::impl_outer_validate_unsigned!( + impl ValidateUnsigned for #runtime { + #( #modules_tokens )* + } + ); + ) +} + +fn decl_outer_inherent<'a>( + block: &'a syn::TypePath, + unchecked_extrinsic: &'a syn::TypePath, + module_declarations: impl Iterator, + scrate: &'a TokenStream2, +) -> TokenStream2 { + let modules_tokens = module_declarations.filter_map(|module_declaration| { + let maybe_config_part = module_declaration.find_part("Inherent"); + maybe_config_part.map(|config_part| { + let arg = config_part + .args + .as_ref() + .and_then(|parens| parens.content.inner.iter().next()) + .unwrap_or(&module_declaration.name); + let name = &module_declaration.name; + quote!(#name : #arg,) + }) + }); + quote!( + #scrate::impl_outer_inherent!( + impl Inherents where Block = #block, UncheckedExtrinsic = #unchecked_extrinsic { + #(#modules_tokens)* + } + ); + ) +} + +fn decl_outer_config<'a>( + runtime: &'a Ident, + module_declarations: impl Iterator, + scrate: &'a TokenStream2, +) -> TokenStream2 { + let modules_tokens = module_declarations + .filter_map(|module_declaration| { + module_declaration.find_part("Config").map(|part| { + let transformed_generics: Vec<_> = part + .generics + .params + .iter() + .map(|param| quote!(<#param>)) + .collect(); + (module_declaration, transformed_generics) + }) + }) + .map(|(module_declaration, generics)| { + let module = &module_declaration.module; + let name = Ident::new( + &format!("{}Config", module_declaration.name), + module_declaration.name.span(), + ); + let instance = module_declaration.instance.as_ref().into_iter(); + quote!( + #name => + #module #(#instance)* #(#generics)*, + ) + }); + quote!( + #scrate::sr_primitives::impl_outer_config! { + pub struct GenesisConfig for #runtime { + #(#modules_tokens)* + } + } + ) +} + +fn decl_runtime_metadata<'a>( + runtime: &'a Ident, + module_declarations: impl Iterator, + scrate: &'a TokenStream2, +) -> TokenStream2 { + let modules_tokens = module_declarations + .filter_map(|module_declaration| { + module_declaration.find_part("Module").map(|_| { + let filtered_names: Vec<_> = module_declaration + .module_parts() + .into_iter() + .filter(|part| part.name != "Module") + .map(|part| part.name.clone()) + .collect(); + (module_declaration, filtered_names) + }) + }) + .map(|(module_declaration, filtered_names)| { + let module = &module_declaration.module; + let name = &module_declaration.name; + let instance = module_declaration + .instance + .as_ref() + .map(|name| quote!(<#name>)) + .into_iter(); + quote!(#module::Module #(#instance)* as #name with #(#filtered_names)* ,) + }); + quote!( + #scrate::impl_runtime_metadata!{ + for #runtime with modules + #(#modules_tokens)* + } + ) +} + +fn decl_outer_dispatch<'a>( + runtime: &'a Ident, + module_declarations: impl Iterator, + scrate: &'a TokenStream2, +) -> TokenStream2 { + let modules_tokens = module_declarations + .filter(|module_declaration| module_declaration.exists_part("Call")) + .map(|module_declaration| { + let module = &module_declaration.module; + let name = &module_declaration.name; + quote!(#module::#name) + }); + quote!( + #scrate::impl_outer_dispatch! { + pub enum Call for #runtime where origin: Origin { + #(#modules_tokens,)* + } + } + ) +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +enum DeclOuterKind { + Event, + Origin, +} + +fn decl_outer_event_or_origin<'a>( + runtime_name: &'a Ident, + module_declarations: impl Iterator, + system_name: &'a Ident, + scrate: &'a TokenStream2, + kind: DeclOuterKind, +) -> syn::Result { + let mut modules_tokens = TokenStream2::new(); + let kind_str = format!("{:?}", kind); + for module_declaration in module_declarations { + match module_declaration.find_part(&kind_str) { + Some(module_entry) => { + let module = &module_declaration.module; + let instance = module_declaration.instance.as_ref(); + let generics = &module_entry.generics; + if instance.is_some() && generics.params.len() == 0 { + let msg = format!( + "Instantiable module with no generic `{}` cannot \ + be constructed: module `{}` must have generic `{}`", + kind_str, module_declaration.name, kind_str + ); + return Err(syn::Error::new(module_declaration.name.span(), msg)); + } + let tokens = quote!(#module #instance #generics ,); + modules_tokens.extend(tokens); + } + None => {} + } + } + let macro_call = match kind { + DeclOuterKind::Event => quote!(#scrate::impl_outer_event!), + DeclOuterKind::Origin => quote!(#scrate::impl_outer_origin!), + }; + let enum_name = Ident::new(kind_str.as_str(), Span::call_site()); + Ok(quote!( + #macro_call { + pub enum #enum_name for #runtime_name where system = #system_name { + #modules_tokens + } + } + )) +} + +fn decl_all_modules<'a>( + runtime: &'a Ident, + module_declarations: impl Iterator, +) -> TokenStream2 { + let mut types = TokenStream2::new(); + let mut names = Vec::new(); + for module_declaration in module_declarations { + let type_name = &module_declaration.name; + let module = &module_declaration.module; + let mut generics = vec![quote!(#runtime)]; + generics.extend( + module_declaration + .instance + .iter() + .map(|name| quote!(#module::#name)), + ); + let type_decl = quote!( + pub type #type_name = #module::Module <#(#generics),*>; + ); + types.extend(type_decl); + names.push(&module_declaration.name); + } + // Make nested tuple structure like (((Babe, Consensus), Grandpa), ...) + let all_modules = names.iter().fold( + TokenStream2::default(), + |combined, name| quote!((#name, #combined)), + ); + + quote!( + pub type System = system::Module<#runtime>; + #types + type AllModules = ( #all_modules ); + ) +} + +fn find_system_module<'a>( + mut module_declarations: impl Iterator, +) -> Option<&'a Ident> { + module_declarations + .find(|decl| decl.name == "System") + .map(|decl| &decl.module) +} diff --git a/substrate/frame/support/procedural/src/construct_runtime/parse.rs b/substrate/frame/support/procedural/src/construct_runtime/parse.rs new file mode 100644 index 0000000000..b3f602824c --- /dev/null +++ b/substrate/frame/support/procedural/src/construct_runtime/parse.rs @@ -0,0 +1,401 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +use frame_support_procedural_tools::syn_ext as ext; +use proc_macro2::Span; +use std::collections::HashSet; +use syn::{ + parse::{Parse, ParseStream}, + spanned::Spanned, + token, Error, Ident, Result, Token, +}; + +mod keyword { + syn::custom_keyword!(Block); + syn::custom_keyword!(NodeBlock); + syn::custom_keyword!(UncheckedExtrinsic); +} + +#[derive(Debug)] +pub struct RuntimeDefinition { + pub visibility_token: Token![pub], + pub enum_token: Token![enum], + pub name: Ident, + pub where_section: WhereSection, + pub modules: ext::Braces>, +} + +impl Parse for RuntimeDefinition { + fn parse(input: ParseStream) -> Result { + Ok(Self { + visibility_token: input.parse()?, + enum_token: input.parse()?, + name: input.parse()?, + where_section: input.parse()?, + modules: input.parse()?, + }) + } +} + +#[derive(Debug)] +pub struct WhereSection { + pub block: syn::TypePath, + pub node_block: syn::TypePath, + pub unchecked_extrinsic: syn::TypePath, +} + +impl Parse for WhereSection { + fn parse(input: ParseStream) -> Result { + input.parse::()?; + let mut definitions = Vec::new(); + while !input.peek(token::Brace) { + let definition: WhereDefinition = input.parse()?; + definitions.push(definition); + if !input.peek(Token![,]) { + if !input.peek(token::Brace) { + return Err(input.error("Expected `,` or `{`")); + } + break; + } + input.parse::()?; + } + let block = remove_kind(input, WhereKind::Block, &mut definitions)?.value; + let node_block = remove_kind(input, WhereKind::NodeBlock, &mut definitions)?.value; + let unchecked_extrinsic = + remove_kind(input, WhereKind::UncheckedExtrinsic, &mut definitions)?.value; + if let Some(WhereDefinition { + ref kind_span, + ref kind, + .. + }) = definitions.first() + { + let msg = format!( + "`{:?}` was declared above. Please use exactly one delcataion for `{:?}`.", + kind, kind + ); + return Err(Error::new(*kind_span, msg)); + } + Ok(Self { + block, + node_block, + unchecked_extrinsic, + }) + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] +pub enum WhereKind { + Block, + NodeBlock, + UncheckedExtrinsic, +} + +#[derive(Debug)] +pub struct WhereDefinition { + pub kind_span: Span, + pub kind: WhereKind, + pub value: syn::TypePath, +} + +impl Parse for WhereDefinition { + fn parse(input: ParseStream) -> Result { + let lookahead = input.lookahead1(); + let (kind_span, kind) = if lookahead.peek(keyword::Block) { + (input.parse::()?.span(), WhereKind::Block) + } else if lookahead.peek(keyword::NodeBlock) { + ( + input.parse::()?.span(), + WhereKind::NodeBlock, + ) + } else if lookahead.peek(keyword::UncheckedExtrinsic) { + ( + input.parse::()?.span(), + WhereKind::UncheckedExtrinsic, + ) + } else { + return Err(lookahead.error()); + }; + + Ok(Self { + kind_span, + kind, + value: { + let _: Token![=] = input.parse()?; + input.parse()? + }, + }) + } +} + +#[derive(Debug)] +pub struct ModuleDeclaration { + pub name: Ident, + pub module: Ident, + pub instance: Option, + pub details: Option>>, +} + +impl Parse for ModuleDeclaration { + fn parse(input: ParseStream) -> Result { + let name = input.parse()?; + let _: Token![:] = input.parse()?; + let module = input.parse()?; + let instance = if input.peek(Token![::]) && input.peek3(Token![<]) { + let _: Token![::] = input.parse()?; + let _: Token![<] = input.parse()?; + let res = Some(input.parse()?); + let _: Token![>] = input.parse()?; + res + } else { + None + }; + let details = if input.peek(Token![::]) { + let _: Token![::] = input.parse()?; + Some(input.parse()?) + } else { + None + }; + let parsed = Self { + name, + module, + instance, + details, + }; + if let Some(ref details) = parsed.details { + let parts = &details.content.inner; + let mut resolved = HashSet::new(); + let has_default = parts.into_iter().any(|m| m.is_default()); + for entry in parts { + match entry { + ModuleEntry::Part(part) if has_default => { + if part.is_included_in_default() { + let msg = format!( + "`{}` is already included in `default`. Either remove `default` or remove `{}`", + part.name, + part.name + ); + return Err(Error::new(part.name.span(), msg)); + } + } + ModuleEntry::Part(part) => { + if !resolved.insert(part.name.clone()) { + let msg = format!( + "`{}` was already declared before. Please remove the duplicate declaration", + part.name + ); + return Err(Error::new(part.name.span(), msg)); + } + } + _ => {} + } + } + } + Ok(parsed) + } +} + +impl ModuleDeclaration { + /// Get resolved module parts, i.e. after expanding `default` keyword + /// or empty declaration + pub fn module_parts(&self) -> Vec { + if let Some(ref details) = self.details { + details + .content + .inner + .iter() + .flat_map(|entry| match entry { + ModuleEntry::Default(ref token) => Self::default_modules(token.span()), + ModuleEntry::Part(ref part) => vec![part.clone()], + }) + .collect() + } else { + Self::default_modules(self.module.span()) + } + } + + pub fn find_part(&self, name: &str) -> Option { + self.module_parts() + .into_iter() + .find(|part| part.name == name) + } + + pub fn exists_part(&self, name: &str) -> bool { + self.find_part(name).is_some() + } + + fn default_modules(span: Span) -> Vec { + let mut res: Vec<_> = ["Module", "Call", "Storage"] + .into_iter() + .map(|name| ModulePart::with_name(name, span)) + .collect(); + res.extend( + ["Event", "Config"] + .into_iter() + .map(|name| ModulePart::with_generics(name, span)), + ); + res + } +} + +#[derive(Debug)] +pub enum ModuleEntry { + Default(Token![default]), + Part(ModulePart), +} + +impl Parse for ModuleEntry { + fn parse(input: ParseStream) -> Result { + let lookahead = input.lookahead1(); + if lookahead.peek(Token![default]) { + Ok(ModuleEntry::Default(input.parse()?)) + } else if lookahead.peek(Ident) { + Ok(ModuleEntry::Part(input.parse()?)) + } else { + Err(lookahead.error()) + } + } +} + +impl ModuleEntry { + pub fn is_default(&self) -> bool { + match self { + ModuleEntry::Default(_) => true, + _ => false, + } + } +} + +#[derive(Debug, Clone)] +pub struct ModulePart { + pub name: Ident, + pub generics: syn::Generics, + pub args: Option>>, +} + +impl Parse for ModulePart { + fn parse(input: ParseStream) -> Result { + let name = input.parse()?; + let generics: syn::Generics = input.parse()?; + if !generics.params.is_empty() && !Self::is_allowed_generic(&name) { + let valid_generics = ModulePart::format_names(ModulePart::allowed_generics()); + let msg = format!( + "`{}` is not allowed to have generics. \ + Only the following modules are allowed to have generics: {}.", + name, valid_generics + ); + return Err(syn::Error::new(name.span(), msg)); + } + let args = if input.peek(token::Paren) { + if !Self::is_allowed_arg(&name) { + let syn::group::Parens { token: parens, .. } = syn::group::parse_parens(input)?; + let valid_names = ModulePart::format_names(ModulePart::allowed_args()); + let msg = format!( + "`{}` is not allowed to have arguments in parens. \ + Only the following modules are allowed to have arguments in parens: {}.", + name, valid_names + ); + return Err(syn::Error::new(parens.span, msg)); + } + Some(input.parse()?) + } else { + None + }; + Ok(Self { + name, + generics, + args, + }) + } +} + +impl ModulePart { + pub fn is_allowed_generic(ident: &Ident) -> bool { + Self::allowed_generics().into_iter().any(|n| ident == n) + } + + pub fn is_allowed_arg(ident: &Ident) -> bool { + Self::allowed_args().into_iter().any(|n| ident == n) + } + + pub fn allowed_generics() -> Vec<&'static str> { + vec!["Event", "Origin", "Config"] + } + + pub fn allowed_args() -> Vec<&'static str> { + vec!["Inherent"] + } + + pub fn format_names(names: Vec<&'static str>) -> String { + let res: Vec<_> = names.into_iter().map(|s| format!("`{}`", s)).collect(); + res.join(", ") + } + + pub fn is_included_in_default(&self) -> bool { + ["Module", "Call", "Storage", "Event", "Config"] + .into_iter() + .any(|name| self.name == name) + } + + /// Plain module name like `Event` or `Call`, etc. + pub fn with_name(name: &str, span: Span) -> Self { + let name = Ident::new(name, span); + Self { + name, + generics: syn::Generics { + lt_token: None, + gt_token: None, + where_clause: None, + ..Default::default() + }, + args: None, + } + } + + /// Module name with generic like `Event` or `Call`, etc. + pub fn with_generics(name: &str, span: Span) -> Self { + let name = Ident::new(name, span); + let typ = Ident::new("T", span); + let generic_param = syn::GenericParam::Type(typ.into()); + let generic_params = vec![generic_param].into_iter().collect(); + let generics = syn::Generics { + lt_token: Some(syn::token::Lt { spans: [span] }), + params: generic_params, + gt_token: Some(syn::token::Gt { spans: [span] }), + where_clause: None, + }; + Self { + name, + generics, + args: None, + } + } +} + +fn remove_kind( + input: ParseStream, + kind: WhereKind, + definitions: &mut Vec, +) -> Result { + if let Some(pos) = definitions.iter().position(|d| d.kind == kind) { + Ok(definitions.remove(pos)) + } else { + let msg = format!( + "Missing associated type for `{:?}`. Add `{:?}` = ... to where section.", + kind, kind + ); + Err(input.error(msg)) + } +} diff --git a/substrate/frame/support/procedural/src/lib.rs b/substrate/frame/support/procedural/src/lib.rs index 1832e870a3..8f23142bea 100644 --- a/substrate/frame/support/procedural/src/lib.rs +++ b/substrate/frame/support/procedural/src/lib.rs @@ -23,6 +23,7 @@ extern crate proc_macro; mod storage; +mod construct_runtime; use proc_macro::TokenStream; @@ -219,3 +220,63 @@ use proc_macro::TokenStream; pub fn decl_storage(input: TokenStream) -> TokenStream { storage::decl_storage_impl(input) } + +/// Construct a runtime, with the given name and the given modules. +/// +/// The parameters here are specific types for `Block`, `NodeBlock`, and `UncheckedExtrinsic` +/// and the modules that are used by the runtime. +/// `Block` is the block type that is used in the runtime and `NodeBlock` is the block type +/// that is used in the node. For instance they can differ in the extrinsics type. +/// +/// # Example: +/// +/// ```nocompile +/// construct_runtime!( +/// pub enum Runtime where +/// Block = Block, +/// NodeBlock = runtime::Block, +/// UncheckedExtrinsic = UncheckedExtrinsic +/// { +/// System: system, +/// Test: test::{default}, +/// Test2: test_with_long_module::{Module}, +/// +/// // Module with instances +/// Test3_Instance1: test3::::{Module, Call, Storage, Event, Config, Origin}, +/// Test3_DefaultInstance: test3::{Module, Call, Storage, Event, Config, Origin}, +/// } +/// ) +/// ``` +/// +/// The module `System: system` will expand to `System: system::{Module, Call, Storage, Event, Config}`. +/// The identifier `System` is the name of the module and the lower case identifier `system` is the +/// name of the Rust module/crate for this Substrate module. +/// +/// The module `Test: test::{default}` will expand to +/// `Test: test::{Module, Call, Storage, Event, Config}`. +/// +/// The module `Test2: test_with_long_module::{Module}` will expand to +/// `Test2: test_with_long_module::{Module}`. +/// +/// We provide support for the following types in a module: +/// +/// - `Module` +/// - `Call` +/// - `Storage` +/// - `Event` or `Event` (if the event is generic) +/// - `Origin` or `Origin` (if the origin is generic) +/// - `Config` or `Config` (if the config is generic) +/// - `Inherent ( $(CALL),* )` - If the module provides/can check inherents. The optional parameter +/// is for modules that use a `Call` from a different module as +/// inherent. +/// - `ValidateUnsigned` - If the module validates unsigned extrinsics. +/// +/// # Note +/// +/// The population of the genesis storage depends on the order of modules. So, if one of your +/// modules depends on another module, the module that is depended upon needs to come before +/// the module depending on it. +#[proc_macro] +pub fn construct_runtime(input: TokenStream) -> TokenStream { + construct_runtime::construct_runtime(input) +} diff --git a/substrate/frame/support/procedural/tools/src/syn_ext.rs b/substrate/frame/support/procedural/tools/src/syn_ext.rs index 18c1cd43ce..afcda4eebf 100644 --- a/substrate/frame/support/procedural/tools/src/syn_ext.rs +++ b/substrate/frame/support/procedural/tools/src/syn_ext.rs @@ -59,6 +59,11 @@ macro_rules! groups_impl { } } + impl Clone for $name

{ + fn clone(&self) -> Self { + Self { token: self.token.clone(), content: self.content.clone() } + } + } } } @@ -72,11 +77,11 @@ pub struct PunctuatedInner { pub variant: V, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct NoTrailing; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Trailing; pub type Punctuated = PunctuatedInner; @@ -107,6 +112,12 @@ impl ToTokens for PunctuatedInner { } } +impl Clone for PunctuatedInner { + fn clone(&self) -> Self { + Self { inner: self.inner.clone(), variant: self.variant.clone() } + } +} + /// Note that syn Meta is almost fine for use case (lacks only `ToToken`) #[derive(Debug, Clone)] pub struct Meta { @@ -178,6 +189,14 @@ impl ToTokens for Opt

{ } } +impl Clone for Opt

{ + fn clone(&self) -> Self { + Self { + inner: self.inner.clone() + } + } +} + pub fn extract_type_option(typ: &syn::Type) -> Option { if let syn::Type::Path(ref path) = typ { let v = path.path.segments.last()?; diff --git a/substrate/frame/support/src/lib.rs b/substrate/frame/support/src/lib.rs index e67120347a..916ffbdfa3 100644 --- a/substrate/frame/support/src/lib.rs +++ b/substrate/frame/support/src/lib.rs @@ -58,8 +58,6 @@ mod origin; #[macro_use] pub mod metadata; #[macro_use] -mod runtime; -#[macro_use] pub mod inherent; #[macro_use] pub mod unsigned; @@ -116,7 +114,7 @@ macro_rules! parameter_types { } #[doc(inline)] -pub use frame_support_procedural::decl_storage; +pub use frame_support_procedural::{decl_storage, construct_runtime}; /// Return Err of the expression: `return Err($expression);`. /// diff --git a/substrate/frame/support/src/runtime.rs b/substrate/frame/support/src/runtime.rs deleted file mode 100644 index 52bf48baa5..0000000000 --- a/substrate/frame/support/src/runtime.rs +++ /dev/null @@ -1,948 +0,0 @@ -// Copyright 2018-2019 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! Macros to define a runtime. A runtime is basically all your logic running in Substrate, -//! consisting of selected SRML modules and maybe some of your own modules. -//! A lot of supporting logic is automatically generated for a runtime, -//! mostly to combine data types and metadata of the included modules. - -/// Construct a runtime, with the given name and the given modules. -/// -/// The parameters here are specific types for `Block`, `NodeBlock`, and `InherentData` -/// and the modules that are used by the runtime. -/// `Block` is the block type that is used in the runtime and `NodeBlock` is the block type -/// that is used in the node. For instance they can differ in the extrinsics type. -/// -/// # Example: -/// -/// ```nocompile -/// construct_runtime!( -/// pub enum Runtime where -/// Block = Block, -/// NodeBlock = runtime::Block, -/// UncheckedExtrinsic = UncheckedExtrinsic -/// { -/// System: system, -/// Test: test::{default}, -/// Test2: test_with_long_module::{Module}, -/// -/// // Module with instances -/// Test3_Instance1: test3::::{Module, Call, Storage, Event, Config, Origin}, -/// Test3_DefaultInstance: test3::{Module, Call, Storage, Event, Config, Origin}, -/// } -/// ) -/// ``` -/// -/// The module `System: system` will expand to `System: system::{Module, Call, Storage, Event, Config}`. -/// The identifier `System` is the name of the module and the lower case identifier `system` is the -/// name of the Rust module/crate for this Substrate module. -/// -/// The module `Test: test::{default}` will expand to -/// `Test: test::{Module, Call, Storage, Event, Config}`. -/// -/// The module `Test2: test_with_long_module::{Module}` will expand to -/// `Test2: test_with_long_module::{Module}`. -/// -/// We provide support for the following types in a module: -/// -/// - `Module` -/// - `Call` -/// - `Storage` -/// - `Event` or `Event` (if the event is generic) -/// - `Origin` or `Origin` (if the origin is generic) -/// - `Config` or `Config` (if the config is generic) -/// - `Inherent $( (CALL) )*` - If the module provides/can check inherents. The optional parameter -/// is for modules that use a `Call` from a different module as -/// inherent. -/// - `ValidateUnsigned` - If the module validates unsigned extrinsics. -/// -/// # Note -/// -/// The population of the genesis storage depends on the order of modules. So, if one of your -/// modules depends on another module, the module that is depended upon needs to come before -/// the module depending on it. -#[macro_export] -macro_rules! construct_runtime { - - // Macro transformations (to convert invocations with incomplete parameters to the canonical - // form) - - ( - pub enum $runtime:ident - where - Block = $block:ident, - NodeBlock = $node_block:ty, - UncheckedExtrinsic = $uncheckedextrinsic:ident - { - $( $rest:tt )* - } - ) => { - $crate::construct_runtime!( - { - $runtime; - $block; - $node_block; - $uncheckedextrinsic; - }; - {}; - $( $rest )* - ); - }; - // No modules given, expand to the default module set. - ( - { $( $preset:tt )* }; - { $( $expanded:tt )* }; - $name:ident: $module:ident, - $( $rest:tt )* - ) => { - $crate::construct_runtime!( - { $( $preset )* }; - { $( $expanded )* }; - $name: $module::{default}, - $( $rest )* - ); - }; - // `default` identifier given, expand to default + given extra modules - ( - { $( $preset:tt )* }; - { $( $expanded:tt )* }; - $name:ident: $module:ident::{ - default - $(, - $modules:ident - $( <$modules_generic:ident> )* - $( ( $( $modules_args:ident ),* ) )* - )* - }, - $( $rest:tt )* - ) => { - $crate::construct_runtime!( - { $( $preset )* }; - { - $( $expanded )* - $name: $module::{ - Module, Call, Storage, Event, Config - $(, - $modules $( <$modules_generic> )* - $( ( $( $modules_args ),* ) )* - )* - }, - }; - $( $rest )* - ); - }; - // Take all modules as given by the user. - ( - { $( $preset:tt )* }; - { $( $expanded:tt )* }; - $name:ident: $module:ident :: $( < $module_instance:ident >:: )? { - $( - $modules:ident - $( <$modules_generic:ident> )* - $( ( $( $modules_args:ident ),* ) )* - ),* - }, - $( $rest:tt )* - ) => { - $crate::construct_runtime!( - { $( $preset )* }; - { - $( $expanded )* - $name: $module:: $( < $module_instance >:: )? { - $( - $modules $( <$modules_generic> )* - $( ( $( $modules_args ),* ) )* - ),* - }, - }; - $( $rest )* - ); - }; - // The main macro expansion that actually renders the Runtime code. - ( - { - $runtime:ident; - $block:ident; - $node_block:ty; - $uncheckedextrinsic:ident; - }; - { - $( - $name:ident: $module:ident :: $( < $module_instance:ident >:: )? { - $( - $modules:ident - $( <$modules_generic:ident> )* - $( ( $( $modules_args:ident ),* ) )* - ),* - }, - )* - }; - ) => { - #[derive(Clone, Copy, PartialEq, Eq, $crate::RuntimeDebug)] - pub struct $runtime; - impl $crate::sr_primitives::traits::GetNodeBlockType for $runtime { - type NodeBlock = $node_block; - } - impl $crate::sr_primitives::traits::GetRuntimeBlockType for $runtime { - type RuntimeBlock = $block; - } - $crate::__decl_outer_event!( - $runtime; - $( - $name: $module:: $( < $module_instance >:: )? { - $( $modules $( <$modules_generic> )* ),* - } - ),* - ); - $crate::__decl_outer_origin!( - $runtime; - $( - $name: $module:: $( < $module_instance >:: )? { - $( $modules $( <$modules_generic> )* ),* - } - ),* - ); - $crate::__decl_all_modules!( - $runtime; - ; - {}; - {}; - $( - $name: $module:: $( < $module_instance >:: )? { $( $modules ),* }, - )* - ); - $crate::__decl_outer_dispatch!( - $runtime; - ; - $( - $name: $module::{ $( $modules ),* } - ),*; - ); - $crate::__decl_runtime_metadata!( - $runtime; - {}; - $( - $name: $module:: $( < $module_instance >:: )? { $( $modules )* } - )* - ); - $crate::__decl_outer_config!( - $runtime; - {}; - $( - $name: $module:: $( < $module_instance >:: )? { - $( $modules $( <$modules_generic> )* ),* - }, - )* - ); - $crate::__decl_outer_inherent!( - $runtime; - $block; - $uncheckedextrinsic; - ; - $( - $name: $module::{ $( $modules $( ( $( $modules_args )* ) )* ),* } - ),*; - ); - $crate::__impl_outer_validate_unsigned!( - $runtime; - {}; - $( - $name: $module::{ $( $modules $( ( $( $modules_args )* ) )* )* } - )* - ); - } -} - -/// A macro that generates a "__decl" private macro that transforms parts of the runtime definition -/// to feed them into a public "impl" macro which accepts the format -/// "pub enum $name for $runtime where system = $system". -/// -/// Used to define Event and Origin associated types. -#[macro_export] -#[doc(hidden)] -macro_rules! __create_decl_macro { - ( - // Parameter $d is a hack for the following issue: - // https://github.com/rust-lang/rust/issues/35853 - $macro_name:ident, $macro_outer_name:ident, $macro_enum_name:ident, $d:tt - ) => { - #[macro_export] - #[doc(hidden)] - macro_rules! $macro_name { - ( - $runtime:ident; - $d( $name:ident : $module:ident:: $d( < $module_instance:ident >:: )? { - $d( $modules:ident $d( <$modules_generic:ident> ),* ),* - }),* - ) => { - $d crate::$macro_name!(@inner - $runtime; - ; - {}; - $d( - $name: $module:: $d( < $module_instance >:: )? { - $d( $modules $d( <$modules_generic> )* ),* - }, - )* - ); - }; - // Parse system module - (@inner - $runtime:ident; - ; // there can not be multiple `System`s - { $d( $parsed:tt )* }; - System: $module:ident::{ - $d( $modules:ident $d( <$modules_generic:ident> )* ),* - }, - $d( $rest:tt )* - ) => { - $d crate::$macro_name!(@inner - $runtime; - $module; - { $d( $parsed )* }; - $d( $rest )* - ); - }; - // Parse instantiable module with generic - (@inner - $runtime:ident; - $d( $system:ident )?; - { $d( $parsed:tt )* }; - $name:ident : $module:ident:: < $module_instance:ident >:: { - $macro_enum_name <$event_generic:ident> $d(, $ingore:ident $d( <$ignor:ident> )* )* - }, - $d( $rest:tt )* - ) => { - $d crate::$macro_name!(@inner - $runtime; - $d( $system )?; - { - $d( $parsed )* - $module $module_instance <$event_generic>, - }; - $d( $rest )* - ); - }; - // Parse instantiable module with no generic - (@inner - $runtime:ident; - $d( $system:ident )?; - { $d( $parsed:tt )* }; - $name:ident : $module:ident:: < $module_instance:ident >:: { - $macro_enum_name $d(, $ingore:ident $d( <$ignor:ident> )* )* - }, - $d( $rest:tt )* - ) => { - compile_error!(concat!( - "Instantiable module with not generic ", stringify!($macro_enum_name), - " cannot be constructed: module `", stringify!($name), "` must have generic ", - stringify!($macro_enum_name), "." - )); - }; - // Parse instantiable module with no generic - (@inner - $runtime:ident; - $d( $system:ident )?; - { $d( $parsed:tt )* }; - $name:ident : $module:ident:: { - $macro_enum_name $d( <$event_generic:ident> )* $d(, $ignore:ident $d( <$ignor:ident> )* )* - }, - $d( $rest:tt )* - ) => { - $d crate::$macro_name!(@inner - $runtime; - $d( $system )?; - { - $d( $parsed )* - $module $d( <$event_generic> )*, - }; - $d( $rest )* - ); - }; - // Ignore keyword - (@inner - $runtime:ident; - $d( $system:ident )?; - { $d( $parsed:tt )* }; - $name:ident : $module:ident:: $d( < $module_instance:ident >:: )? { - $ingore:ident $d( <$ignor:ident> )* $d(, $modules:ident $d( <$modules_generic:ident> )* )* - }, - $d( $rest:tt )* - ) => { - $d crate::$macro_name!(@inner - $runtime; - $d( $system )?; - { $d( $parsed )* }; - $name: $module:: $d( < $module_instance >:: )? { $d( $modules $d( <$modules_generic> )* ),* }, - $d( $rest )* - ); - }; - // Ignore module - (@inner - $runtime:ident; - $d( $system:ident )?; - { $d( $parsed:tt )* }; - $name:ident: $module:ident:: $d( < $module_instance:ident >:: )? {}, - $d( $rest:tt )* - ) => { - $d crate::$macro_name!(@inner - $runtime; - $d( $system )?; - { $d( $parsed )* }; - $d( $rest )* - ); - }; - // Expand - (@inner - $runtime:ident; - $system:ident; - { $d( $parsed_modules:ident $d( $instance:ident )? $d( <$parsed_generic:ident> )? ,)* }; - ) => { - $d crate::$macro_outer_name! { - pub enum $macro_enum_name for $runtime where system = $system { - $d( - $parsed_modules $d( $instance )? $d( <$parsed_generic> )?, - )* - } - } - } - } - } -} - -__create_decl_macro!(__decl_outer_event, impl_outer_event, Event, $); -__create_decl_macro!(__decl_outer_origin, impl_outer_origin, Origin, $); - -/// A macro that defines all modules as an associated types of the Runtime type. -#[macro_export] -#[doc(hidden)] -macro_rules! __decl_all_modules { - ( - $runtime:ident; - ; - { $( $parsed:tt )* }; - { $( $parsed_nested:tt )* }; - System: $module:ident::{ Module $(, $modules:ident )* }, - $( $rest:tt )* - ) => { - $crate::__decl_all_modules!( - $runtime; - $module; - { $( $parsed )* }; - { $( $parsed_nested )* }; - $( $rest )* - ); - }; - ( - $runtime:ident; - $( $system:ident )?; - { $( $parsed:tt )* }; - {}; - $name:ident: $module:ident:: $( < $module_instance:ident >:: )? { Module $(, $modules:ident )* }, - $( $rest:tt )* - ) => { - $crate::__decl_all_modules!( - $runtime; - $( $system )?; - { - $( $parsed )* - $module::$name $(<$module_instance>)?, - }; - { $name }; - $( $rest )* - ); - }; - ( - $runtime:ident; - $( $system:ident )?; - { $( $parsed:tt )* }; - { $( $parsed_nested:tt )* }; - $name:ident: $module:ident:: $( < $module_instance:ident >:: )? { Module $(, $modules:ident )* }, - $( $rest:tt )* - ) => { - $crate::__decl_all_modules!( - $runtime; - $( $system )?; - { - $( $parsed )* - $module::$name $(<$module_instance>)?, - }; - { ( $( $parsed_nested )*, $name, ) }; - $( $rest )* - ); - }; - ( - $runtime:ident; - $( $system:ident )?; - { $( $parsed:tt )* }; - { $( $parsed_nested:tt )* }; - $name:ident: $module:ident:: $( < $module_instance:ident >:: )? { $ignore:ident $(, $modules:ident )* }, - $( $rest:tt )* - ) => { - $crate::__decl_all_modules!( - $runtime; - $( $system )?; - { $( $parsed )* }; - { $( $parsed_nested )* }; - $name: $module::{ $( $modules ),* }, - $( $rest )* - ); - }; - ( - $runtime:ident; - $( $system:ident )?; - { $( $parsed:tt )* }; - $name:ident: $module:ident:: $( < $module_instance:ident >:: )? {}, - $( $rest:tt )* - ) => { - $crate::__decl_all_modules!( - $runtime; - $( $system )?; - { $( $parsed )* }; - { $( $parsed_nested )* }; - $( $rest )* - ); - }; - ( - $runtime:ident; - $system:ident; - { $( $parsed_module:ident :: $parsed_name:ident $(<$instance:ident>)? ,)*}; - { $( $parsed_nested:tt )* }; - ) => { - pub type System = system::Module<$runtime>; - $( - pub type $parsed_name = $parsed_module::Module<$runtime $(, $parsed_module::$instance )?>; - )* - type AllModules = ( $( $parsed_nested )* ); - } -} - -/// A macro that defines the Call enum to represent calls to functions in the modules included -/// in the runtime (by wrapping the values of all FooModule::Call enums). -#[macro_export] -#[doc(hidden)] -macro_rules! __decl_outer_dispatch { - ( - $runtime:ident; - $( $parsed_modules:ident :: $parsed_name:ident ),*; - $name:ident: $module:ident::{ - Call $(, $modules:ident $( <$modules_generic:ident> )* )* - } - $(, $rest_name:ident : $rest_module:ident::{ - $( $rest_modules:ident $( <$rest_modules_generic:ident> )* ),* - })*; - ) => { - $crate::__decl_outer_dispatch!( - $runtime; - $( $parsed_modules :: $parsed_name, )* $module::$name; - $( - $rest_name: $rest_module::{ - $( $rest_modules $( <$rest_modules_generic> )* ),* - } - ),*; - ); - }; - ( - $runtime:ident; - $( $parsed_modules:ident :: $parsed_name:ident ),*; - $name:ident: $module:ident::{ - $ignore:ident $( <$ignor:ident> )* $(, $modules:ident $( <$modules_generic:ident> )* )* - } - $(, $rest_name:ident : $rest_module:ident::{ - $( $rest_modules:ident $( <$rest_modules_generic:ident> )* ),* - })*; - ) => { - $crate::__decl_outer_dispatch!( - $runtime; - $( $parsed_modules :: $parsed_name ),*; - $name: $module::{ $( $modules $( <$modules_generic> )* ),* } - $( - , $rest_name: $rest_module::{ - $( $rest_modules $( <$rest_modules_generic> )* ),* - } - )*; - ); - }; - ( - $runtime:ident; - $( $parsed_modules:ident :: $parsed_name:ident ),*; - $name:ident: $module:ident::{} - $(, $rest_name:ident : $rest_module:ident::{ - $( $rest_modules:ident $( <$rest_modules_generic:ident> )* ),* - })*; - ) => { - $crate::__decl_outer_dispatch!( - $runtime; - $( $parsed_modules :: $parsed_name ),*; - $( - $rest_name: $rest_module::{ - $( $rest_modules $( <$rest_modules_generic> )* ),* - } - ),*; - ); - }; - ( - $runtime:ident; - $( $parsed_modules:ident :: $parsed_name:ident ),*; - ; - ) => { - $crate::impl_outer_dispatch!( - pub enum Call for $runtime where origin: Origin { - $( $parsed_modules::$parsed_name, )* - } - ); - }; -} - -/// A private macro that generates metadata() method for the runtime. See impl_runtime_metadata macro. -#[macro_export] -#[doc(hidden)] -macro_rules! __decl_runtime_metadata { - // leading is Module : parse - ( - $runtime:ident; - { $( $parsed:tt )* }; - $( { leading_module: $( $leading_module:ident )* } )? - $name:ident: $module:ident:: $( < $module_instance:ident >:: )? { - Module $( $modules:ident )* - } - $( $rest:tt )* - ) => { - $crate::__decl_runtime_metadata!( - $runtime; - { - $( $parsed )* - $module $( < $module_instance > )? as $name { - $( $( $leading_module )* )? $( $modules )* - } - }; - $( $rest )* - ); - }; - // leading isn't Module : put it in leadings - ( - $runtime:ident; - { $( $parsed:tt )* }; - $( { leading_module: $( $leading_module:ident )* } )? - $name:ident: $module:ident:: $( < $module_instance:ident >:: )? { - $other_module:ident $( $modules:ident )* - } - $( $rest:tt )* - ) => { - $crate::__decl_runtime_metadata!( - $runtime; - { $( $parsed )* }; - { leading_module: $( $( $leading_module )* )? $other_module } - $name: $module:: $( < $module_instance >:: )? { - $( $modules )* - } - $( $rest )* - ); - }; - // does not contain Module : skip - ( - $runtime:ident; - { $( $parsed:tt )* }; - $( { leading_module: $( $leading_module:ident )* } )? - $name:ident: $module:ident:: $( < $module_instance:ident >:: )? {} - $( $rest:tt )* - ) => { - $crate::__decl_runtime_metadata!( - $runtime; - { $( $parsed )* }; - $( $rest )* - ); - }; - // end of decl - ( - $runtime:ident; - { - $( - $parsed_modules:ident $( < $module_instance:ident > )? as $parsed_name:ident { - $( $withs:ident )* - } - )* - }; - ) => { - $crate::impl_runtime_metadata!( - for $runtime with modules - $( $parsed_modules::Module $( < $module_instance > )? as $parsed_name - with $( $withs )* , )* - ); - } -} - -/// A private macro that generates GenesisConfig for the runtime. See `impl_outer_config!` macro. -#[macro_export] -#[doc(hidden)] -macro_rules! __decl_outer_config { - ( - $runtime:ident; - { $( $parsed:tt )* }; - $name:ident: $module:ident:: $( < $module_instance:ident >:: )? { - Config $( <$config_generic:ident> )? - $(, $modules:ident $( <$modules_generic:ident> )* )* - }, - $( $rest:tt )* - ) => { - $crate::__decl_outer_config!( - $runtime; - { - $( $parsed )* - $module::$name $( $module_instance )? $( <$config_generic> )?, - }; - $( $rest )* - ); - }; - ( - $runtime:ident; - { $( $parsed:tt )* }; - $name:ident: $module:ident:: $( < $module_instance:ident >:: )? { - $ingore:ident $( <$ignore_gen:ident> )* - $(, $modules:ident $( <$modules_generic:ident> )* )* - }, - $( $rest:tt )* - ) => { - $crate::__decl_outer_config!( - $runtime; - { $( $parsed )* }; - $name: $module:: $( < $module_instance >:: )? { $( $modules $( <$modules_generic> )* ),* }, - $( $rest )* - ); - }; - ( - $runtime:ident; - { $( $parsed:tt )* }; - $name:ident: $module:ident:: $( < $module_instance:ident >:: )? {}, - $( $rest:tt )* - ) => { - $crate::__decl_outer_config!( - $runtime; - { $( $parsed )* }; - $( $rest )* - ); - }; - ( - $runtime:ident; - { - $( - $parsed_modules:ident :: $parsed_name:ident $( $parsed_instance:ident )? - $( - <$parsed_generic:ident> - )* - ,)* - }; - ) => { - $crate::paste::item! { - $crate::sr_primitives::impl_outer_config!( - pub struct GenesisConfig for $runtime { - $( - [< $parsed_name Config >] => - $parsed_modules $( $parsed_instance )? $( <$parsed_generic> )*, - )* - } - ); - } - }; -} - -/// A private macro that generates check_inherents() implementation for the runtime. -#[macro_export] -#[doc(hidden)] -macro_rules! __decl_outer_inherent { - ( - $runtime:ident; - $block:ident; - $uncheckedextrinsic:ident; - $( $parsed_name:ident :: $parsed_call:ident ),*; - $name:ident: $module:ident::{ - Inherent $(, $modules:ident $( ( $( $modules_call:ident )* ) )* )* - } - $(, $rest_name:ident : $rest_module:ident::{ - $( $rest_modules:ident $( ( $( $rest_call:ident )* ) )* ),* - })*; - ) => { - $crate::__decl_outer_inherent!( - $runtime; - $block; - $uncheckedextrinsic; - $( $parsed_name :: $parsed_call, )* $name::$name; - $( - $rest_name: $rest_module::{ - $( $rest_modules $( ( $( $rest_call )* ) )* ),* - } - ),*; - ); - }; - ( - $runtime:ident; - $block:ident; - $uncheckedextrinsic:ident; - $( $parsed_name:ident :: $parsed_call:ident ),*; - $name:ident: $module:ident::{ - Inherent ( $call:ident ) $(, $modules:ident $( ( $( $modules_call:ident )* ) )* )* - } - $(, $rest_name:ident : $rest_module:ident::{ - $( $rest_modules:ident $( ( $( $rest_call:ident )* ) )* ),* - })*; - ) => { - $crate::__decl_outer_inherent!( - $runtime; - $block; - $uncheckedextrinsic; - $( $parsed_name :: $parsed_call, )* $name::$call; - $( - $rest_name: $rest_module::{ - $( $rest_modules $( ( $( $rest_call )* ) )* ),* - } - ),*; - ); - }; - ( - $runtime:ident; - $block:ident; - $uncheckedextrinsic:ident; - $( $parsed_name:ident :: $parsed_call:ident ),*; - $name:ident: $module:ident::{ - $ignore:ident $( ( $( $ignor:ident )* ) )* - $(, $modules:ident $( ( $( $modules_call:ident )* ) )* )* - } - $(, $rest_name:ident : $rest_module:ident::{ - $( $rest_modules:ident $( ( $( $rest_call:ident )* ) )* ),* - })*; - ) => { - $crate::__decl_outer_inherent!( - $runtime; - $block; - $uncheckedextrinsic; - $( $parsed_name :: $parsed_call ),*; - $name: $module::{ $( $modules $( ( $( $modules_call )* ) )* ),* } - $( - , $rest_name: $rest_module::{ - $( $rest_modules $( ( $( $rest_call )* ) )* ),* - } - )*; - ); - }; - ( - $runtime:ident; - $block:ident; - $uncheckedextrinsic:ident; - $( $parsed_name:ident :: $parsed_call:ident ),*; - $name:ident: $module:ident::{} - $(, $rest_name:ident : $rest_module:ident::{ - $( $rest_modules:ident $( ( $( $rest_call:ident )* ) )* ),* - })*; - ) => { - $crate::__decl_outer_inherent!( - $runtime; - $block; - $uncheckedextrinsic; - $( $parsed_name :: $parsed_call ),*; - $( - $rest_name: $rest_module::{ - $( $rest_modules $( ( $( $rest_call )* ) )* ),* - } - ),*; - ); - }; - ( - $runtime:ident; - $block:ident; - $uncheckedextrinsic:ident; - $( $parsed_name:ident :: $parsed_call:ident ),*; - ; - ) => { - $crate::impl_outer_inherent!( - impl Inherents where Block = $block, UncheckedExtrinsic = $uncheckedextrinsic { - $( $parsed_name : $parsed_call, )* - } - ); - }; -} - -#[macro_export] -#[doc(hidden)] -// Those imports are used by event, config, origin and log macros to get access to its inner type -macro_rules! __decl_instance_import { - ( $( $module:ident <$instance:ident> )* ) => { - $crate::paste::item! { - $(use $module as [< $module _ $instance >];)* - } - }; -} - -/// A private macro that calls impl_outer_validate_unsigned for Call. -#[macro_export] -#[doc(hidden)] -macro_rules! __impl_outer_validate_unsigned { - ( - $runtime:ident; - { $( $parsed:tt )* }; - $name:ident: $module:ident:: $(<$module_instance:ident>::)? { - ValidateUnsigned $( $modules:ident $( ( $( $modules_args:ident )* ) )* )* - } - $( $rest:tt )* - ) => { - $crate::__impl_outer_validate_unsigned!( - $runtime; - { $( $parsed )* $name }; - $( $rest )* - ); - }; - ( - $runtime:ident; - { $( $parsed:tt )* }; - $name:ident: $module:ident:: $(<$module_instance:ident>::)? { - $ignore:ident $( ( $( $args_ignore:ident )* ) )* - $( $modules:ident $( ( $( $modules_args:ident )* ) )* )* - } - $( $rest:tt )* - ) => { - $crate::__impl_outer_validate_unsigned!( - $runtime; - { $( $parsed )* }; - $name: $module:: $(<$module_instance>::)? { - $( $modules $( ( $( $modules_args )* ) )* )* - } - $( $rest )* - ); - }; - ( - $runtime:ident; - { $( $parsed:tt )* }; - $name:ident: $module:ident:: $(<$module_instance:ident>::)? {} - $( $rest:tt )* - ) => { - $crate::__impl_outer_validate_unsigned!( - $runtime; - { $( $parsed )* }; - $( $rest )* - ); - }; - ( - $runtime:ident; - { $( - $parsed_modules:ident - )* }; - ) => { - $crate::impl_outer_validate_unsigned!( - impl ValidateUnsigned for $runtime { - $( $parsed_modules )* - } - ); - }; -} diff --git a/substrate/frame/support/test/tests/construct_runtime_ui.rs b/substrate/frame/support/test/tests/construct_runtime_ui.rs new file mode 100644 index 0000000000..acddf01f03 --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui.rs @@ -0,0 +1,10 @@ +use std::env; + +#[test] +fn ui() { + // As trybuild is using `cargo check`, we don't need the real WASM binaries. + env::set_var("BUILD_DUMMY_WASM_BINARY", "1"); + + let t = trybuild::TestCases::new(); + t.compile_fail("tests/construct_runtime_ui/*.rs"); +} diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/abundant_where_param.rs b/substrate/frame/support/test/tests/construct_runtime_ui/abundant_where_param.rs new file mode 100644 index 0000000000..4d5b40d7c9 --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/abundant_where_param.rs @@ -0,0 +1,12 @@ +use support::construct_runtime; + +construct_runtime! { + pub enum Runtime where + Block = Block, + NodeBlock = Block, + Block = Block1, + UncheckedExtrinsic = Uxt, + {} +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/abundant_where_param.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/abundant_where_param.stderr new file mode 100644 index 0000000000..4bac57d63c --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/abundant_where_param.stderr @@ -0,0 +1,5 @@ +error: `Block` was declared above. Please use exactly one delcataion for `Block`. + --> $DIR/abundant_where_param.rs:7:3 + | +7 | Block = Block1, + | ^^^^^ diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/double_module_parts.rs b/substrate/frame/support/test/tests/construct_runtime_ui/double_module_parts.rs new file mode 100644 index 0000000000..b741b316e1 --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/double_module_parts.rs @@ -0,0 +1,14 @@ +use support::construct_runtime; + +construct_runtime! { + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + System: system, + Balance: balances::{Config, Call, Config, Origin}, + } +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/double_module_parts.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/double_module_parts.stderr new file mode 100644 index 0000000000..9d10474ce8 --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/double_module_parts.stderr @@ -0,0 +1,5 @@ +error: `Config` was already declared before. Please remove the duplicate declaration + --> $DIR/double_module_parts.rs:10:37 + | +10 | Balance: balances::{Config, Call, Config, Origin}, + | ^^^^^^ diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/double_module_parts_default.rs b/substrate/frame/support/test/tests/construct_runtime_ui/double_module_parts_default.rs new file mode 100644 index 0000000000..d259389460 --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/double_module_parts_default.rs @@ -0,0 +1,14 @@ +use support::construct_runtime; + +construct_runtime! { + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + System: system, + Balance: balances::{default, Config}, + } +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/double_module_parts_default.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/double_module_parts_default.stderr new file mode 100644 index 0000000000..d8205acc4a --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/double_module_parts_default.stderr @@ -0,0 +1,5 @@ +error: `Config` is already included in `default`. Either remove `default` or remove `Config` + --> $DIR/double_module_parts_default.rs:10:32 + | +10 | Balance: balances::{default, Config}, + | ^^^^^^ diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/generics_in_invalid_module.rs b/substrate/frame/support/test/tests/construct_runtime_ui/generics_in_invalid_module.rs new file mode 100644 index 0000000000..16e3696195 --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/generics_in_invalid_module.rs @@ -0,0 +1,14 @@ +use support::construct_runtime; + +construct_runtime! { + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + System: system, + Balance: balances::::{Call, Origin}, + } +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/generics_in_invalid_module.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/generics_in_invalid_module.stderr new file mode 100644 index 0000000000..fe88054921 --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/generics_in_invalid_module.stderr @@ -0,0 +1,5 @@ +error: `Call` is not allowed to have generics. Only the following modules are allowed to have generics: `Event`, `Origin`, `Config`. + --> $DIR/generics_in_invalid_module.rs:10:36 + | +10 | Balance: balances::::{Call, Origin}, + | ^^^^ diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/invalid_module_details.rs b/substrate/frame/support/test/tests/construct_runtime_ui/invalid_module_details.rs new file mode 100644 index 0000000000..bfaeca6a42 --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/invalid_module_details.rs @@ -0,0 +1,13 @@ +use support::construct_runtime; + +construct_runtime! { + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + system: System::(), + } +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/invalid_module_details.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/invalid_module_details.stderr new file mode 100644 index 0000000000..559a4637d6 --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/invalid_module_details.stderr @@ -0,0 +1,5 @@ +error: expected curly braces + --> $DIR/invalid_module_details.rs:9:19 + | +9 | system: System::(), + | ^^ diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/invalid_module_details_keyword.rs b/substrate/frame/support/test/tests/construct_runtime_ui/invalid_module_details_keyword.rs new file mode 100644 index 0000000000..87e802f4bc --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/invalid_module_details_keyword.rs @@ -0,0 +1,13 @@ +use support::construct_runtime; + +construct_runtime! { + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + system: System::{enum}, + } +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/invalid_module_details_keyword.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/invalid_module_details_keyword.stderr new file mode 100644 index 0000000000..4441f6dc0f --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/invalid_module_details_keyword.stderr @@ -0,0 +1,5 @@ +error: expected `default` or identifier + --> $DIR/invalid_module_details_keyword.rs:9:20 + | +9 | system: System::{enum}, + | ^^^^ diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/invalid_token_after_module.rs b/substrate/frame/support/test/tests/construct_runtime_ui/invalid_token_after_module.rs new file mode 100644 index 0000000000..d7307c2548 --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/invalid_token_after_module.rs @@ -0,0 +1,13 @@ +use support::construct_runtime; + +construct_runtime! { + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + system: System ? + } +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/invalid_token_after_module.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/invalid_token_after_module.stderr new file mode 100644 index 0000000000..8a6f15f791 --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/invalid_token_after_module.stderr @@ -0,0 +1,5 @@ +error: expected `,` + --> $DIR/invalid_token_after_module.rs:9:18 + | +9 | system: System ? + | ^ diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/invalid_token_after_name.rs b/substrate/frame/support/test/tests/construct_runtime_ui/invalid_token_after_name.rs new file mode 100644 index 0000000000..f0fb296c73 --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/invalid_token_after_name.rs @@ -0,0 +1,13 @@ +use support::construct_runtime; + +construct_runtime! { + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + system ? + } +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/invalid_token_after_name.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/invalid_token_after_name.stderr new file mode 100644 index 0000000000..eaae082c84 --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/invalid_token_after_name.stderr @@ -0,0 +1,5 @@ +error: expected `:` + --> $DIR/invalid_token_after_name.rs:9:10 + | +9 | system ? + | ^ diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/invalid_where_param.rs b/substrate/frame/support/test/tests/construct_runtime_ui/invalid_where_param.rs new file mode 100644 index 0000000000..000af42715 --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/invalid_where_param.rs @@ -0,0 +1,12 @@ +use support::construct_runtime; + +construct_runtime! { + pub enum Runtime where + Block = Block, + NodeBlock = Block, + TypeX = Block, + UnchekcedExtrinsic = UnchekcedExtrinsic, + {} +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/invalid_where_param.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/invalid_where_param.stderr new file mode 100644 index 0000000000..9e358b6a21 --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/invalid_where_param.stderr @@ -0,0 +1,5 @@ +error: expected one of: `Block`, `NodeBlock`, `UncheckedExtrinsic` + --> $DIR/invalid_where_param.rs:7:3 + | +7 | TypeX = Block, + | ^^^^^ diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/missing_event_generic_on_module_with_instance.rs b/substrate/frame/support/test/tests/construct_runtime_ui/missing_event_generic_on_module_with_instance.rs new file mode 100644 index 0000000000..224ec65ee3 --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/missing_event_generic_on_module_with_instance.rs @@ -0,0 +1,14 @@ +use support::construct_runtime; + +construct_runtime! { + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + System: system, + Balance: balances::::{Event}, + } +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/missing_event_generic_on_module_with_instance.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/missing_event_generic_on_module_with_instance.stderr new file mode 100644 index 0000000000..f80b4bd66a --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/missing_event_generic_on_module_with_instance.stderr @@ -0,0 +1,5 @@ +error: Instantiable module with no generic `Event` cannot be constructed: module `Balance` must have generic `Event` + --> $DIR/missing_event_generic_on_module_with_instance.rs:10:3 + | +10 | Balance: balances::::{Event}, + | ^^^^^^^ diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/missing_module_instance.rs b/substrate/frame/support/test/tests/construct_runtime_ui/missing_module_instance.rs new file mode 100644 index 0000000000..9171827d16 --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/missing_module_instance.rs @@ -0,0 +1,13 @@ +use support::construct_runtime; + +construct_runtime! { + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + system: System::<>, + } +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/missing_module_instance.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/missing_module_instance.stderr new file mode 100644 index 0000000000..6303c74e42 --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/missing_module_instance.stderr @@ -0,0 +1,5 @@ +error: expected identifier + --> $DIR/missing_module_instance.rs:9:20 + | +9 | system: System::<>, + | ^ diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/missing_origin_generic_on_module_with_instance.rs b/substrate/frame/support/test/tests/construct_runtime_ui/missing_origin_generic_on_module_with_instance.rs new file mode 100644 index 0000000000..6eadbbf3e0 --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/missing_origin_generic_on_module_with_instance.rs @@ -0,0 +1,14 @@ +use support::construct_runtime; + +construct_runtime! { + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + System: system, + Balance: balances::::{Origin}, + } +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/missing_origin_generic_on_module_with_instance.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/missing_origin_generic_on_module_with_instance.stderr new file mode 100644 index 0000000000..0f7d36aafb --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/missing_origin_generic_on_module_with_instance.stderr @@ -0,0 +1,5 @@ +error: Instantiable module with no generic `Origin` cannot be constructed: module `Balance` must have generic `Origin` + --> $DIR/missing_origin_generic_on_module_with_instance.rs:10:3 + | +10 | Balance: balances::::{Origin}, + | ^^^^^^^ diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/missing_system_module.rs b/substrate/frame/support/test/tests/construct_runtime_ui/missing_system_module.rs new file mode 100644 index 0000000000..409ee2b8ba --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/missing_system_module.rs @@ -0,0 +1,12 @@ +use support::construct_runtime; + +construct_runtime! { + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + } +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/missing_system_module.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/missing_system_module.stderr new file mode 100644 index 0000000000..5964a8aa76 --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/missing_system_module.stderr @@ -0,0 +1,7 @@ +error: `System` module declaration is missing. Please add this line: `System: system::{Module, Call, Storage, Config, Event},` + --> $DIR/missing_system_module.rs:8:2 + | +8 | { + | _____^ +9 | | } + | |_____^ diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/missing_where_block.rs b/substrate/frame/support/test/tests/construct_runtime_ui/missing_where_block.rs new file mode 100644 index 0000000000..8d43c58140 --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/missing_where_block.rs @@ -0,0 +1,7 @@ +use support::construct_runtime; + +construct_runtime! { + pub enum Runtime {} +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/missing_where_block.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/missing_where_block.stderr new file mode 100644 index 0000000000..4af672a261 --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/missing_where_block.stderr @@ -0,0 +1,5 @@ +error: expected `where` + --> $DIR/missing_where_block.rs:4:19 + | +4 | pub enum Runtime {} + | ^^ diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/missing_where_param.rs b/substrate/frame/support/test/tests/construct_runtime_ui/missing_where_param.rs new file mode 100644 index 0000000000..7ec6409124 --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/missing_where_param.rs @@ -0,0 +1,10 @@ +use support::construct_runtime; + +construct_runtime! { + pub enum Runtime where + Block = Block, + NodeBlock = Block, + {} +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/missing_where_param.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/missing_where_param.stderr new file mode 100644 index 0000000000..ac7313523c --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/missing_where_param.stderr @@ -0,0 +1,5 @@ +error: Missing associated type for `UncheckedExtrinsic`. Add `UncheckedExtrinsic` = ... to where section. + --> $DIR/missing_where_param.rs:7:2 + | +7 | {} + | ^^ diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/no_comma_after_where.rs b/substrate/frame/support/test/tests/construct_runtime_ui/no_comma_after_where.rs new file mode 100644 index 0000000000..096d5fd376 --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/no_comma_after_where.rs @@ -0,0 +1,13 @@ +use support::construct_runtime; + +construct_runtime! { + pub enum Runtime where + UncheckedExtrinsic = UncheckedExtrinsic + Block = Block, + NodeBlock = Block, + { + System: system, + } +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/no_comma_after_where.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/no_comma_after_where.stderr new file mode 100644 index 0000000000..caf4a7401b --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/no_comma_after_where.stderr @@ -0,0 +1,5 @@ +error: Expected `,` or `{` + --> $DIR/no_comma_after_where.rs:6:3 + | +6 | Block = Block, + | ^^^^^ diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/params_in_invalid_module.rs b/substrate/frame/support/test/tests/construct_runtime_ui/params_in_invalid_module.rs new file mode 100644 index 0000000000..f493371bb3 --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/params_in_invalid_module.rs @@ -0,0 +1,14 @@ +use support::construct_runtime; + +construct_runtime! { + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + System: system, + Balance: balances::::{Call(toto), Origin}, + } +} + +fn main() {} diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/params_in_invalid_module.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/params_in_invalid_module.stderr new file mode 100644 index 0000000000..58f35720e3 --- /dev/null +++ b/substrate/frame/support/test/tests/construct_runtime_ui/params_in_invalid_module.stderr @@ -0,0 +1,5 @@ +error: `Call` is not allowed to have arguments in parens. Only the following modules are allowed to have arguments in parens: `Inherent`. + --> $DIR/params_in_invalid_module.rs:10:40 + | +10 | Balance: balances::::{Call(toto), Origin}, + | ^^^^^^