From 13c7adabeacb9540e8556f121a36ba0946e99eea Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 16 Sep 2024 12:24:07 -0400 Subject: [PATCH 01/53] add pallet index lint --- support/linting/src/lib.rs | 2 + support/linting/src/pallet_index.rs | 128 ++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 support/linting/src/pallet_index.rs diff --git a/support/linting/src/lib.rs b/support/linting/src/lib.rs index d02a70a2b..e5416c1d5 100644 --- a/support/linting/src/lib.rs +++ b/support/linting/src/lib.rs @@ -1,6 +1,8 @@ pub mod lint; pub use lint::*; +mod pallet_index; mod require_freeze_struct; +pub use pallet_index::RequireExplicitPalletIndex; pub use require_freeze_struct::RequireFreezeStruct; diff --git a/support/linting/src/pallet_index.rs b/support/linting/src/pallet_index.rs new file mode 100644 index 000000000..2b80f7d57 --- /dev/null +++ b/support/linting/src/pallet_index.rs @@ -0,0 +1,128 @@ +use super::*; +use syn::parse::{Parse, ParseStream}; +use syn::punctuated::Punctuated; +use syn::token::Colon; +use syn::visit::Visit; +use syn::{File, ItemMacro, Token}; + +pub struct RequireExplicitPalletIndex; + +impl Lint for RequireExplicitPalletIndex { + fn lint(source: &File) -> Result { + let mut visitor = ConstructRuntimeVisitor::default(); + + visitor.visit_file(source); + + if !visitor.errors.is_empty() { + return Err(visitor.errors); + } + + Ok(()) + } +} + +#[derive(Default)] +struct ConstructRuntimeVisitor { + errors: Vec, +} + +impl<'ast> Visit<'ast> for ConstructRuntimeVisitor { + fn visit_item_macro(&mut self, node: &'ast ItemMacro) { + if node.mac.path.is_ident("construct_runtime") { + let tokens = node.mac.tokens.clone(); + if let Ok(runtime_entries) = syn::parse2::(tokens) { + for entry in runtime_entries.entries { + if entry.index.is_none() { + self.errors.push(syn::Error::new( + entry.pallet_name.span(), + format!( + "Pallet `{}` does not have an explicit index in construct_runtime!", + entry.pallet_name + ), + )); + } + } + } + } + + syn::visit::visit_item_macro(self, node); + } +} + +struct ConstructRuntimeEntries { + entries: Punctuated, +} + +impl Parse for ConstructRuntimeEntries { + fn parse(input: ParseStream) -> syn::Result { + Ok(ConstructRuntimeEntries { + entries: input.parse_terminated(PalletEntry::parse, Token![,])?, + }) + } +} + +struct PalletEntry { + pallet_name: syn::Ident, + index: Option, +} + +impl Parse for PalletEntry { + fn parse(input: ParseStream) -> syn::Result { + let pallet_name: syn::Ident = input.parse()?; + let index = if input.peek(Colon) { + input.parse::()?; + Some(input.parse::()?) + } else { + None + }; + Ok(PalletEntry { pallet_name, index }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn lint_macro(input: &str) -> Result { + let item_macro: ItemMacro = syn::parse_str(input).expect("should only use on a macro"); + let mut visitor = ConstructRuntimeVisitor::default(); + visitor.visit_item_macro(&item_macro); + if !visitor.errors.is_empty() { + return Err(visitor.errors); + } + Ok(()) + } + + #[test] + fn test_no_pallet_index() { + let input = r#" + construct_runtime!( + PalletA, + PalletB + ); + "#; + assert!(lint_macro(input).is_err()); + } + + #[test] + fn test_with_pallet_index() { + let input = r#" + construct_runtime!( + PalletA: 0, + PalletB: 1 + ); + "#; + assert!(lint_macro(input).is_ok()); + } + + #[test] + fn test_mixed_pallet_index() { + let input = r#" + construct_runtime!( + PalletA, + PalletB: 1 + ); + "#; + assert!(lint_macro(input).is_err()); + } +} From 3fd76968e99d2c87b6ada99923c70f253fd506d6 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Tue, 17 Sep 2024 01:12:22 -0400 Subject: [PATCH 02/53] WIP --- build.rs | 1 + support/linting/src/pallet_index.rs | 116 ++++++++++++++++++++++++---- 2 files changed, 100 insertions(+), 17 deletions(-) diff --git a/build.rs b/build.rs index 4739a8aca..85388fd6c 100644 --- a/build.rs +++ b/build.rs @@ -60,6 +60,7 @@ fn main() { }; track_lint(RequireFreezeStruct::lint(&parsed_file)); + track_lint(RequireExplicitPalletIndex::lint(&parsed_file)); }); // Collect and print all errors after the parallel processing is done diff --git a/support/linting/src/pallet_index.rs b/support/linting/src/pallet_index.rs index 2b80f7d57..6e0633d51 100644 --- a/support/linting/src/pallet_index.rs +++ b/support/linting/src/pallet_index.rs @@ -1,9 +1,12 @@ use super::*; +use quote::ToTokens; +use syn::braced; use syn::parse::{Parse, ParseStream}; use syn::punctuated::Punctuated; +use syn::spanned::Spanned; use syn::token::Colon; use syn::visit::Visit; -use syn::{File, ItemMacro, Token}; +use syn::{File, Ident, ItemMacro, Path, Token, Visibility}; pub struct RequireExplicitPalletIndex; @@ -29,22 +32,24 @@ struct ConstructRuntimeVisitor { impl<'ast> Visit<'ast> for ConstructRuntimeVisitor { fn visit_item_macro(&mut self, node: &'ast ItemMacro) { if node.mac.path.is_ident("construct_runtime") { + // Token stream parsing logic let tokens = node.mac.tokens.clone(); - if let Ok(runtime_entries) = syn::parse2::(tokens) { - for entry in runtime_entries.entries { - if entry.index.is_none() { - self.errors.push(syn::Error::new( - entry.pallet_name.span(), - format!( - "Pallet `{}` does not have an explicit index in construct_runtime!", - entry.pallet_name - ), - )); - } + let runtime_entries = syn::parse2::(tokens).unwrap(); + for entry in runtime_entries.entries { + // Check if the entry is missing an explicit index + if entry.index.is_none() { + self.errors.push(syn::Error::new( + entry.pallet_name.span(), + format!( + "Pallet `{}` does not have an explicit index in construct_runtime!", + entry.pallet_name.to_token_stream().to_string().trim() + ), + )); } } } + // Continue visiting the rest of the file syn::visit::visit_item_macro(self, node); } } @@ -62,20 +67,64 @@ impl Parse for ConstructRuntimeEntries { } struct PalletEntry { - pallet_name: syn::Ident, - index: Option, + visibility: Option, + pallet_name: Path, + components: Option, + index: Option, // Now index can be None (i.e., missing) } impl Parse for PalletEntry { fn parse(input: ParseStream) -> syn::Result { - let pallet_name: syn::Ident = input.parse()?; + // Optionally parse visibility (e.g., `pub`) + let visibility: Option = input.parse().ok(); + + // Parse the pallet name (with possible generics and paths like `pallet_collective::::{ Pallet, Call, Storage }`) + let pallet_name = parse_complex_pallet_path(input)?; + + // Optionally parse the index if it's present let index = if input.peek(Colon) { input.parse::()?; Some(input.parse::()?) } else { - None + None // Missing index is allowed during parsing }; - Ok(PalletEntry { pallet_name, index }) + + Ok(PalletEntry { + visibility, + pallet_name, + components: None, // Components will be handled directly in `parse_complex_pallet_path` + index, + }) + } +} + +fn parse_complex_pallet_path(input: ParseStream) -> syn::Result { + let mut path = Path::parse_mod_style(input)?; + + // Check if there are generics like `::` + if input.peek(syn::token::Lt) { + let _generics: syn::AngleBracketedGenericArguments = input.parse()?; + } + + // Now check for nested components in `{ Pallet, Call, Storage }` + if input.peek(syn::token::Brace) { + let content; + braced!(content in input); + let _: Punctuated = content.parse_terminated(Ident::parse, Token![,])?; + } + + Ok(path) +} + +struct PalletComponents { + components: Punctuated, +} + +impl Parse for PalletComponents { + fn parse(input: ParseStream) -> syn::Result { + Ok(PalletComponents { + components: input.parse_terminated(Ident::parse, Token![,])?, + }) } } @@ -125,4 +174,37 @@ mod tests { "#; assert!(lint_macro(input).is_err()); } + + #[test] + fn test_with_visibility_and_index() { + let input = r#" + construct_runtime!( + pub PalletA: 0, + PalletB: 1 + ); + "#; + assert!(lint_macro(input).is_ok()); + } + + #[test] + fn test_with_generic_and_index() { + let input = r#" + construct_runtime!( + PalletA, + pallet_collective::::{ Pallet, Call, Storage }: 1 + ); + "#; + assert!(lint_macro(input).is_ok()); + } + + #[test] + fn test_with_nested_and_missing_index() { + let input = r#" + construct_runtime!( + PalletA, + pallet_collective::::{ Pallet, Call, Storage } + ); + "#; + assert!(lint_macro(input).is_err()); + } } From b6c7165538fc2f40895386475e1cf0ac771d4974 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Tue, 17 Sep 2024 15:44:12 -0400 Subject: [PATCH 03/53] silence warnings --- support/linting/src/pallet_index.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/support/linting/src/pallet_index.rs b/support/linting/src/pallet_index.rs index 6e0633d51..9b6723a49 100644 --- a/support/linting/src/pallet_index.rs +++ b/support/linting/src/pallet_index.rs @@ -67,9 +67,9 @@ impl Parse for ConstructRuntimeEntries { } struct PalletEntry { - visibility: Option, + _visibility: Option, pallet_name: Path, - components: Option, + _components: Option, index: Option, // Now index can be None (i.e., missing) } @@ -90,16 +90,16 @@ impl Parse for PalletEntry { }; Ok(PalletEntry { - visibility, + _visibility: visibility, pallet_name, - components: None, // Components will be handled directly in `parse_complex_pallet_path` + _components: None, // Components will be handled directly in `parse_complex_pallet_path` index, }) } } fn parse_complex_pallet_path(input: ParseStream) -> syn::Result { - let mut path = Path::parse_mod_style(input)?; + let path = Path::parse_mod_style(input)?; // Check if there are generics like `::` if input.peek(syn::token::Lt) { @@ -117,13 +117,13 @@ fn parse_complex_pallet_path(input: ParseStream) -> syn::Result { } struct PalletComponents { - components: Punctuated, + _components: Punctuated, } impl Parse for PalletComponents { fn parse(input: ParseStream) -> syn::Result { Ok(PalletComponents { - components: input.parse_terminated(Ident::parse, Token![,])?, + _components: input.parse_terminated(Ident::parse, Token![,])?, }) } } From 5b24374f697cd958e2a480725308680b39a27483 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 18 Sep 2024 01:13:01 -0400 Subject: [PATCH 04/53] checkpoint --- support/linting/src/pallet_index.rs | 50 +++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/support/linting/src/pallet_index.rs b/support/linting/src/pallet_index.rs index 9b6723a49..9f77b4582 100644 --- a/support/linting/src/pallet_index.rs +++ b/support/linting/src/pallet_index.rs @@ -34,6 +34,7 @@ impl<'ast> Visit<'ast> for ConstructRuntimeVisitor { if node.mac.path.is_ident("construct_runtime") { // Token stream parsing logic let tokens = node.mac.tokens.clone(); + println!("{}", tokens.to_string()); let runtime_entries = syn::parse2::(tokens).unwrap(); for entry in runtime_entries.entries { // Check if the entry is missing an explicit index @@ -78,7 +79,7 @@ impl Parse for PalletEntry { // Optionally parse visibility (e.g., `pub`) let visibility: Option = input.parse().ok(); - // Parse the pallet name (with possible generics and paths like `pallet_collective::::{ Pallet, Call, Storage }`) + // Parse the pallet name (handling complex paths with generics and nested components) let pallet_name = parse_complex_pallet_path(input)?; // Optionally parse the index if it's present @@ -92,25 +93,29 @@ impl Parse for PalletEntry { Ok(PalletEntry { _visibility: visibility, pallet_name, - _components: None, // Components will be handled directly in `parse_complex_pallet_path` + _components: None, // Components will be handled in `parse_complex_pallet_path` index, }) } } fn parse_complex_pallet_path(input: ParseStream) -> syn::Result { - let path = Path::parse_mod_style(input)?; + // Start by parsing the base path (pallet name) + let mut path = input.parse::()?; - // Check if there are generics like `::` + // If there are generics like `::`, handle them if input.peek(syn::token::Lt) { let _generics: syn::AngleBracketedGenericArguments = input.parse()?; } - // Now check for nested components in `{ Pallet, Call, Storage }` + // Now handle nested components like `{ Pallet, Call, Storage }` if input.peek(syn::token::Brace) { let content; braced!(content in input); - let _: Punctuated = content.parse_terminated(Ident::parse, Token![,])?; + let components: Punctuated = + content.parse_terminated(Ident::parse, Token![,])?; + + // We can attach the components to the path, if necessary, or validate them separately. } Ok(path) @@ -207,4 +212,37 @@ mod tests { "#; assert!(lint_macro(input).is_err()); } + + #[test] + fn test_complex_construct_runtime() { + let input = r#" + pub struct Runtime { + System : frame_system = 0, + RandomnessCollectiveFlip : pallet_insecure_randomness_collective_flip = 1, + Timestamp : pallet_timestamp = 2, + Aura : pallet_aura = 3, + Grandpa : pallet_grandpa = 4, + Balances : pallet_balances = 5, + TransactionPayment : pallet_transaction_payment = 6, + SubtensorModule : pallet_subtensor = 7, + Triumvirate : pallet_collective::::{ Pallet, Call, Storage, Origin, Event, Config } = 8, + TriumvirateMembers : pallet_membership::::{ Pallet, Call, Storage, Event, Config } = 9, + SenateMembers : pallet_membership::::{ Pallet, Call, Storage, Event, Config } = 10, + Utility : pallet_utility = 11, + Sudo : pallet_sudo = 12, + Multisig : pallet_multisig = 13, + Preimage : pallet_preimage = 14, + Scheduler : pallet_scheduler = 15, + Proxy : pallet_proxy = 16, + Registry : pallet_registry = 17, + Commitments : pallet_commitments = 18, + AdminUtils : pallet_admin_utils = 19, + SafeMode : pallet_safe_mode = 20 + } + "#; + + // Call the lint function on this input to ensure it parses correctly + let result = lint_macro(input); + assert!(result.is_ok()); + } } From ca3c9f4c9be18fadb1342a1d8f2f49212c01299a Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 18 Sep 2024 01:34:21 -0400 Subject: [PATCH 05/53] closer --- support/linting/src/pallet_index.rs | 107 ++++++++++++++++++---------- 1 file changed, 71 insertions(+), 36 deletions(-) diff --git a/support/linting/src/pallet_index.rs b/support/linting/src/pallet_index.rs index 9f77b4582..b0ae7d177 100644 --- a/support/linting/src/pallet_index.rs +++ b/support/linting/src/pallet_index.rs @@ -34,19 +34,27 @@ impl<'ast> Visit<'ast> for ConstructRuntimeVisitor { if node.mac.path.is_ident("construct_runtime") { // Token stream parsing logic let tokens = node.mac.tokens.clone(); - println!("{}", tokens.to_string()); - let runtime_entries = syn::parse2::(tokens).unwrap(); - for entry in runtime_entries.entries { - // Check if the entry is missing an explicit index - if entry.index.is_none() { - self.errors.push(syn::Error::new( - entry.pallet_name.span(), - format!( - "Pallet `{}` does not have an explicit index in construct_runtime!", - entry.pallet_name.to_token_stream().to_string().trim() - ), - )); + + // Try parsing as runtime entries + if let Ok(runtime_entries) = syn::parse2::(tokens) { + for entry in runtime_entries.entries { + // Check if the entry is missing an explicit index + if entry.index.is_none() { + self.errors.push(syn::Error::new( + entry.pallet_name.span(), + format!( + "Pallet `{}` does not have an explicit index in construct_runtime!", + entry.pallet_name.to_token_stream().to_string().trim() + ), + )); + } } + } else { + // Handle other cases, e.g., enum/struct definitions inside construct_runtime + self.errors.push(syn::Error::new( + node.mac.span(), + "Failed to parse construct_runtime!", + )); } } @@ -214,31 +222,58 @@ mod tests { } #[test] - fn test_complex_construct_runtime() { + fn test_complex_construct_runtime_enum_should_fail() { + // This test should fail because there are no explicit indices for the pallets let input = r#" - pub struct Runtime { - System : frame_system = 0, - RandomnessCollectiveFlip : pallet_insecure_randomness_collective_flip = 1, - Timestamp : pallet_timestamp = 2, - Aura : pallet_aura = 3, - Grandpa : pallet_grandpa = 4, - Balances : pallet_balances = 5, - TransactionPayment : pallet_transaction_payment = 6, - SubtensorModule : pallet_subtensor = 7, - Triumvirate : pallet_collective::::{ Pallet, Call, Storage, Origin, Event, Config } = 8, - TriumvirateMembers : pallet_membership::::{ Pallet, Call, Storage, Event, Config } = 9, - SenateMembers : pallet_membership::::{ Pallet, Call, Storage, Event, Config } = 10, - Utility : pallet_utility = 11, - Sudo : pallet_sudo = 12, - Multisig : pallet_multisig = 13, - Preimage : pallet_preimage = 14, - Scheduler : pallet_scheduler = 15, - Proxy : pallet_proxy = 16, - Registry : pallet_registry = 17, - Commitments : pallet_commitments = 18, - AdminUtils : pallet_admin_utils = 19, - SafeMode : pallet_safe_mode = 20 - } + construct_runtime! { + pub enum Test { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Config, Storage, Event}, + Triumvirate: pallet_collective::::{Pallet, Call, Storage, Origin, Event, Config}, + TriumvirateMembers: pallet_membership::::{Pallet, Call, Storage, Event, Config}, + Senate: pallet_collective::::{Pallet, Call, Storage, Origin, Event, Config}, + SenateMembers: pallet_membership::::{Pallet, Call, Storage, Event, Config}, + SubtensorModule: pallet_subtensor::{Pallet, Call, Storage, Event}, + Utility: pallet_utility::{Pallet, Call, Storage, Event}, + Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event}, + Preimage: pallet_preimage::{Pallet, Call, Storage, Event}, + } + } + "#; + + // This should fail because there are no explicit indices + let result = lint_macro(input); + assert!(result.is_err()); + } + + #[test] + fn test_complex_construct_runtime_struct() { + let input = r#" + construct_runtime! { + pub struct Runtime { + System : frame_system = 0, + RandomnessCollectiveFlip : pallet_insecure_randomness_collective_flip = 1, + Timestamp : pallet_timestamp = 2, + Aura : pallet_aura = 3, + Grandpa : pallet_grandpa = 4, + Balances : pallet_balances = 5, + TransactionPayment : pallet_transaction_payment = 6, + SubtensorModule : pallet_subtensor = 7, + Triumvirate : pallet_collective::::{ Pallet, Call, Storage, Origin, Event, Config } = 8, + TriumvirateMembers : pallet_membership::::{ Pallet, Call, Storage, Event, Config } = 9, + SenateMembers : pallet_membership::::{ Pallet, Call, Storage, Event, Config } = 10, + Utility : pallet_utility = 11, + Sudo : pallet_sudo = 12, + Multisig : pallet_multisig = 13, + Preimage : pallet_preimage = 14, + Scheduler : pallet_scheduler = 15, + Proxy : pallet_proxy = 16, + Registry : pallet_registry = 17, + Commitments : pallet_commitments = 18, + AdminUtils : pallet_admin_utils = 19, + SafeMode : pallet_safe_mode = 20 + } + } "#; // Call the lint function on this input to ensure it parses correctly From d49581250e753c22bb2bf4422efb94db592650c7 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 18 Sep 2024 10:47:01 -0400 Subject: [PATCH 06/53] still 2 test failures --- support/linting/src/pallet_index.rs | 42 +++++++++++++++-------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/support/linting/src/pallet_index.rs b/support/linting/src/pallet_index.rs index b0ae7d177..57cc4e46f 100644 --- a/support/linting/src/pallet_index.rs +++ b/support/linting/src/pallet_index.rs @@ -36,7 +36,8 @@ impl<'ast> Visit<'ast> for ConstructRuntimeVisitor { let tokens = node.mac.tokens.clone(); // Try parsing as runtime entries - if let Ok(runtime_entries) = syn::parse2::(tokens) { + let result = syn::parse2::(tokens); + if let Ok(runtime_entries) = result { for entry in runtime_entries.entries { // Check if the entry is missing an explicit index if entry.index.is_none() { @@ -51,10 +52,7 @@ impl<'ast> Visit<'ast> for ConstructRuntimeVisitor { } } else { // Handle other cases, e.g., enum/struct definitions inside construct_runtime - self.errors.push(syn::Error::new( - node.mac.span(), - "Failed to parse construct_runtime!", - )); + self.errors.push(result.unwrap_err()); } } @@ -63,6 +61,7 @@ impl<'ast> Visit<'ast> for ConstructRuntimeVisitor { } } +#[derive(Debug)] struct ConstructRuntimeEntries { entries: Punctuated, } @@ -75,6 +74,7 @@ impl Parse for ConstructRuntimeEntries { } } +#[derive(Debug)] struct PalletEntry { _visibility: Option, pallet_name: Path, @@ -87,6 +87,11 @@ impl Parse for PalletEntry { // Optionally parse visibility (e.g., `pub`) let visibility: Option = input.parse().ok(); + // Handle 'struct' keyword if present + if input.peek(Token![struct]) { + let _: Token![struct] = input.parse()?; + } + // Parse the pallet name (handling complex paths with generics and nested components) let pallet_name = parse_complex_pallet_path(input)?; @@ -109,26 +114,25 @@ impl Parse for PalletEntry { fn parse_complex_pallet_path(input: ParseStream) -> syn::Result { // Start by parsing the base path (pallet name) - let mut path = input.parse::()?; + let path = input.parse::()?; // If there are generics like `::`, handle them if input.peek(syn::token::Lt) { - let _generics: syn::AngleBracketedGenericArguments = input.parse()?; + let _: syn::AngleBracketedGenericArguments = input.parse()?; } // Now handle nested components like `{ Pallet, Call, Storage }` if input.peek(syn::token::Brace) { let content; braced!(content in input); - let components: Punctuated = + let _components: Punctuated = content.parse_terminated(Ident::parse, Token![,])?; - - // We can attach the components to the path, if necessary, or validate them separately. } Ok(path) } +#[derive(Debug)] struct PalletComponents { _components: Punctuated, } @@ -163,7 +167,7 @@ mod tests { PalletB ); "#; - assert!(lint_macro(input).is_err()); + lint_macro(input).unwrap_err(); } #[test] @@ -174,7 +178,7 @@ mod tests { PalletB: 1 ); "#; - assert!(lint_macro(input).is_ok()); + lint_macro(input).unwrap(); } #[test] @@ -185,7 +189,7 @@ mod tests { PalletB: 1 ); "#; - assert!(lint_macro(input).is_err()); + lint_macro(input).unwrap_err(); } #[test] @@ -196,7 +200,7 @@ mod tests { PalletB: 1 ); "#; - assert!(lint_macro(input).is_ok()); + lint_macro(input).unwrap(); } #[test] @@ -207,7 +211,7 @@ mod tests { pallet_collective::::{ Pallet, Call, Storage }: 1 ); "#; - assert!(lint_macro(input).is_ok()); + lint_macro(input).unwrap(); } #[test] @@ -218,7 +222,7 @@ mod tests { pallet_collective::::{ Pallet, Call, Storage } ); "#; - assert!(lint_macro(input).is_err()); + lint_macro(input).unwrap_err(); } #[test] @@ -242,8 +246,7 @@ mod tests { "#; // This should fail because there are no explicit indices - let result = lint_macro(input); - assert!(result.is_err()); + lint_macro(input).unwrap_err(); } #[test] @@ -277,7 +280,6 @@ mod tests { "#; // Call the lint function on this input to ensure it parses correctly - let result = lint_macro(input); - assert!(result.is_ok()); + lint_macro(input).unwrap(); } } From 11ca35fd8a49528a39878ca0b2babfdd435c7192 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 18 Sep 2024 11:46:30 -0400 Subject: [PATCH 07/53] close --- support/linting/src/pallet_index.rs | 33 +++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/support/linting/src/pallet_index.rs b/support/linting/src/pallet_index.rs index 57cc4e46f..60c0b26d7 100644 --- a/support/linting/src/pallet_index.rs +++ b/support/linting/src/pallet_index.rs @@ -34,11 +34,13 @@ impl<'ast> Visit<'ast> for ConstructRuntimeVisitor { if node.mac.path.is_ident("construct_runtime") { // Token stream parsing logic let tokens = node.mac.tokens.clone(); + println!("Parsing construct_runtime! tokens: {}", tokens.to_string()); // Try parsing as runtime entries let result = syn::parse2::(tokens); if let Ok(runtime_entries) = result { for entry in runtime_entries.entries { + println!("Parsed entry: {:?}", entry); // Check if the entry is missing an explicit index if entry.index.is_none() { self.errors.push(syn::Error::new( @@ -51,7 +53,8 @@ impl<'ast> Visit<'ast> for ConstructRuntimeVisitor { } } } else { - // Handle other cases, e.g., enum/struct definitions inside construct_runtime + // Print out the error and where it failed + println!("Failed to parse construct_runtime! block: {:?}", result); self.errors.push(result.unwrap_err()); } } @@ -68,9 +71,10 @@ struct ConstructRuntimeEntries { impl Parse for ConstructRuntimeEntries { fn parse(input: ParseStream) -> syn::Result { - Ok(ConstructRuntimeEntries { - entries: input.parse_terminated(PalletEntry::parse, Token![,])?, - }) + println!("Parsing ConstructRuntimeEntries"); + let entries = input.parse_terminated(PalletEntry::parse, Token![,])?; + println!("Parsed entries: {:?}", entries); + Ok(ConstructRuntimeEntries { entries }) } } @@ -86,20 +90,26 @@ impl Parse for PalletEntry { fn parse(input: ParseStream) -> syn::Result { // Optionally parse visibility (e.g., `pub`) let visibility: Option = input.parse().ok(); + println!("Parsed visibility: {:?}", visibility); // Handle 'struct' keyword if present if input.peek(Token![struct]) { let _: Token![struct] = input.parse()?; + println!("Parsed 'struct' keyword"); } // Parse the pallet name (handling complex paths with generics and nested components) let pallet_name = parse_complex_pallet_path(input)?; + println!("Parsed pallet name: {:?}", pallet_name); // Optionally parse the index if it's present let index = if input.peek(Colon) { input.parse::()?; - Some(input.parse::()?) + let index = input.parse::()?; + println!("Parsed index: {:?}", index); + Some(index) } else { + println!("No index found"); None // Missing index is allowed during parsing }; @@ -114,19 +124,22 @@ impl Parse for PalletEntry { fn parse_complex_pallet_path(input: ParseStream) -> syn::Result { // Start by parsing the base path (pallet name) - let path = input.parse::()?; + let mut path = input.parse::()?; + println!("Parsed base path: {:?}", path); // If there are generics like `::`, handle them if input.peek(syn::token::Lt) { - let _: syn::AngleBracketedGenericArguments = input.parse()?; + let generics: syn::AngleBracketedGenericArguments = input.parse()?; + println!("Parsed generics: {:?}", generics); } // Now handle nested components like `{ Pallet, Call, Storage }` if input.peek(syn::token::Brace) { let content; braced!(content in input); - let _components: Punctuated = + let components: Punctuated = content.parse_terminated(Ident::parse, Token![,])?; + println!("Parsed components: {:?}", components); } Ok(path) @@ -139,8 +152,10 @@ struct PalletComponents { impl Parse for PalletComponents { fn parse(input: ParseStream) -> syn::Result { + let components = input.parse_terminated(Ident::parse, Token![,])?; + println!("Parsed components: {:?}", components); Ok(PalletComponents { - _components: input.parse_terminated(Ident::parse, Token![,])?, + _components: components, }) } } From 57eb7508463efbc0bbb6c5fea833968c85ee6f27 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 18 Sep 2024 12:32:30 -0400 Subject: [PATCH 08/53] WIP --- support/linting/src/pallet_index.rs | 55 ++++++++++++----------------- 1 file changed, 22 insertions(+), 33 deletions(-) diff --git a/support/linting/src/pallet_index.rs b/support/linting/src/pallet_index.rs index 60c0b26d7..b373a04b2 100644 --- a/support/linting/src/pallet_index.rs +++ b/support/linting/src/pallet_index.rs @@ -36,11 +36,9 @@ impl<'ast> Visit<'ast> for ConstructRuntimeVisitor { let tokens = node.mac.tokens.clone(); println!("Parsing construct_runtime! tokens: {}", tokens.to_string()); - // Try parsing as runtime entries let result = syn::parse2::(tokens); if let Ok(runtime_entries) = result { for entry in runtime_entries.entries { - println!("Parsed entry: {:?}", entry); // Check if the entry is missing an explicit index if entry.index.is_none() { self.errors.push(syn::Error::new( @@ -53,7 +51,7 @@ impl<'ast> Visit<'ast> for ConstructRuntimeVisitor { } } } else { - // Print out the error and where it failed + // Log error println!("Failed to parse construct_runtime! block: {:?}", result); self.errors.push(result.unwrap_err()); } @@ -71,66 +69,60 @@ struct ConstructRuntimeEntries { impl Parse for ConstructRuntimeEntries { fn parse(input: ParseStream) -> syn::Result { - println!("Parsing ConstructRuntimeEntries"); let entries = input.parse_terminated(PalletEntry::parse, Token![,])?; - println!("Parsed entries: {:?}", entries); Ok(ConstructRuntimeEntries { entries }) } } #[derive(Debug)] struct PalletEntry { - _visibility: Option, + visibility: Option, pallet_name: Path, - _components: Option, - index: Option, // Now index can be None (i.e., missing) + components: Option, + index: Option, } impl Parse for PalletEntry { fn parse(input: ParseStream) -> syn::Result { // Optionally parse visibility (e.g., `pub`) let visibility: Option = input.parse().ok(); - println!("Parsed visibility: {:?}", visibility); - - // Handle 'struct' keyword if present - if input.peek(Token![struct]) { - let _: Token![struct] = input.parse()?; - println!("Parsed 'struct' keyword"); - } // Parse the pallet name (handling complex paths with generics and nested components) let pallet_name = parse_complex_pallet_path(input)?; - println!("Parsed pallet name: {:?}", pallet_name); + + // Optionally parse the components in `{ Pallet, Call, Storage }` + let components = if input.peek(syn::token::Brace) { + let content; + braced!(content in input); + Some(content.parse::()?) + } else { + None + }; // Optionally parse the index if it's present let index = if input.peek(Colon) { input.parse::()?; - let index = input.parse::()?; - println!("Parsed index: {:?}", index); - Some(index) + Some(input.parse::()?) } else { - println!("No index found"); - None // Missing index is allowed during parsing + None }; Ok(PalletEntry { - _visibility: visibility, + visibility, pallet_name, - _components: None, // Components will be handled in `parse_complex_pallet_path` + components, index, }) } } fn parse_complex_pallet_path(input: ParseStream) -> syn::Result { - // Start by parsing the base path (pallet name) - let mut path = input.parse::()?; - println!("Parsed base path: {:?}", path); + // Parse the base path (e.g., `pallet_collective`) + let path = input.parse::()?; // If there are generics like `::`, handle them if input.peek(syn::token::Lt) { - let generics: syn::AngleBracketedGenericArguments = input.parse()?; - println!("Parsed generics: {:?}", generics); + let _generics: syn::AngleBracketedGenericArguments = input.parse()?; } // Now handle nested components like `{ Pallet, Call, Storage }` @@ -147,15 +139,13 @@ fn parse_complex_pallet_path(input: ParseStream) -> syn::Result { #[derive(Debug)] struct PalletComponents { - _components: Punctuated, + components: Punctuated, } impl Parse for PalletComponents { fn parse(input: ParseStream) -> syn::Result { - let components = input.parse_terminated(Ident::parse, Token![,])?; - println!("Parsed components: {:?}", components); Ok(PalletComponents { - _components: components, + components: input.parse_terminated(Ident::parse, Token![,])?, }) } } @@ -294,7 +284,6 @@ mod tests { } "#; - // Call the lint function on this input to ensure it parses correctly lint_macro(input).unwrap(); } } From 23b4a68b40497d4474e0fcaffa2820aa31886f59 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 18 Sep 2024 15:04:28 -0400 Subject: [PATCH 09/53] add procedural-fork stub --- Cargo.lock | 5 +++++ Cargo.toml | 2 +- support/linting/Cargo.toml | 1 + support/procedural-fork/Cargo.toml | 9 +++++++++ support/procedural-fork/src/lib.rs | 1 + 5 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 support/procedural-fork/Cargo.toml create mode 100644 support/procedural-fork/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index ee0933379..867ed787f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5970,6 +5970,10 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "procedural-fork" +version = "1.10.0-rc3" + [[package]] name = "prometheus" version = "0.13.4" @@ -9231,6 +9235,7 @@ name = "subtensor-linting" version = "0.1.0" dependencies = [ "proc-macro2", + "procedural-fork", "quote", "syn 2.0.71", ] diff --git a/Cargo.toml b/Cargo.toml index 4f162ca6b..a9788a222 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ members = [ "runtime", "support/tools", "support/macros", - "support/linting", + "support/linting", "support/procedural-fork", ] resolver = "2" diff --git a/support/linting/Cargo.toml b/support/linting/Cargo.toml index 1e37d8163..4378ca9dd 100644 --- a/support/linting/Cargo.toml +++ b/support/linting/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" syn.workspace = true quote.workspace = true proc-macro2.workspace = true +procedural-fork = { version = "1.10.0-rc3", path = "../procedural-fork" } [lints] workspace = true diff --git a/support/procedural-fork/Cargo.toml b/support/procedural-fork/Cargo.toml new file mode 100644 index 000000000..070e3e9c2 --- /dev/null +++ b/support/procedural-fork/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "procedural-fork" +version = "1.10.0-rc3" +edition = "2021" + +[dependencies] + +[lints] +workspace = true diff --git a/support/procedural-fork/src/lib.rs b/support/procedural-fork/src/lib.rs new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/support/procedural-fork/src/lib.rs @@ -0,0 +1 @@ + From c23485fac00b303a2fc0b0cd35fc970fbeeb448c Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 18 Sep 2024 15:05:37 -0400 Subject: [PATCH 10/53] add update.sh script --- support/procedural-fork/update.sh | 39 +++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100755 support/procedural-fork/update.sh diff --git a/support/procedural-fork/update.sh b/support/procedural-fork/update.sh new file mode 100755 index 000000000..c8bbd3cc9 --- /dev/null +++ b/support/procedural-fork/update.sh @@ -0,0 +1,39 @@ +#!/bin/sh + +# Enable error handling +set -e + +# Set the repository and tag +REPO_URL="git@github.com:paritytech/polkadot-sdk.git" +POLKADOT_SDK_TAG="v1.10.0-rc3" + +# Create a temporary directory for cloning +TMP_DIR=$(mktemp -d) + +# Define source and destination directories +SRC_DIR="substrate/support/procedural/src" +DEST_DIR="$(pwd)/src" # Absolute path to `src` directory of procedural-fork + +# Check if DEST_DIR exists +if [ ! -d "$DEST_DIR" ]; then + echo "Error: Destination directory $DEST_DIR does not exist." + rm -rf "$TMP_DIR" + exit 1 +fi + +# Clone only the required directory from the repository +echo "Cloning $REPO_URL at tag $POLKADOT_SDK_TAG ..." +git clone --depth 1 --branch "$POLKADOT_SDK_TAG" --filter=blob:none --sparse "$REPO_URL" "$TMP_DIR" + +cd "$TMP_DIR" +git sparse-checkout init --cone +git sparse-checkout set "$SRC_DIR" + +# Copy all files from `src` except `lib.rs` to the destination folder +echo "Copying files to $DEST_DIR ..." +rsync -a --exclude='lib.rs' "$TMP_DIR/$SRC_DIR/" "$DEST_DIR/" + +# Clean up the temporary directory +rm -rf "$TMP_DIR" + +echo "Update completed successfully." From fdd33fbeb3ba59740b05294bb49b9458236b41e1 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 18 Sep 2024 15:06:58 -0400 Subject: [PATCH 11/53] add debugging --- support/procedural-fork/update.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/support/procedural-fork/update.sh b/support/procedural-fork/update.sh index c8bbd3cc9..f47a782e2 100755 --- a/support/procedural-fork/update.sh +++ b/support/procedural-fork/update.sh @@ -25,10 +25,17 @@ fi echo "Cloning $REPO_URL at tag $POLKADOT_SDK_TAG ..." git clone --depth 1 --branch "$POLKADOT_SDK_TAG" --filter=blob:none --sparse "$REPO_URL" "$TMP_DIR" +# Navigate to the cloned directory cd "$TMP_DIR" + +# Initialize sparse-checkout and set the directory git sparse-checkout init --cone git sparse-checkout set "$SRC_DIR" +# Debugging: List the contents of the sparse-checked-out directory +echo "Contents of $TMP_DIR/$SRC_DIR after sparse-checkout:" +ls -l "$TMP_DIR/$SRC_DIR" || { echo "Error: Sparse checkout failed, $SRC_DIR not found."; rm -rf "$TMP_DIR"; exit 1; } + # Copy all files from `src` except `lib.rs` to the destination folder echo "Copying files to $DEST_DIR ..." rsync -a --exclude='lib.rs' "$TMP_DIR/$SRC_DIR/" "$DEST_DIR/" From 69410ace4a8ab6420c5548f42a65ec96765e0cfb Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 18 Sep 2024 15:08:18 -0400 Subject: [PATCH 12/53] update script --- support/procedural-fork/update.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/support/procedural-fork/update.sh b/support/procedural-fork/update.sh index f47a782e2..a8793b261 100755 --- a/support/procedural-fork/update.sh +++ b/support/procedural-fork/update.sh @@ -11,7 +11,7 @@ POLKADOT_SDK_TAG="v1.10.0-rc3" TMP_DIR=$(mktemp -d) # Define source and destination directories -SRC_DIR="substrate/support/procedural/src" +SRC_DIR="substrate/frame/support/procedural/src" DEST_DIR="$(pwd)/src" # Absolute path to `src` directory of procedural-fork # Check if DEST_DIR exists @@ -28,7 +28,7 @@ git clone --depth 1 --branch "$POLKADOT_SDK_TAG" --filter=blob:none --sparse "$R # Navigate to the cloned directory cd "$TMP_DIR" -# Initialize sparse-checkout and set the directory +# Initialize sparse-checkout and set the correct directory git sparse-checkout init --cone git sparse-checkout set "$SRC_DIR" From 021ac06dce3cd8448d5e8e7dded7f2bcecb68f20 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 18 Sep 2024 16:00:10 -0400 Subject: [PATCH 13/53] local no-proc-macro fork of frame-support-procedural --- Cargo.lock | 75 +- support/procedural-fork/Cargo.toml | 29 +- support/procedural-fork/src/benchmark.rs | 1202 +++++++++++++++++ .../src/construct_runtime/expand/call.rs | 223 +++ .../expand/composite_helper.rs | 101 ++ .../src/construct_runtime/expand/config.rs | 147 ++ .../construct_runtime/expand/freeze_reason.rs | 75 + .../construct_runtime/expand/hold_reason.rs | 75 + .../src/construct_runtime/expand/inherent.rs | 254 ++++ .../src/construct_runtime/expand/lock_id.rs | 64 + .../src/construct_runtime/expand/metadata.rs | 258 ++++ .../src/construct_runtime/expand/mod.rs | 43 + .../src/construct_runtime/expand/origin.rs | 455 +++++++ .../construct_runtime/expand/outer_enums.rs | 279 ++++ .../construct_runtime/expand/slash_reason.rs | 64 + .../src/construct_runtime/expand/task.rs | 131 ++ .../src/construct_runtime/expand/unsigned.rs | 89 ++ .../src/construct_runtime/mod.rs | 809 +++++++++++ .../src/construct_runtime/parse.rs | 786 +++++++++++ support/procedural-fork/src/crate_version.rs | 54 + support/procedural-fork/src/derive_impl.rs | 303 +++++ .../procedural-fork/src/dummy_part_checker.rs | 79 ++ support/procedural-fork/src/dynamic_params.rs | 563 ++++++++ support/procedural-fork/src/key_prefix.rs | 104 ++ support/procedural-fork/src/lib.rs | 66 + .../procedural-fork/src/match_and_insert.rs | 159 +++ support/procedural-fork/src/no_bound/clone.rs | 107 ++ support/procedural-fork/src/no_bound/debug.rs | 121 ++ .../procedural-fork/src/no_bound/default.rs | 161 +++ support/procedural-fork/src/no_bound/mod.rs | 25 + support/procedural-fork/src/no_bound/ord.rs | 75 + .../src/no_bound/partial_eq.rs | 137 ++ .../src/no_bound/partial_ord.rs | 89 ++ .../procedural-fork/src/pallet/expand/call.rs | 452 +++++++ .../src/pallet/expand/composite.rs | 40 + .../src/pallet/expand/config.rs | 97 ++ .../src/pallet/expand/constants.rs | 108 ++ .../src/pallet/expand/doc_only.rs | 103 ++ .../src/pallet/expand/documentation.rs | 172 +++ .../src/pallet/expand/error.rs | 191 +++ .../src/pallet/expand/event.rs | 174 +++ .../src/pallet/expand/genesis_build.rs | 49 + .../src/pallet/expand/genesis_config.rs | 147 ++ .../src/pallet/expand/hooks.rs | 340 +++++ .../src/pallet/expand/inherent.rs | 55 + .../src/pallet/expand/instances.rs | 43 + .../procedural-fork/src/pallet/expand/mod.rs | 130 ++ .../src/pallet/expand/origin.rs | 55 + .../src/pallet/expand/pallet_struct.rs | 290 ++++ .../src/pallet/expand/storage.rs | 919 +++++++++++++ .../src/pallet/expand/tasks.rs | 267 ++++ .../src/pallet/expand/tt_default_parts.rs | 216 +++ .../src/pallet/expand/type_value.rs | 77 ++ .../src/pallet/expand/validate_unsigned.rs | 56 + .../src/pallet/expand/warnings.rs | 98 ++ support/procedural-fork/src/pallet/mod.rs | 61 + .../procedural-fork/src/pallet/parse/call.rs | 467 +++++++ .../src/pallet/parse/composite.rs | 191 +++ .../src/pallet/parse/config.rs | 590 ++++++++ .../procedural-fork/src/pallet/parse/error.rs | 115 ++ .../procedural-fork/src/pallet/parse/event.rs | 141 ++ .../src/pallet/parse/extra_constants.rs | 160 +++ .../src/pallet/parse/genesis_build.rs | 61 + .../src/pallet/parse/genesis_config.rs | 73 + .../src/pallet/parse/helper.rs | 632 +++++++++ .../procedural-fork/src/pallet/parse/hooks.rs | 86 ++ .../src/pallet/parse/inherent.rs | 60 + .../procedural-fork/src/pallet/parse/mod.rs | 749 ++++++++++ .../src/pallet/parse/origin.rs | 72 + .../src/pallet/parse/pallet_struct.rs | 149 ++ .../src/pallet/parse/storage.rs | 947 +++++++++++++ .../procedural-fork/src/pallet/parse/tasks.rs | 968 +++++++++++++ .../src/pallet/parse/tests/mod.rs | 264 ++++ .../src/pallet/parse/tests/tasks.rs | 240 ++++ .../src/pallet/parse/type_value.rs | 123 ++ .../src/pallet/parse/validate_unsigned.rs | 62 + support/procedural-fork/src/pallet_error.rs | 178 +++ .../procedural-fork/src/runtime/expand/mod.rs | 320 +++++ support/procedural-fork/src/runtime/mod.rs | 236 ++++ .../src/runtime/parse/helper.rs | 37 + .../procedural-fork/src/runtime/parse/mod.rs | 266 ++++ .../src/runtime/parse/pallet.rs | 99 ++ .../src/runtime/parse/pallet_decl.rs | 60 + .../src/runtime/parse/runtime_struct.rs | 35 + .../src/runtime/parse/runtime_types.rs | 76 ++ support/procedural-fork/src/storage_alias.rs | 676 +++++++++ support/procedural-fork/src/transactional.rs | 60 + support/procedural-fork/src/tt_macro.rs | 105 ++ 88 files changed, 19627 insertions(+), 13 deletions(-) create mode 100644 support/procedural-fork/src/benchmark.rs create mode 100644 support/procedural-fork/src/construct_runtime/expand/call.rs create mode 100644 support/procedural-fork/src/construct_runtime/expand/composite_helper.rs create mode 100644 support/procedural-fork/src/construct_runtime/expand/config.rs create mode 100644 support/procedural-fork/src/construct_runtime/expand/freeze_reason.rs create mode 100644 support/procedural-fork/src/construct_runtime/expand/hold_reason.rs create mode 100644 support/procedural-fork/src/construct_runtime/expand/inherent.rs create mode 100644 support/procedural-fork/src/construct_runtime/expand/lock_id.rs create mode 100644 support/procedural-fork/src/construct_runtime/expand/metadata.rs create mode 100644 support/procedural-fork/src/construct_runtime/expand/mod.rs create mode 100644 support/procedural-fork/src/construct_runtime/expand/origin.rs create mode 100644 support/procedural-fork/src/construct_runtime/expand/outer_enums.rs create mode 100644 support/procedural-fork/src/construct_runtime/expand/slash_reason.rs create mode 100644 support/procedural-fork/src/construct_runtime/expand/task.rs create mode 100644 support/procedural-fork/src/construct_runtime/expand/unsigned.rs create mode 100644 support/procedural-fork/src/construct_runtime/mod.rs create mode 100644 support/procedural-fork/src/construct_runtime/parse.rs create mode 100644 support/procedural-fork/src/crate_version.rs create mode 100644 support/procedural-fork/src/derive_impl.rs create mode 100644 support/procedural-fork/src/dummy_part_checker.rs create mode 100644 support/procedural-fork/src/dynamic_params.rs create mode 100644 support/procedural-fork/src/key_prefix.rs create mode 100644 support/procedural-fork/src/match_and_insert.rs create mode 100644 support/procedural-fork/src/no_bound/clone.rs create mode 100644 support/procedural-fork/src/no_bound/debug.rs create mode 100644 support/procedural-fork/src/no_bound/default.rs create mode 100644 support/procedural-fork/src/no_bound/mod.rs create mode 100644 support/procedural-fork/src/no_bound/ord.rs create mode 100644 support/procedural-fork/src/no_bound/partial_eq.rs create mode 100644 support/procedural-fork/src/no_bound/partial_ord.rs create mode 100644 support/procedural-fork/src/pallet/expand/call.rs create mode 100644 support/procedural-fork/src/pallet/expand/composite.rs create mode 100644 support/procedural-fork/src/pallet/expand/config.rs create mode 100644 support/procedural-fork/src/pallet/expand/constants.rs create mode 100644 support/procedural-fork/src/pallet/expand/doc_only.rs create mode 100644 support/procedural-fork/src/pallet/expand/documentation.rs create mode 100644 support/procedural-fork/src/pallet/expand/error.rs create mode 100644 support/procedural-fork/src/pallet/expand/event.rs create mode 100644 support/procedural-fork/src/pallet/expand/genesis_build.rs create mode 100644 support/procedural-fork/src/pallet/expand/genesis_config.rs create mode 100644 support/procedural-fork/src/pallet/expand/hooks.rs create mode 100644 support/procedural-fork/src/pallet/expand/inherent.rs create mode 100644 support/procedural-fork/src/pallet/expand/instances.rs create mode 100644 support/procedural-fork/src/pallet/expand/mod.rs create mode 100644 support/procedural-fork/src/pallet/expand/origin.rs create mode 100644 support/procedural-fork/src/pallet/expand/pallet_struct.rs create mode 100644 support/procedural-fork/src/pallet/expand/storage.rs create mode 100644 support/procedural-fork/src/pallet/expand/tasks.rs create mode 100644 support/procedural-fork/src/pallet/expand/tt_default_parts.rs create mode 100644 support/procedural-fork/src/pallet/expand/type_value.rs create mode 100644 support/procedural-fork/src/pallet/expand/validate_unsigned.rs create mode 100644 support/procedural-fork/src/pallet/expand/warnings.rs create mode 100644 support/procedural-fork/src/pallet/mod.rs create mode 100644 support/procedural-fork/src/pallet/parse/call.rs create mode 100644 support/procedural-fork/src/pallet/parse/composite.rs create mode 100644 support/procedural-fork/src/pallet/parse/config.rs create mode 100644 support/procedural-fork/src/pallet/parse/error.rs create mode 100644 support/procedural-fork/src/pallet/parse/event.rs create mode 100644 support/procedural-fork/src/pallet/parse/extra_constants.rs create mode 100644 support/procedural-fork/src/pallet/parse/genesis_build.rs create mode 100644 support/procedural-fork/src/pallet/parse/genesis_config.rs create mode 100644 support/procedural-fork/src/pallet/parse/helper.rs create mode 100644 support/procedural-fork/src/pallet/parse/hooks.rs create mode 100644 support/procedural-fork/src/pallet/parse/inherent.rs create mode 100644 support/procedural-fork/src/pallet/parse/mod.rs create mode 100644 support/procedural-fork/src/pallet/parse/origin.rs create mode 100644 support/procedural-fork/src/pallet/parse/pallet_struct.rs create mode 100644 support/procedural-fork/src/pallet/parse/storage.rs create mode 100644 support/procedural-fork/src/pallet/parse/tasks.rs create mode 100644 support/procedural-fork/src/pallet/parse/tests/mod.rs create mode 100644 support/procedural-fork/src/pallet/parse/tests/tasks.rs create mode 100644 support/procedural-fork/src/pallet/parse/type_value.rs create mode 100644 support/procedural-fork/src/pallet/parse/validate_unsigned.rs create mode 100644 support/procedural-fork/src/pallet_error.rs create mode 100644 support/procedural-fork/src/runtime/expand/mod.rs create mode 100644 support/procedural-fork/src/runtime/mod.rs create mode 100644 support/procedural-fork/src/runtime/parse/helper.rs create mode 100644 support/procedural-fork/src/runtime/parse/mod.rs create mode 100644 support/procedural-fork/src/runtime/parse/pallet.rs create mode 100644 support/procedural-fork/src/runtime/parse/pallet_decl.rs create mode 100644 support/procedural-fork/src/runtime/parse/runtime_struct.rs create mode 100644 support/procedural-fork/src/runtime/parse/runtime_types.rs create mode 100644 support/procedural-fork/src/storage_alias.rs create mode 100644 support/procedural-fork/src/transactional.rs create mode 100644 support/procedural-fork/src/tt_macro.rs diff --git a/Cargo.lock b/Cargo.lock index 867ed787f..50f4e50c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2497,13 +2497,26 @@ dependencies = [ "cfg-expr", "derive-syn-parse 0.2.0", "expander", - "frame-support-procedural-tools", + "frame-support-procedural-tools 10.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=v1.10.0-rc3)", "itertools 0.10.5", "macro_magic", "proc-macro-warning", "proc-macro2", "quote", - "sp-crypto-hashing", + "sp-crypto-hashing 0.0.0", + "syn 2.0.71", +] + +[[package]] +name = "frame-support-procedural-tools" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3363df38464c47a73eb521a4f648bfcc7537a82d70347ef8af3f73b6d019e910" +dependencies = [ + "frame-support-procedural-tools-derive 11.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-crate 3.1.0", + "proc-macro2", + "quote", "syn 2.0.71", ] @@ -2512,13 +2525,24 @@ name = "frame-support-procedural-tools" version = "10.0.0" source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=v1.10.0-rc3#8d2f55dfe06bae13e9f47ccf587acfd3fb9cd923" dependencies = [ - "frame-support-procedural-tools-derive", + "frame-support-procedural-tools-derive 11.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=v1.10.0-rc3)", "proc-macro-crate 3.1.0", "proc-macro2", "quote", "syn 2.0.71", ] +[[package]] +name = "frame-support-procedural-tools-derive" +version = "11.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68672b9ec6fe72d259d3879dc212c5e42e977588cdac830c76f54d9f492aeb58" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.71", +] + [[package]] name = "frame-support-procedural-tools-derive" version = "11.0.0" @@ -5973,6 +5997,21 @@ dependencies = [ [[package]] name = "procedural-fork" version = "1.10.0-rc3" +dependencies = [ + "Inflector", + "cfg-expr", + "derive-syn-parse 0.2.0", + "expander", + "frame-support-procedural-tools 10.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.10.5", + "macro_magic", + "proc-macro-warning", + "proc-macro2", + "quote", + "regex", + "sp-crypto-hashing 0.1.0", + "syn 2.0.71", +] [[package]] name = "prometheus" @@ -6789,7 +6828,7 @@ dependencies = [ "serde_json", "sp-blockchain", "sp-core", - "sp-crypto-hashing", + "sp-crypto-hashing 0.0.0", "sp-genesis-builder", "sp-io", "sp-runtime", @@ -6991,7 +7030,7 @@ dependencies = [ "sp-consensus", "sp-consensus-grandpa", "sp-core", - "sp-crypto-hashing", + "sp-crypto-hashing 0.0.0", "sp-keystore", "sp-runtime", "substrate-prometheus-endpoint", @@ -7576,7 +7615,7 @@ dependencies = [ "serde", "serde_json", "sp-core", - "sp-crypto-hashing", + "sp-crypto-hashing 0.0.0", "sp-io", "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=v1.10.0-rc3)", ] @@ -7660,7 +7699,7 @@ dependencies = [ "sp-api", "sp-blockchain", "sp-core", - "sp-crypto-hashing", + "sp-crypto-hashing 0.0.0", "sp-runtime", "sp-tracing 16.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=v1.10.0-rc3)", "sp-transaction-pool", @@ -8398,7 +8437,7 @@ dependencies = [ "secp256k1", "secrecy", "serde", - "sp-crypto-hashing", + "sp-crypto-hashing 0.0.0", "sp-debug-derive 14.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=v1.10.0-rc3)", "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=v1.10.0-rc3)", "sp-runtime-interface 24.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=v1.10.0-rc3)", @@ -8445,13 +8484,27 @@ dependencies = [ "twox-hash", ] +[[package]] +name = "sp-crypto-hashing" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc9927a7f81334ed5b8a98a4a978c81324d12bd9713ec76b5c68fd410174c5eb" +dependencies = [ + "blake2b_simd", + "byteorder", + "digest 0.10.7", + "sha2 0.10.8", + "sha3", + "twox-hash", +] + [[package]] name = "sp-crypto-hashing-proc-macro" version = "0.0.0" source = "git+https://github.com/paritytech/polkadot-sdk.git?tag=v1.10.0-rc3#8d2f55dfe06bae13e9f47ccf587acfd3fb9cd923" dependencies = [ "quote", - "sp-crypto-hashing", + "sp-crypto-hashing 0.0.0", "syn 2.0.71", ] @@ -8541,7 +8594,7 @@ dependencies = [ "rustversion", "secp256k1", "sp-core", - "sp-crypto-hashing", + "sp-crypto-hashing 0.0.0", "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=v1.10.0-rc3)", "sp-keystore", "sp-runtime-interface 24.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=v1.10.0-rc3)", @@ -8785,7 +8838,7 @@ dependencies = [ "sp-api", "sp-application-crypto", "sp-core", - "sp-crypto-hashing", + "sp-crypto-hashing 0.0.0", "sp-externalities 0.25.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=v1.10.0-rc3)", "sp-runtime", "sp-runtime-interface 24.0.0 (git+https://github.com/paritytech/polkadot-sdk.git?tag=v1.10.0-rc3)", diff --git a/support/procedural-fork/Cargo.toml b/support/procedural-fork/Cargo.toml index 070e3e9c2..61221ead8 100644 --- a/support/procedural-fork/Cargo.toml +++ b/support/procedural-fork/Cargo.toml @@ -3,7 +3,32 @@ name = "procedural-fork" version = "1.10.0-rc3" edition = "2021" -[dependencies] - [lints] workspace = true + +[dependencies] +derive-syn-parse = "0.2" +Inflector = "0.11" +cfg-expr = "0.15" +itertools = "0.10" +proc-macro2.workspace = true +quote.workspace = true +syn.workspace = true +macro_magic = { version = "0.5", features = ["proc_support"] } +frame-support-procedural-tools = { version = "10.0.0" } +proc-macro-warning = { version = "1", default-features = false } +expander = "2" +sp-crypto-hashing = { default-features = false, version = "0.1.0" } + +[dev-dependencies] +regex = "1" + +[features] +default = ["std"] +std = ["sp-crypto-hashing/std"] +no-metadata-docs = [] +experimental = [] +# Generate impl-trait for tuples with the given number of tuples. Will be needed as the number of +# pallets in a runtime grows. Does increase the compile time! +tuples-96 = [] +tuples-128 = [] diff --git a/support/procedural-fork/src/benchmark.rs b/support/procedural-fork/src/benchmark.rs new file mode 100644 index 000000000..0a62c3f92 --- /dev/null +++ b/support/procedural-fork/src/benchmark.rs @@ -0,0 +1,1202 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Home of the parsing and expansion code for the new pallet benchmarking syntax + +use derive_syn_parse::Parse; +use frame_support_procedural_tools::generate_access_from_frame_or_crate; +use proc_macro::TokenStream; +use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; +use quote::{quote, ToTokens}; +use syn::{ + parse::{Nothing, ParseStream}, + parse_quote, + punctuated::Punctuated, + spanned::Spanned, + token::{Comma, Gt, Lt, PathSep}, + Attribute, Error, Expr, ExprBlock, ExprCall, ExprPath, FnArg, Item, ItemFn, ItemMod, Pat, Path, + PathArguments, PathSegment, Result, ReturnType, Signature, Stmt, Token, Type, TypePath, + Visibility, WhereClause, +}; + +mod keywords { + use syn::custom_keyword; + + custom_keyword!(benchmark); + custom_keyword!(benchmarks); + custom_keyword!(block); + custom_keyword!(extra); + custom_keyword!(pov_mode); + custom_keyword!(extrinsic_call); + custom_keyword!(skip_meta); + custom_keyword!(BenchmarkError); + custom_keyword!(Result); + custom_keyword!(MaxEncodedLen); + custom_keyword!(Measured); + custom_keyword!(Ignored); + + pub const BENCHMARK_TOKEN: &str = stringify!(benchmark); + pub const BENCHMARKS_TOKEN: &str = stringify!(benchmarks); +} + +/// This represents the raw parsed data for a param definition such as `x: Linear<10, 20>`. +#[derive(Clone)] +struct ParamDef { + name: String, + _typ: Type, + start: syn::GenericArgument, + end: syn::GenericArgument, +} + +/// Allows easy parsing of the `<10, 20>` component of `x: Linear<10, 20>`. +#[derive(Parse)] +struct RangeArgs { + _lt_token: Lt, + start: syn::GenericArgument, + _comma: Comma, + end: syn::GenericArgument, + _trailing_comma: Option, + _gt_token: Gt, +} + +#[derive(Clone, Debug)] +struct BenchmarkAttrs { + skip_meta: bool, + extra: bool, + pov_mode: Option, +} + +/// Represents a single benchmark option +enum BenchmarkAttr { + Extra, + SkipMeta, + /// How the PoV should be measured. + PoV(PovModeAttr), +} + +impl syn::parse::Parse for PovModeAttr { + fn parse(input: ParseStream) -> Result { + let _pov: keywords::pov_mode = input.parse()?; + let _eq: Token![=] = input.parse()?; + let root = PovEstimationMode::parse(input)?; + + let mut maybe_content = None; + let _ = || -> Result<()> { + let content; + syn::braced!(content in input); + maybe_content = Some(content); + Ok(()) + }(); + + let per_key = match maybe_content { + Some(content) => { + let per_key = Punctuated::::parse_terminated(&content)?; + per_key.into_iter().collect() + }, + None => Vec::new(), + }; + + Ok(Self { root, per_key }) + } +} + +impl syn::parse::Parse for BenchmarkAttr { + fn parse(input: ParseStream) -> Result { + let lookahead = input.lookahead1(); + if lookahead.peek(keywords::extra) { + let _extra: keywords::extra = input.parse()?; + Ok(BenchmarkAttr::Extra) + } else if lookahead.peek(keywords::skip_meta) { + let _skip_meta: keywords::skip_meta = input.parse()?; + Ok(BenchmarkAttr::SkipMeta) + } else if lookahead.peek(keywords::pov_mode) { + PovModeAttr::parse(input).map(BenchmarkAttr::PoV) + } else { + Err(lookahead.error()) + } + } +} + +/// A `#[pov_mode = .. { .. }]` attribute. +#[derive(Debug, Clone)] +struct PovModeAttr { + /// The root mode for this benchmarks. + root: PovEstimationMode, + /// The pov-mode for a specific key. This overwrites `root` for this key. + per_key: Vec, +} + +/// A single key-value pair inside the `{}` of a `#[pov_mode = .. { .. }]` attribute. +#[derive(Debug, Clone, derive_syn_parse::Parse)] +struct PovModeKeyAttr { + /// A specific storage key for which to set the PoV mode. + key: Path, + _underscore: Token![:], + /// The PoV mode for this key. + mode: PovEstimationMode, +} + +/// How the PoV should be estimated. +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum PovEstimationMode { + /// Use the maximal encoded length as provided by [`codec::MaxEncodedLen`]. + MaxEncodedLen, + /// Measure the accessed value size in the pallet benchmarking and add some trie overhead. + Measured, + /// Do not estimate the PoV size for this storage item or benchmark. + Ignored, +} + +impl syn::parse::Parse for PovEstimationMode { + fn parse(input: ParseStream) -> Result { + let lookahead = input.lookahead1(); + if lookahead.peek(keywords::MaxEncodedLen) { + let _max_encoded_len: keywords::MaxEncodedLen = input.parse()?; + return Ok(PovEstimationMode::MaxEncodedLen) + } else if lookahead.peek(keywords::Measured) { + let _measured: keywords::Measured = input.parse()?; + return Ok(PovEstimationMode::Measured) + } else if lookahead.peek(keywords::Ignored) { + let _ignored: keywords::Ignored = input.parse()?; + return Ok(PovEstimationMode::Ignored) + } else { + return Err(lookahead.error()) + } + } +} + +impl ToString for PovEstimationMode { + fn to_string(&self) -> String { + match self { + PovEstimationMode::MaxEncodedLen => "MaxEncodedLen".into(), + PovEstimationMode::Measured => "Measured".into(), + PovEstimationMode::Ignored => "Ignored".into(), + } + } +} + +impl quote::ToTokens for PovEstimationMode { + fn to_tokens(&self, tokens: &mut TokenStream2) { + match self { + PovEstimationMode::MaxEncodedLen => tokens.extend(quote!(MaxEncodedLen)), + PovEstimationMode::Measured => tokens.extend(quote!(Measured)), + PovEstimationMode::Ignored => tokens.extend(quote!(Ignored)), + } + } +} + +impl syn::parse::Parse for BenchmarkAttrs { + fn parse(input: ParseStream) -> syn::Result { + let mut extra = false; + let mut skip_meta = false; + let mut pov_mode = None; + let args = Punctuated::::parse_terminated(&input)?; + + for arg in args.into_iter() { + match arg { + BenchmarkAttr::Extra => { + if extra { + return Err(input.error("`extra` can only be specified once")) + } + extra = true; + }, + BenchmarkAttr::SkipMeta => { + if skip_meta { + return Err(input.error("`skip_meta` can only be specified once")) + } + skip_meta = true; + }, + BenchmarkAttr::PoV(mode) => { + if pov_mode.is_some() { + return Err(input.error("`pov_mode` can only be specified once")) + } + pov_mode = Some(mode); + }, + } + } + Ok(BenchmarkAttrs { extra, skip_meta, pov_mode }) + } +} + +/// Represents the parsed extrinsic call for a benchmark +#[derive(Clone)] +enum BenchmarkCallDef { + ExtrinsicCall { origin: Expr, expr_call: ExprCall, attr_span: Span }, // #[extrinsic_call] + Block { block: ExprBlock, attr_span: Span }, // #[block] +} + +impl BenchmarkCallDef { + /// Returns the `span()` for attribute + fn attr_span(&self) -> Span { + match self { + BenchmarkCallDef::ExtrinsicCall { origin: _, expr_call: _, attr_span } => *attr_span, + BenchmarkCallDef::Block { block: _, attr_span } => *attr_span, + } + } +} + +/// Represents a parsed `#[benchmark]` or `#[instance_benchmark]` item. +#[derive(Clone)] +struct BenchmarkDef { + params: Vec, + setup_stmts: Vec, + call_def: BenchmarkCallDef, + verify_stmts: Vec, + last_stmt: Option, + fn_sig: Signature, + fn_vis: Visibility, + fn_attrs: Vec, +} + +/// used to parse something compatible with `Result` +#[derive(Parse)] +struct ResultDef { + _result_kw: keywords::Result, + _lt: Token![<], + unit: Type, + _comma: Comma, + e_type: TypePath, + _gt: Token![>], +} + +/// Ensures that `ReturnType` is a `Result<(), BenchmarkError>`, if specified +fn ensure_valid_return_type(item_fn: &ItemFn) -> Result<()> { + if let ReturnType::Type(_, typ) = &item_fn.sig.output { + let non_unit = |span| return Err(Error::new(span, "expected `()`")); + let Type::Path(TypePath { path, qself: _ }) = &**typ else { + return Err(Error::new( + typ.span(), + "Only `Result<(), BenchmarkError>` or a blank return type is allowed on benchmark function definitions", + )) + }; + let seg = path + .segments + .last() + .expect("to be parsed as a TypePath, it must have at least one segment; qed"); + let res: ResultDef = syn::parse2(seg.to_token_stream())?; + // ensure T in Result is () + let Type::Tuple(tup) = res.unit else { return non_unit(res.unit.span()) }; + if !tup.elems.is_empty() { + return non_unit(tup.span()) + } + let TypePath { path, qself: _ } = res.e_type; + let seg = path + .segments + .last() + .expect("to be parsed as a TypePath, it must have at least one segment; qed"); + syn::parse2::(seg.to_token_stream())?; + } + Ok(()) +} + +/// Parses params such as `x: Linear<0, 1>` +fn parse_params(item_fn: &ItemFn) -> Result> { + let mut params: Vec = Vec::new(); + for arg in &item_fn.sig.inputs { + let invalid_param = |span| { + return Err(Error::new( + span, + "Invalid benchmark function param. A valid example would be `x: Linear<5, 10>`.", + )) + }; + + let FnArg::Typed(arg) = arg else { return invalid_param(arg.span()) }; + let Pat::Ident(ident) = &*arg.pat else { return invalid_param(arg.span()) }; + + // check param name + let var_span = ident.span(); + let invalid_param_name = || { + return Err(Error::new( + var_span, + "Benchmark parameter names must consist of a single lowercase letter (a-z) and no other characters.", + )); + }; + let name = ident.ident.to_token_stream().to_string(); + if name.len() > 1 { + return invalid_param_name() + }; + let Some(name_char) = name.chars().next() else { return invalid_param_name() }; + if !name_char.is_alphabetic() || !name_char.is_lowercase() { + return invalid_param_name() + } + + // parse type + let typ = &*arg.ty; + let Type::Path(tpath) = typ else { return invalid_param(typ.span()) }; + let Some(segment) = tpath.path.segments.last() else { return invalid_param(typ.span()) }; + let args = segment.arguments.to_token_stream().into(); + let Ok(args) = syn::parse::(args) else { return invalid_param(typ.span()) }; + + params.push(ParamDef { name, _typ: typ.clone(), start: args.start, end: args.end }); + } + Ok(params) +} + +/// Used in several places where the `#[extrinsic_call]` or `#[body]` annotation is missing +fn missing_call(item_fn: &ItemFn) -> Result { + return Err(Error::new( + item_fn.block.brace_token.span.join(), + "No valid #[extrinsic_call] or #[block] annotation could be found in benchmark function body." + )); +} + +/// Finds the `BenchmarkCallDef` and its index (within the list of stmts for the fn) and +/// returns them. Also handles parsing errors for invalid / extra call defs. AKA this is +/// general handling for `#[extrinsic_call]` and `#[block]` +fn parse_call_def(item_fn: &ItemFn) -> Result<(usize, BenchmarkCallDef)> { + // #[extrinsic_call] / #[block] handling + let call_defs = item_fn.block.stmts.iter().enumerate().filter_map(|(i, child)| { + if let Stmt::Expr(Expr::Call(expr_call), _semi) = child { + // #[extrinsic_call] case + expr_call.attrs.iter().enumerate().find_map(|(k, attr)| { + let segment = attr.path().segments.last()?; + let _: keywords::extrinsic_call = syn::parse(segment.ident.to_token_stream().into()).ok()?; + let mut expr_call = expr_call.clone(); + + // consume #[extrinsic_call] tokens + expr_call.attrs.remove(k); + + // extract origin from expr_call + let Some(origin) = expr_call.args.first().cloned() else { + return Some(Err(Error::new(expr_call.span(), "Single-item extrinsic calls must specify their origin as the first argument."))) + }; + + Some(Ok((i, BenchmarkCallDef::ExtrinsicCall { origin, expr_call, attr_span: attr.span() }))) + }) + } else if let Stmt::Expr(Expr::Block(block), _) = child { + // #[block] case + block.attrs.iter().enumerate().find_map(|(k, attr)| { + let segment = attr.path().segments.last()?; + let _: keywords::block = syn::parse(segment.ident.to_token_stream().into()).ok()?; + let mut block = block.clone(); + + // consume #[block] tokens + block.attrs.remove(k); + + Some(Ok((i, BenchmarkCallDef::Block { block, attr_span: attr.span() }))) + }) + } else { + None + } + }).collect::>>()?; + Ok(match &call_defs[..] { + [(i, call_def)] => (*i, call_def.clone()), // = 1 + [] => return missing_call(item_fn), + _ => + return Err(Error::new( + call_defs[1].1.attr_span(), + "Only one #[extrinsic_call] or #[block] attribute is allowed per benchmark.", + )), + }) +} + +impl BenchmarkDef { + /// Constructs a [`BenchmarkDef`] by traversing an existing [`ItemFn`] node. + pub fn from(item_fn: &ItemFn) -> Result { + let params = parse_params(item_fn)?; + ensure_valid_return_type(item_fn)?; + let (i, call_def) = parse_call_def(&item_fn)?; + + let (verify_stmts, last_stmt) = match item_fn.sig.output { + ReturnType::Default => + // no return type, last_stmt should be None + (Vec::from(&item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len()]), None), + ReturnType::Type(_, _) => { + // defined return type, last_stmt should be Result<(), BenchmarkError> + // compatible and should not be included in verify_stmts + if i + 1 >= item_fn.block.stmts.len() { + return Err(Error::new( + item_fn.block.span(), + "Benchmark `#[block]` or `#[extrinsic_call]` item cannot be the \ + last statement of your benchmark function definition if you have \ + defined a return type. You should return something compatible \ + with Result<(), BenchmarkError> (i.e. `Ok(())`) as the last statement \ + or change your signature to a blank return type.", + )) + } + let Some(stmt) = item_fn.block.stmts.last() else { return missing_call(item_fn) }; + ( + Vec::from(&item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len() - 1]), + Some(stmt.clone()), + ) + }, + }; + + Ok(BenchmarkDef { + params, + setup_stmts: Vec::from(&item_fn.block.stmts[0..i]), + call_def, + verify_stmts, + last_stmt, + fn_sig: item_fn.sig.clone(), + fn_vis: item_fn.vis.clone(), + fn_attrs: item_fn.attrs.clone(), + }) + } +} + +/// Parses and expands a `#[benchmarks]` or `#[instance_benchmarks]` invocation +pub fn benchmarks( + attrs: TokenStream, + tokens: TokenStream, + instance: bool, +) -> syn::Result { + let krate = generate_access_from_frame_or_crate("frame-benchmarking")?; + // gather module info + let module: ItemMod = syn::parse(tokens)?; + let mod_span = module.span(); + let where_clause = match syn::parse::(attrs.clone()) { + Ok(_) => quote!(), + Err(_) => syn::parse::(attrs)?.predicates.to_token_stream(), + }; + let mod_vis = module.vis; + let mod_name = module.ident; + + // consume #[benchmarks] attribute by excluding it from mod_attrs + let mod_attrs: Vec<&Attribute> = module + .attrs + .iter() + .filter(|attr| !attr.path().is_ident(keywords::BENCHMARKS_TOKEN)) + .collect(); + + let mut benchmark_names: Vec = Vec::new(); + let mut extra_benchmark_names: Vec = Vec::new(); + let mut skip_meta_benchmark_names: Vec = Vec::new(); + // Map benchmarks to PoV modes. + let mut pov_modes = Vec::new(); + + let (_brace, mut content) = + module.content.ok_or(syn::Error::new(mod_span, "Module cannot be empty!"))?; + + // find all function defs marked with #[benchmark] + let benchmark_fn_metas = content.iter_mut().filter_map(|stmt| { + // parse as a function def first + let Item::Fn(func) = stmt else { return None }; + + // find #[benchmark] attribute on function def + let benchmark_attr = + func.attrs.iter().find(|attr| attr.path().is_ident(keywords::BENCHMARK_TOKEN))?; + + Some((benchmark_attr.clone(), func.clone(), stmt)) + }); + + // parse individual benchmark defs and args + for (benchmark_attr, func, stmt) in benchmark_fn_metas { + // parse benchmark def + let benchmark_def = BenchmarkDef::from(&func)?; + + // record benchmark name + let name = &func.sig.ident; + benchmark_names.push(name.clone()); + + // Check if we need to parse any args + if benchmark_attr.meta.require_path_only().is_err() { + // parse any args provided to #[benchmark] + let benchmark_attrs: BenchmarkAttrs = benchmark_attr.parse_args()?; + + // record name sets + if benchmark_attrs.extra { + extra_benchmark_names.push(name.clone()); + } else if benchmark_attrs.skip_meta { + skip_meta_benchmark_names.push(name.clone()); + } + + if let Some(mode) = benchmark_attrs.pov_mode { + let mut modes = Vec::new(); + // We cannot expand strings here since it is no-std, but syn does not expand bytes. + let name = name.to_string(); + let m = mode.root.to_string(); + modes.push(quote!(("ALL".as_bytes().to_vec(), #m.as_bytes().to_vec()))); + + for attr in mode.per_key.iter() { + // syn always puts spaces in quoted paths: + let key = attr.key.clone().into_token_stream().to_string().replace(" ", ""); + let mode = attr.mode.to_string(); + modes.push(quote!((#key.as_bytes().to_vec(), #mode.as_bytes().to_vec()))); + } + + pov_modes.push( + quote!((#name.as_bytes().to_vec(), #krate::__private::vec![#(#modes),*])), + ); + } + } + + // expand benchmark + let expanded = expand_benchmark(benchmark_def, name, instance, where_clause.clone()); + + // replace original function def with expanded code + *stmt = Item::Verbatim(expanded); + } + + // generics + let type_use_generics = match instance { + false => quote!(T), + true => quote!(T, I), + }; + let type_impl_generics = match instance { + false => quote!(T: Config), + true => quote!(T: Config, I: 'static), + }; + + let frame_system = generate_access_from_frame_or_crate("frame-system")?; + + // benchmark name variables + let benchmark_names_str: Vec = benchmark_names.iter().map(|n| n.to_string()).collect(); + let extra_benchmark_names_str: Vec = + extra_benchmark_names.iter().map(|n| n.to_string()).collect(); + let skip_meta_benchmark_names_str: Vec = + skip_meta_benchmark_names.iter().map(|n| n.to_string()).collect(); + let mut selected_benchmark_mappings: Vec = Vec::new(); + let mut benchmarks_by_name_mappings: Vec = Vec::new(); + let test_idents: Vec = benchmark_names_str + .iter() + .map(|n| Ident::new(format!("test_benchmark_{}", n).as_str(), Span::call_site())) + .collect(); + for i in 0..benchmark_names.len() { + let name_ident = &benchmark_names[i]; + let name_str = &benchmark_names_str[i]; + let test_ident = &test_idents[i]; + selected_benchmark_mappings.push(quote!(#name_str => SelectedBenchmark::#name_ident)); + benchmarks_by_name_mappings.push(quote!(#name_str => Self::#test_ident())) + } + + let impl_test_function = content + .iter_mut() + .find_map(|item| { + let Item::Macro(item_macro) = item else { + return None; + }; + + if !item_macro + .mac + .path + .segments + .iter() + .any(|s| s.ident == "impl_benchmark_test_suite") + { + return None; + } + + let tokens = item_macro.mac.tokens.clone(); + *item = Item::Verbatim(quote! {}); + + Some(quote! { + impl_test_function!( + (#( {} #benchmark_names )*) + (#( #extra_benchmark_names )*) + (#( #skip_meta_benchmark_names )*) + #tokens + ); + }) + }) + .unwrap_or(quote! {}); + + // emit final quoted tokens + let res = quote! { + #(#mod_attrs) + * + #mod_vis mod #mod_name { + #(#content) + * + + #[allow(non_camel_case_types)] + enum SelectedBenchmark { + #(#benchmark_names), + * + } + + impl<#type_impl_generics> #krate::BenchmarkingSetup<#type_use_generics> for SelectedBenchmark where #where_clause { + fn components(&self) -> #krate::__private::Vec<(#krate::BenchmarkParameter, u32, u32)> { + match self { + #( + Self::#benchmark_names => { + <#benchmark_names as #krate::BenchmarkingSetup<#type_use_generics>>::components(&#benchmark_names) + } + ) + * + } + } + + fn instance( + &self, + components: &[(#krate::BenchmarkParameter, u32)], + verify: bool, + ) -> Result< + #krate::__private::Box Result<(), #krate::BenchmarkError>>, + #krate::BenchmarkError, + > { + match self { + #( + Self::#benchmark_names => { + <#benchmark_names as #krate::BenchmarkingSetup< + #type_use_generics + >>::instance(&#benchmark_names, components, verify) + } + ) + * + } + } + } + #[cfg(any(feature = "runtime-benchmarks", test))] + impl<#type_impl_generics> #krate::Benchmarking for Pallet<#type_use_generics> + where T: #frame_system::Config, #where_clause + { + fn benchmarks( + extra: bool, + ) -> #krate::__private::Vec<#krate::BenchmarkMetadata> { + let mut all_names = #krate::__private::vec![ + #(#benchmark_names_str), + * + ]; + if !extra { + let extra = [ + #(#extra_benchmark_names_str), + * + ]; + all_names.retain(|x| !extra.contains(x)); + } + let pov_modes: + #krate::__private::Vec<( + #krate::__private::Vec, + #krate::__private::Vec<( + #krate::__private::Vec, + #krate::__private::Vec + )>, + )> = #krate::__private::vec![ + #( #pov_modes ),* + ]; + all_names.into_iter().map(|benchmark| { + let selected_benchmark = match benchmark { + #(#selected_benchmark_mappings), + *, + _ => panic!("all benchmarks should be selectable") + }; + let components = >::components(&selected_benchmark); + let name = benchmark.as_bytes().to_vec(); + let modes = pov_modes.iter().find(|p| p.0 == name).map(|p| p.1.clone()); + + #krate::BenchmarkMetadata { + name: benchmark.as_bytes().to_vec(), + components, + pov_modes: modes.unwrap_or_default(), + } + }).collect::<#krate::__private::Vec<_>>() + } + + fn run_benchmark( + extrinsic: &[u8], + c: &[(#krate::BenchmarkParameter, u32)], + whitelist: &[#krate::__private::TrackedStorageKey], + verify: bool, + internal_repeats: u32, + ) -> Result<#krate::__private::Vec<#krate::BenchmarkResult>, #krate::BenchmarkError> { + let extrinsic = #krate::__private::str::from_utf8(extrinsic).map_err(|_| "`extrinsic` is not a valid utf-8 string!")?; + let selected_benchmark = match extrinsic { + #(#selected_benchmark_mappings), + *, + _ => return Err("Could not find extrinsic.".into()), + }; + let mut whitelist = whitelist.to_vec(); + let whitelisted_caller_key = <#frame_system::Account< + T, + > as #krate::__private::storage::StorageMap<_, _,>>::hashed_key_for( + #krate::whitelisted_caller::() + ); + whitelist.push(whitelisted_caller_key.into()); + let transactional_layer_key = #krate::__private::TrackedStorageKey::new( + #krate::__private::storage::transactional::TRANSACTION_LEVEL_KEY.into(), + ); + whitelist.push(transactional_layer_key); + // Whitelist the `:extrinsic_index`. + let extrinsic_index = #krate::__private::TrackedStorageKey::new( + #krate::__private::well_known_keys::EXTRINSIC_INDEX.into() + ); + whitelist.push(extrinsic_index); + // Whitelist the `:intrablock_entropy`. + let intrablock_entropy = #krate::__private::TrackedStorageKey::new( + #krate::__private::well_known_keys::INTRABLOCK_ENTROPY.into() + ); + whitelist.push(intrablock_entropy); + + #krate::benchmarking::set_whitelist(whitelist.clone()); + let mut results: #krate::__private::Vec<#krate::BenchmarkResult> = #krate::__private::Vec::new(); + + // Always do at least one internal repeat... + for _ in 0 .. internal_repeats.max(1) { + // Always reset the state after the benchmark. + #krate::__private::defer!(#krate::benchmarking::wipe_db()); + + // Set up the externalities environment for the setup we want to + // benchmark. + let closure_to_benchmark = < + SelectedBenchmark as #krate::BenchmarkingSetup<#type_use_generics> + >::instance(&selected_benchmark, c, verify)?; + + // Set the block number to at least 1 so events are deposited. + if #krate::__private::Zero::is_zero(&#frame_system::Pallet::::block_number()) { + #frame_system::Pallet::::set_block_number(1u32.into()); + } + + // Commit the externalities to the database, flushing the DB cache. + // This will enable worst case scenario for reading from the database. + #krate::benchmarking::commit_db(); + + // Access all whitelisted keys to get them into the proof recorder since the + // recorder does now have a whitelist. + for key in &whitelist { + #krate::__private::storage::unhashed::get_raw(&key.key); + } + + // Reset the read/write counter so we don't count operations in the setup process. + #krate::benchmarking::reset_read_write_count(); + + // Time the extrinsic logic. + #krate::__private::log::trace!( + target: "benchmark", + "Start Benchmark: {} ({:?})", + extrinsic, + c + ); + + let start_pov = #krate::benchmarking::proof_size(); + let start_extrinsic = #krate::benchmarking::current_time(); + + closure_to_benchmark()?; + + let finish_extrinsic = #krate::benchmarking::current_time(); + let end_pov = #krate::benchmarking::proof_size(); + + // Calculate the diff caused by the benchmark. + let elapsed_extrinsic = finish_extrinsic.saturating_sub(start_extrinsic); + let diff_pov = match (start_pov, end_pov) { + (Some(start), Some(end)) => end.saturating_sub(start), + _ => Default::default(), + }; + + // Commit the changes to get proper write count + #krate::benchmarking::commit_db(); + #krate::__private::log::trace!( + target: "benchmark", + "End Benchmark: {} ns", elapsed_extrinsic + ); + let read_write_count = #krate::benchmarking::read_write_count(); + #krate::__private::log::trace!( + target: "benchmark", + "Read/Write Count {:?}", read_write_count + ); + + // Time the storage root recalculation. + let start_storage_root = #krate::benchmarking::current_time(); + #krate::__private::storage_root(#krate::__private::StateVersion::V1); + let finish_storage_root = #krate::benchmarking::current_time(); + let elapsed_storage_root = finish_storage_root - start_storage_root; + + let skip_meta = [ #(#skip_meta_benchmark_names_str),* ]; + let read_and_written_keys = if skip_meta.contains(&extrinsic) { + #krate::__private::vec![(b"Skipped Metadata".to_vec(), 0, 0, false)] + } else { + #krate::benchmarking::get_read_and_written_keys() + }; + + results.push(#krate::BenchmarkResult { + components: c.to_vec(), + extrinsic_time: elapsed_extrinsic, + storage_root_time: elapsed_storage_root, + reads: read_write_count.0, + repeat_reads: read_write_count.1, + writes: read_write_count.2, + repeat_writes: read_write_count.3, + proof_size: diff_pov, + keys: read_and_written_keys, + }); + } + + return Ok(results); + } + } + + #[cfg(test)] + impl<#type_impl_generics> Pallet<#type_use_generics> where T: #frame_system::Config, #where_clause { + /// Test a particular benchmark by name. + /// + /// This isn't called `test_benchmark_by_name` just in case some end-user eventually + /// writes a benchmark, itself called `by_name`; the function would be shadowed in + /// that case. + /// + /// This is generally intended to be used by child test modules such as those created + /// by the `impl_benchmark_test_suite` macro. However, it is not an error if a pallet + /// author chooses not to implement benchmarks. + #[allow(unused)] + fn test_bench_by_name(name: &[u8]) -> Result<(), #krate::BenchmarkError> { + let name = #krate::__private::str::from_utf8(name) + .map_err(|_| -> #krate::BenchmarkError { "`name` is not a valid utf8 string!".into() })?; + match name { + #(#benchmarks_by_name_mappings), + *, + _ => Err("Could not find test for requested benchmark.".into()), + } + } + } + + #impl_test_function + } + #mod_vis use #mod_name::*; + }; + Ok(res.into()) +} + +/// Prepares a [`Vec`] to be interpolated by [`quote!`] by creating easily-iterable +/// arrays formatted in such a way that they can be interpolated directly. +struct UnrolledParams { + param_ranges: Vec, + param_names: Vec, +} + +impl UnrolledParams { + /// Constructs an [`UnrolledParams`] from a [`Vec`] + fn from(params: &Vec) -> UnrolledParams { + let param_ranges: Vec = params + .iter() + .map(|p| { + let name = Ident::new(&p.name, Span::call_site()); + let start = &p.start; + let end = &p.end; + quote!(#name, #start, #end) + }) + .collect(); + let param_names: Vec = params + .iter() + .map(|p| { + let name = Ident::new(&p.name, Span::call_site()); + quote!(#name) + }) + .collect(); + UnrolledParams { param_ranges, param_names } + } +} + +/// Performs expansion of an already-parsed [`BenchmarkDef`]. +fn expand_benchmark( + benchmark_def: BenchmarkDef, + name: &Ident, + is_instance: bool, + where_clause: TokenStream2, +) -> TokenStream2 { + // set up variables needed during quoting + let krate = match generate_access_from_frame_or_crate("frame-benchmarking") { + Ok(ident) => ident, + Err(err) => return err.to_compile_error().into(), + }; + let frame_system = match generate_access_from_frame_or_crate("frame-system") { + Ok(path) => path, + Err(err) => return err.to_compile_error().into(), + }; + let codec = quote!(#krate::__private::codec); + let traits = quote!(#krate::__private::traits); + let setup_stmts = benchmark_def.setup_stmts; + let verify_stmts = benchmark_def.verify_stmts; + let last_stmt = benchmark_def.last_stmt; + let test_ident = + Ident::new(format!("test_benchmark_{}", name.to_string()).as_str(), Span::call_site()); + + // unroll params (prepare for quoting) + let unrolled = UnrolledParams::from(&benchmark_def.params); + let param_names = unrolled.param_names; + let param_ranges = unrolled.param_ranges; + + let type_use_generics = match is_instance { + false => quote!(T), + true => quote!(T, I), + }; + + let type_impl_generics = match is_instance { + false => quote!(T: Config), + true => quote!(T: Config, I: 'static), + }; + + // used in the benchmarking impls + let (pre_call, post_call, fn_call_body) = match &benchmark_def.call_def { + BenchmarkCallDef::ExtrinsicCall { origin, expr_call, attr_span: _ } => { + let mut expr_call = expr_call.clone(); + + // remove first arg from expr_call + let mut final_args = Punctuated::::new(); + let args: Vec<&Expr> = expr_call.args.iter().collect(); + for arg in &args[1..] { + final_args.push((*(*arg)).clone()); + } + expr_call.args = final_args; + + let origin = match origin { + Expr::Cast(t) => { + let ty = t.ty.clone(); + quote! { + <::RuntimeOrigin as From<#ty>>::from(#origin); + } + }, + _ => quote! { + #origin.into(); + }, + }; + + // determine call name (handles `_` and normal call syntax) + let expr_span = expr_call.span(); + let call_err = || { + syn::Error::new(expr_span, "Extrinsic call must be a function call or `_`") + .to_compile_error() + }; + let call_name = match *expr_call.func { + Expr::Path(expr_path) => { + // normal function call + let Some(segment) = expr_path.path.segments.last() else { return call_err() }; + segment.ident.to_string() + }, + Expr::Infer(_) => { + // `_` style + // replace `_` with fn name + name.to_string() + }, + _ => return call_err(), + }; + + // modify extrinsic call to be prefixed with "new_call_variant" + let call_name = format!("new_call_variant_{}", call_name); + let mut punct: Punctuated = Punctuated::new(); + punct.push(PathSegment { + arguments: PathArguments::None, + ident: Ident::new(call_name.as_str(), Span::call_site()), + }); + *expr_call.func = Expr::Path(ExprPath { + attrs: vec![], + qself: None, + path: Path { leading_colon: None, segments: punct }, + }); + let pre_call = quote! { + let __call = Call::<#type_use_generics>::#expr_call; + let __benchmarked_call_encoded = #codec::Encode::encode(&__call); + }; + let post_call = quote! { + let __call_decoded = as #codec::Decode> + ::decode(&mut &__benchmarked_call_encoded[..]) + .expect("call is encoded above, encoding must be correct"); + let __origin = #origin; + as #traits::UnfilteredDispatchable>::dispatch_bypass_filter( + __call_decoded, + __origin, + ) + }; + ( + // (pre_call, post_call, fn_call_body): + pre_call.clone(), + quote!(#post_call?;), + quote! { + #pre_call + #post_call.unwrap(); + }, + ) + }, + BenchmarkCallDef::Block { block, attr_span: _ } => + (quote!(), quote!(#block), quote!(#block)), + }; + + let vis = benchmark_def.fn_vis; + + // remove #[benchmark] attribute + let fn_attrs = benchmark_def + .fn_attrs + .iter() + .filter(|attr| !attr.path().is_ident(keywords::BENCHMARK_TOKEN)); + + // modify signature generics, ident, and inputs, e.g: + // before: `fn bench(u: Linear<1, 100>) -> Result<(), BenchmarkError>` + // after: `fn _bench , I: 'static>(u: u32, verify: bool) -> Result<(), + // BenchmarkError>` + let mut sig = benchmark_def.fn_sig; + sig.generics = parse_quote!(<#type_impl_generics>); + if !where_clause.is_empty() { + sig.generics.where_clause = parse_quote!(where #where_clause); + } + sig.ident = + Ident::new(format!("_{}", name.to_token_stream().to_string()).as_str(), Span::call_site()); + let mut fn_param_inputs: Vec = + param_names.iter().map(|name| quote!(#name: u32)).collect(); + fn_param_inputs.push(quote!(verify: bool)); + sig.inputs = parse_quote!(#(#fn_param_inputs),*); + + // used in instance() impl + let impl_last_stmt = match &last_stmt { + Some(stmt) => quote!(#stmt), + None => quote!(Ok(())), + }; + let fn_attrs_clone = fn_attrs.clone(); + + let fn_def = quote! { + #( + #fn_attrs_clone + )* + #vis #sig { + #( + #setup_stmts + )* + #fn_call_body + if verify { + #( + #verify_stmts + )* + } + #last_stmt + } + }; + + // generate final quoted tokens + let res = quote! { + // benchmark function definition + #fn_def + + #[allow(non_camel_case_types)] + #( + #fn_attrs + )* + struct #name; + + #[allow(unused_variables)] + impl<#type_impl_generics> #krate::BenchmarkingSetup<#type_use_generics> + for #name where #where_clause { + fn components(&self) -> #krate::__private::Vec<(#krate::BenchmarkParameter, u32, u32)> { + #krate::__private::vec! [ + #( + (#krate::BenchmarkParameter::#param_ranges) + ),* + ] + } + + fn instance( + &self, + components: &[(#krate::BenchmarkParameter, u32)], + verify: bool + ) -> Result<#krate::__private::Box Result<(), #krate::BenchmarkError>>, #krate::BenchmarkError> { + #( + // prepare instance #param_names + let #param_names = components.iter() + .find(|&c| c.0 == #krate::BenchmarkParameter::#param_names) + .ok_or("Could not find component during benchmark preparation.")? + .1; + )* + + // benchmark setup code + #( + #setup_stmts + )* + #pre_call + Ok(#krate::__private::Box::new(move || -> Result<(), #krate::BenchmarkError> { + #post_call + if verify { + #( + #verify_stmts + )* + } + #impl_last_stmt + })) + } + } + + #[cfg(test)] + impl<#type_impl_generics> Pallet<#type_use_generics> where T: #frame_system::Config, #where_clause { + #[allow(unused)] + fn #test_ident() -> Result<(), #krate::BenchmarkError> { + let selected_benchmark = SelectedBenchmark::#name; + let components = < + SelectedBenchmark as #krate::BenchmarkingSetup + >::components(&selected_benchmark); + let execute_benchmark = | + c: #krate::__private::Vec<(#krate::BenchmarkParameter, u32)> + | -> Result<(), #krate::BenchmarkError> { + // Always reset the state after the benchmark. + #krate::__private::defer!(#krate::benchmarking::wipe_db()); + + // Set up the benchmark, return execution + verification function. + let closure_to_verify = < + SelectedBenchmark as #krate::BenchmarkingSetup + >::instance(&selected_benchmark, &c, true)?; + + // Set the block number to at least 1 so events are deposited. + if #krate::__private::Zero::is_zero(&#frame_system::Pallet::::block_number()) { + #frame_system::Pallet::::set_block_number(1u32.into()); + } + + // Run execution + verification + closure_to_verify() + }; + + if components.is_empty() { + execute_benchmark(Default::default())?; + } else { + let num_values: u32 = if let Ok(ev) = std::env::var("VALUES_PER_COMPONENT") { + ev.parse().map_err(|_| { + #krate::BenchmarkError::Stop( + "Could not parse env var `VALUES_PER_COMPONENT` as u32." + ) + })? + } else { + 6 + }; + + if num_values < 2 { + return Err("`VALUES_PER_COMPONENT` must be at least 2".into()); + } + + for (name, low, high) in components.clone().into_iter() { + // Test the lowest, highest (if its different from the lowest) + // and up to num_values-2 more equidistant values in between. + // For 0..10 and num_values=6 this would mean: [0, 2, 4, 6, 8, 10] + if high < low { + return Err("The start of a `ParamRange` must be less than or equal to the end".into()); + } + + let mut values = #krate::__private::vec![low]; + let diff = (high - low).min(num_values - 1); + let slope = (high - low) as f32 / diff as f32; + + for i in 1..=diff { + let value = ((low as f32 + slope * i as f32) as u32) + .clamp(low, high); + values.push(value); + } + + for component_value in values { + // Select the max value for all the other components. + let c: #krate::__private::Vec<(#krate::BenchmarkParameter, u32)> = components + .iter() + .map(|(n, _, h)| + if *n == name { + (*n, component_value) + } else { + (*n, *h) + } + ) + .collect(); + + execute_benchmark(c)?; + } + } + } + return Ok(()); + } + } + }; + res +} diff --git a/support/procedural-fork/src/construct_runtime/expand/call.rs b/support/procedural-fork/src/construct_runtime/expand/call.rs new file mode 100644 index 000000000..b0041ccc0 --- /dev/null +++ b/support/procedural-fork/src/construct_runtime/expand/call.rs @@ -0,0 +1,223 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +use crate::construct_runtime::Pallet; +use proc_macro2::TokenStream; +use quote::quote; +use std::str::FromStr; +use syn::Ident; + +pub fn expand_outer_dispatch( + runtime: &Ident, + system_pallet: &Pallet, + pallet_decls: &[Pallet], + scrate: &TokenStream, +) -> TokenStream { + let mut variant_defs = TokenStream::new(); + let mut variant_patterns = Vec::new(); + let mut query_call_part_macros = Vec::new(); + let mut pallet_names = Vec::new(); + let mut pallet_attrs = Vec::new(); + let system_path = &system_pallet.path; + + let pallets_with_call = pallet_decls.iter().filter(|decl| decl.exists_part("Call")); + + for pallet_declaration in pallets_with_call { + let name = &pallet_declaration.name; + let path = &pallet_declaration.path; + let index = pallet_declaration.index; + let attr = + pallet_declaration.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); + + variant_defs.extend(quote! { + #attr + #[codec(index = #index)] + #name( #scrate::dispatch::CallableCallFor<#name, #runtime> ), + }); + variant_patterns.push(quote!(RuntimeCall::#name(call))); + pallet_names.push(name); + pallet_attrs.push(attr); + query_call_part_macros.push(quote! { + #path::__substrate_call_check::is_call_part_defined!(#name); + }); + } + + quote! { + #( #query_call_part_macros )* + + #[derive( + Clone, PartialEq, Eq, + #scrate::__private::codec::Encode, + #scrate::__private::codec::Decode, + #scrate::__private::scale_info::TypeInfo, + #scrate::__private::RuntimeDebug, + )] + pub enum RuntimeCall { + #variant_defs + } + #[cfg(test)] + impl RuntimeCall { + /// Return a list of the module names together with their size in memory. + pub const fn sizes() -> &'static [( &'static str, usize )] { + use #scrate::dispatch::Callable; + use core::mem::size_of; + &[#( + #pallet_attrs + ( + stringify!(#pallet_names), + size_of::< <#pallet_names as Callable<#runtime>>::RuntimeCall >(), + ), + )*] + } + + /// Panics with diagnostic information if the size is greater than the given `limit`. + pub fn assert_size_under(limit: usize) { + let size = core::mem::size_of::(); + let call_oversize = size > limit; + if call_oversize { + println!("Size of `Call` is {} bytes (provided limit is {} bytes)", size, limit); + let mut sizes = Self::sizes().to_vec(); + sizes.sort_by_key(|x| -(x.1 as isize)); + for (i, &(name, size)) in sizes.iter().enumerate().take(5) { + println!("Offender #{}: {} at {} bytes", i + 1, name, size); + } + if let Some((_, next_size)) = sizes.get(5) { + println!("{} others of size {} bytes or less", sizes.len() - 5, next_size); + } + panic!( + "Size of `Call` is more than limit; use `Box` on complex parameter types to reduce the + size of `Call`. + If the limit is too strong, maybe consider providing a higher limit." + ); + } + } + } + impl #scrate::dispatch::GetDispatchInfo for RuntimeCall { + fn get_dispatch_info(&self) -> #scrate::dispatch::DispatchInfo { + match self { + #( + #pallet_attrs + #variant_patterns => call.get_dispatch_info(), + )* + } + } + } + + impl #scrate::dispatch::CheckIfFeeless for RuntimeCall { + type Origin = #system_path::pallet_prelude::OriginFor<#runtime>; + fn is_feeless(&self, origin: &Self::Origin) -> bool { + match self { + #( + #pallet_attrs + #variant_patterns => call.is_feeless(origin), + )* + } + } + } + + impl #scrate::traits::GetCallMetadata for RuntimeCall { + fn get_call_metadata(&self) -> #scrate::traits::CallMetadata { + use #scrate::traits::GetCallName; + match self { + #( + #pallet_attrs + #variant_patterns => { + let function_name = call.get_call_name(); + let pallet_name = stringify!(#pallet_names); + #scrate::traits::CallMetadata { function_name, pallet_name } + } + )* + } + } + + fn get_module_names() -> &'static [&'static str] { + &[#( + #pallet_attrs + stringify!(#pallet_names), + )*] + } + + fn get_call_names(module: &str) -> &'static [&'static str] { + use #scrate::{dispatch::Callable, traits::GetCallName}; + match module { + #( + #pallet_attrs + stringify!(#pallet_names) => + <<#pallet_names as Callable<#runtime>>::RuntimeCall + as GetCallName>::get_call_names(), + )* + _ => unreachable!(), + } + } + } + impl #scrate::__private::Dispatchable for RuntimeCall { + type RuntimeOrigin = RuntimeOrigin; + type Config = RuntimeCall; + type Info = #scrate::dispatch::DispatchInfo; + type PostInfo = #scrate::dispatch::PostDispatchInfo; + fn dispatch(self, origin: RuntimeOrigin) -> #scrate::dispatch::DispatchResultWithPostInfo { + if !::filter_call(&origin, &self) { + return ::core::result::Result::Err( + #system_path::Error::<#runtime>::CallFiltered.into() + ); + } + + #scrate::traits::UnfilteredDispatchable::dispatch_bypass_filter(self, origin) + } + } + impl #scrate::traits::UnfilteredDispatchable for RuntimeCall { + type RuntimeOrigin = RuntimeOrigin; + fn dispatch_bypass_filter(self, origin: RuntimeOrigin) -> #scrate::dispatch::DispatchResultWithPostInfo { + match self { + #( + #pallet_attrs + #variant_patterns => + #scrate::traits::UnfilteredDispatchable::dispatch_bypass_filter(call, origin), + )* + } + } + } + + #( + #pallet_attrs + impl #scrate::traits::IsSubType<#scrate::dispatch::CallableCallFor<#pallet_names, #runtime>> for RuntimeCall { + #[allow(unreachable_patterns)] + fn is_sub_type(&self) -> Option<&#scrate::dispatch::CallableCallFor<#pallet_names, #runtime>> { + match self { + #variant_patterns => Some(call), + // May be unreachable + _ => None, + } + } + } + + #pallet_attrs + impl From<#scrate::dispatch::CallableCallFor<#pallet_names, #runtime>> for RuntimeCall { + fn from(call: #scrate::dispatch::CallableCallFor<#pallet_names, #runtime>) -> Self { + #variant_patterns + } + } + )* + } +} diff --git a/support/procedural-fork/src/construct_runtime/expand/composite_helper.rs b/support/procedural-fork/src/construct_runtime/expand/composite_helper.rs new file mode 100644 index 000000000..101a476fb --- /dev/null +++ b/support/procedural-fork/src/construct_runtime/expand/composite_helper.rs @@ -0,0 +1,101 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +use crate::construct_runtime::parse::PalletPath; +use proc_macro2::{Ident, TokenStream}; +use quote::quote; + +pub(crate) fn expand_conversion_fn( + composite_name: &str, + path: &PalletPath, + instance: Option<&Ident>, + variant_name: &Ident, +) -> TokenStream { + let composite_name = quote::format_ident!("{}", composite_name); + let runtime_composite_name = quote::format_ident!("Runtime{}", composite_name); + + if let Some(inst) = instance { + quote! { + impl From<#path::#composite_name<#path::#inst>> for #runtime_composite_name { + fn from(hr: #path::#composite_name<#path::#inst>) -> Self { + #runtime_composite_name::#variant_name(hr) + } + } + } + } else { + quote! { + impl From<#path::#composite_name> for #runtime_composite_name { + fn from(hr: #path::#composite_name) -> Self { + #runtime_composite_name::#variant_name(hr) + } + } + } + } +} + +pub(crate) fn expand_variant( + composite_name: &str, + index: u8, + path: &PalletPath, + instance: Option<&Ident>, + variant_name: &Ident, +) -> TokenStream { + let composite_name = quote::format_ident!("{}", composite_name); + + if let Some(inst) = instance { + quote! { + #[codec(index = #index)] + #variant_name(#path::#composite_name<#path::#inst>), + } + } else { + quote! { + #[codec(index = #index)] + #variant_name(#path::#composite_name), + } + } +} + +pub(crate) fn expand_variant_count( + composite_name: &str, + path: &PalletPath, + instance: Option<&Ident>, +) -> TokenStream { + let composite_name = quote::format_ident!("{}", composite_name); + + if let Some(inst) = instance { + quote! { + #path::#composite_name::<#path::#inst>::VARIANT_COUNT + } + } else { + // Wrapped `<`..`>` means: use default type parameter for enum. + // + // This is used for pallets without instance support or pallets with instance support when + // we don't specify instance: + // + // ``` + // pub struct Pallet{..} + // + // #[pallet::composite_enum] + // pub enum HoldReason {..} + // + // Pallet1: pallet_x, // <- default type parameter + // ``` + quote! { + <#path::#composite_name>::VARIANT_COUNT + } + } +} diff --git a/support/procedural-fork/src/construct_runtime/expand/config.rs b/support/procedural-fork/src/construct_runtime/expand/config.rs new file mode 100644 index 000000000..dbbe6ba6e --- /dev/null +++ b/support/procedural-fork/src/construct_runtime/expand/config.rs @@ -0,0 +1,147 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +use crate::construct_runtime::Pallet; +use inflector::Inflector; +use proc_macro2::TokenStream; +use quote::{format_ident, quote, ToTokens}; +use std::str::FromStr; +use syn::Ident; + +pub fn expand_outer_config( + runtime: &Ident, + pallet_decls: &[Pallet], + scrate: &TokenStream, +) -> TokenStream { + let mut types = TokenStream::new(); + let mut fields = TokenStream::new(); + let mut genesis_build_calls = TokenStream::new(); + let mut query_genesis_config_part_macros = Vec::new(); + + for decl in pallet_decls { + if let Some(pallet_entry) = decl.find_part("Config") { + let path = &decl.path; + let pallet_name = &decl.name; + let path_str = path.into_token_stream().to_string(); + let config = format_ident!("{}Config", pallet_name); + let field_name = + &Ident::new(&pallet_name.to_string().to_snake_case(), decl.name.span()); + let part_is_generic = !pallet_entry.generics.params.is_empty(); + let attr = &decl.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); + + types.extend(expand_config_types(attr, runtime, decl, &config, part_is_generic)); + fields.extend(quote!(#attr pub #field_name: #config,)); + genesis_build_calls + .extend(expand_config_build_storage_call(scrate, &config, attr, field_name)); + query_genesis_config_part_macros.push(quote! { + #path::__substrate_genesis_config_check::is_genesis_config_defined!(#pallet_name); + #[cfg(feature = "std")] + #path::__substrate_genesis_config_check::is_std_enabled_for_genesis!(#pallet_name, #path_str); + }); + } + } + + quote! { + #( #query_genesis_config_part_macros )* + + #types + + use #scrate::__private::serde as __genesis_config_serde_import__; + #[derive(#scrate::__private::serde::Serialize, #scrate::__private::serde::Deserialize, Default)] + #[serde(rename_all = "camelCase")] + #[serde(deny_unknown_fields)] + #[serde(crate = "__genesis_config_serde_import__")] + pub struct RuntimeGenesisConfig { + #fields + } + + #[cfg(any(feature = "std", test))] + impl #scrate::sp_runtime::BuildStorage for RuntimeGenesisConfig { + fn assimilate_storage( + &self, + storage: &mut #scrate::sp_runtime::Storage, + ) -> std::result::Result<(), String> { + #scrate::__private::BasicExternalities::execute_with_storage(storage, || { + ::build(&self); + Ok(()) + }) + } + } + + impl #scrate::traits::BuildGenesisConfig for RuntimeGenesisConfig { + fn build(&self) { + #genesis_build_calls + ::on_genesis(); + } + } + + /// Test the `Default` derive impl of the `RuntimeGenesisConfig`. + #[cfg(test)] + #[test] + fn test_genesis_config_builds() { + #scrate::__private::sp_io::TestExternalities::default().execute_with(|| { + ::build( + &RuntimeGenesisConfig::default() + ); + }); + } + } +} + +fn expand_config_types( + attr: &TokenStream, + runtime: &Ident, + decl: &Pallet, + config: &Ident, + part_is_generic: bool, +) -> TokenStream { + let path = &decl.path; + + match (decl.instance.as_ref(), part_is_generic) { + (Some(inst), true) => quote! { + #attr + pub type #config = #path::GenesisConfig<#runtime, #path::#inst>; + }, + (None, true) => quote! { + #attr + pub type #config = #path::GenesisConfig<#runtime>; + }, + (_, false) => quote! { + #attr + pub type #config = #path::GenesisConfig; + }, + } +} + +fn expand_config_build_storage_call( + scrate: &TokenStream, + pallet_genesis_config: &Ident, + attr: &TokenStream, + field_name: &Ident, +) -> TokenStream { + quote! { + #attr + <#pallet_genesis_config as #scrate::traits::BuildGenesisConfig>::build(&self.#field_name); + } +} diff --git a/support/procedural-fork/src/construct_runtime/expand/freeze_reason.rs b/support/procedural-fork/src/construct_runtime/expand/freeze_reason.rs new file mode 100644 index 000000000..f12f99526 --- /dev/null +++ b/support/procedural-fork/src/construct_runtime/expand/freeze_reason.rs @@ -0,0 +1,75 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +use super::composite_helper; +use crate::construct_runtime::Pallet; +use proc_macro2::TokenStream; +use quote::quote; + +pub fn expand_outer_freeze_reason(pallet_decls: &[Pallet], scrate: &TokenStream) -> TokenStream { + let mut conversion_fns = Vec::new(); + let mut freeze_reason_variants = Vec::new(); + let mut freeze_reason_variants_count = Vec::new(); + for decl in pallet_decls { + if let Some(_) = decl.find_part("FreezeReason") { + let variant_name = &decl.name; + let path = &decl.path; + let index = decl.index; + let instance = decl.instance.as_ref(); + + conversion_fns.push(composite_helper::expand_conversion_fn( + "FreezeReason", + path, + instance, + variant_name, + )); + + freeze_reason_variants.push(composite_helper::expand_variant( + "FreezeReason", + index, + path, + instance, + variant_name, + )); + + freeze_reason_variants_count.push(composite_helper::expand_variant_count( + "FreezeReason", + path, + instance, + )); + } + } + + quote! { + /// A reason for placing a freeze on funds. + #[derive( + Copy, Clone, Eq, PartialEq, + #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, + #scrate::__private::scale_info::TypeInfo, + #scrate::__private::RuntimeDebug, + )] + pub enum RuntimeFreezeReason { + #( #freeze_reason_variants )* + } + + impl #scrate::traits::VariantCount for RuntimeFreezeReason { + const VARIANT_COUNT: u32 = 0 #( + #freeze_reason_variants_count )*; + } + + #( #conversion_fns )* + } +} diff --git a/support/procedural-fork/src/construct_runtime/expand/hold_reason.rs b/support/procedural-fork/src/construct_runtime/expand/hold_reason.rs new file mode 100644 index 000000000..cdab92712 --- /dev/null +++ b/support/procedural-fork/src/construct_runtime/expand/hold_reason.rs @@ -0,0 +1,75 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +use super::composite_helper; +use crate::construct_runtime::Pallet; +use proc_macro2::TokenStream; +use quote::quote; + +pub fn expand_outer_hold_reason(pallet_decls: &[Pallet], scrate: &TokenStream) -> TokenStream { + let mut conversion_fns = Vec::new(); + let mut hold_reason_variants = Vec::new(); + let mut hold_reason_variants_count = Vec::new(); + for decl in pallet_decls { + if let Some(_) = decl.find_part("HoldReason") { + let variant_name = &decl.name; + let path = &decl.path; + let index = decl.index; + let instance = decl.instance.as_ref(); + + conversion_fns.push(composite_helper::expand_conversion_fn( + "HoldReason", + path, + instance, + variant_name, + )); + + hold_reason_variants.push(composite_helper::expand_variant( + "HoldReason", + index, + path, + instance, + variant_name, + )); + + hold_reason_variants_count.push(composite_helper::expand_variant_count( + "HoldReason", + path, + instance, + )); + } + } + + quote! { + /// A reason for placing a hold on funds. + #[derive( + Copy, Clone, Eq, PartialEq, + #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, + #scrate::__private::scale_info::TypeInfo, + #scrate::__private::RuntimeDebug, + )] + pub enum RuntimeHoldReason { + #( #hold_reason_variants )* + } + + impl #scrate::traits::VariantCount for RuntimeHoldReason { + const VARIANT_COUNT: u32 = 0 #( + #hold_reason_variants_count )*; + } + + #( #conversion_fns )* + } +} diff --git a/support/procedural-fork/src/construct_runtime/expand/inherent.rs b/support/procedural-fork/src/construct_runtime/expand/inherent.rs new file mode 100644 index 000000000..da483fa6c --- /dev/null +++ b/support/procedural-fork/src/construct_runtime/expand/inherent.rs @@ -0,0 +1,254 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +use crate::construct_runtime::Pallet; +use proc_macro2::TokenStream; +use quote::quote; +use std::str::FromStr; +use syn::Ident; + +pub fn expand_outer_inherent( + runtime: &Ident, + block: &TokenStream, + unchecked_extrinsic: &TokenStream, + pallet_decls: &[Pallet], + scrate: &TokenStream, +) -> TokenStream { + let mut pallet_names = Vec::new(); + let mut pallet_attrs = Vec::new(); + let mut query_inherent_part_macros = Vec::new(); + + for pallet_decl in pallet_decls { + if pallet_decl.exists_part("Inherent") { + let name = &pallet_decl.name; + let path = &pallet_decl.path; + let attr = pallet_decl.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); + + pallet_names.push(name); + pallet_attrs.push(attr); + query_inherent_part_macros.push(quote! { + #path::__substrate_inherent_check::is_inherent_part_defined!(#name); + }); + } + } + + quote! { + #( #query_inherent_part_macros )* + + trait InherentDataExt { + fn create_extrinsics(&self) -> + #scrate::__private::sp_std::vec::Vec<<#block as #scrate::sp_runtime::traits::Block>::Extrinsic>; + fn check_extrinsics(&self, block: &#block) -> #scrate::inherent::CheckInherentsResult; + } + + impl InherentDataExt for #scrate::inherent::InherentData { + fn create_extrinsics(&self) -> + #scrate::__private::sp_std::vec::Vec<<#block as #scrate::sp_runtime::traits::Block>::Extrinsic> + { + use #scrate::inherent::ProvideInherent; + + let mut inherents = #scrate::__private::sp_std::vec::Vec::new(); + + #( + #pallet_attrs + if let Some(inherent) = #pallet_names::create_inherent(self) { + let inherent = <#unchecked_extrinsic as #scrate::sp_runtime::traits::Extrinsic>::new( + inherent.into(), + None, + ).expect("Runtime UncheckedExtrinsic is not Opaque, so it has to return \ + `Some`; qed"); + + inherents.push(inherent); + } + )* + + inherents + } + + fn check_extrinsics(&self, block: &#block) -> #scrate::inherent::CheckInherentsResult { + use #scrate::inherent::{ProvideInherent, IsFatalError}; + use #scrate::traits::{IsSubType, ExtrinsicCall}; + use #scrate::sp_runtime::traits::Block as _; + use #scrate::__private::{sp_inherents::Error, log}; + + let mut result = #scrate::inherent::CheckInherentsResult::new(); + + // This handle assume we abort on the first fatal error. + fn handle_put_error_result(res: Result<(), Error>) { + const LOG_TARGET: &str = "runtime::inherent"; + match res { + Ok(()) => (), + Err(Error::InherentDataExists(id)) => + log::debug!( + target: LOG_TARGET, + "Some error already reported for inherent {:?}, new non fatal \ + error is ignored", + id + ), + Err(Error::FatalErrorReported) => + log::error!( + target: LOG_TARGET, + "Fatal error already reported, unexpected considering there is \ + only one fatal error", + ), + Err(_) => + log::error!( + target: LOG_TARGET, + "Unexpected error from `put_error` operation", + ), + } + } + + for xt in block.extrinsics() { + // Inherents are before any other extrinsics. + // And signed extrinsics are not inherents. + if #scrate::sp_runtime::traits::Extrinsic::is_signed(xt).unwrap_or(false) { + break + } + + let mut is_inherent = false; + + #( + #pallet_attrs + { + let call = <#unchecked_extrinsic as ExtrinsicCall>::call(xt); + if let Some(call) = IsSubType::<_>::is_sub_type(call) { + if #pallet_names::is_inherent(call) { + is_inherent = true; + if let Err(e) = #pallet_names::check_inherent(call, self) { + handle_put_error_result(result.put_error( + #pallet_names::INHERENT_IDENTIFIER, &e + )); + if e.is_fatal_error() { + return result; + } + } + } + } + } + )* + + // Inherents are before any other extrinsics. + // No module marked it as inherent thus it is not. + if !is_inherent { + break + } + } + + #( + #pallet_attrs + match #pallet_names::is_inherent_required(self) { + Ok(Some(e)) => { + let found = block.extrinsics().iter().any(|xt| { + let is_signed = #scrate::sp_runtime::traits::Extrinsic::is_signed(xt) + .unwrap_or(false); + + if !is_signed { + let call = < + #unchecked_extrinsic as ExtrinsicCall + >::call(xt); + if let Some(call) = IsSubType::<_>::is_sub_type(call) { + #pallet_names::is_inherent(&call) + } else { + false + } + } else { + // Signed extrinsics are not inherents. + false + } + }); + + if !found { + handle_put_error_result(result.put_error( + #pallet_names::INHERENT_IDENTIFIER, &e + )); + if e.is_fatal_error() { + return result; + } + } + }, + Ok(None) => (), + Err(e) => { + handle_put_error_result(result.put_error( + #pallet_names::INHERENT_IDENTIFIER, &e + )); + if e.is_fatal_error() { + return result; + } + }, + } + )* + + result + } + } + + impl #scrate::traits::IsInherent<<#block as #scrate::sp_runtime::traits::Block>::Extrinsic> for #runtime { + fn is_inherent(ext: &<#block as #scrate::sp_runtime::traits::Block>::Extrinsic) -> bool { + use #scrate::inherent::ProvideInherent; + use #scrate::traits::{IsSubType, ExtrinsicCall}; + + if #scrate::sp_runtime::traits::Extrinsic::is_signed(ext).unwrap_or(false) { + // Signed extrinsics are never inherents. + return false + } + + #( + #pallet_attrs + { + let call = <#unchecked_extrinsic as ExtrinsicCall>::call(ext); + if let Some(call) = IsSubType::<_>::is_sub_type(call) { + if <#pallet_names as ProvideInherent>::is_inherent(&call) { + return true; + } + } + } + )* + false + } + } + + impl #scrate::traits::EnsureInherentsAreFirst<#block> for #runtime { + fn ensure_inherents_are_first(block: &#block) -> Result { + use #scrate::inherent::ProvideInherent; + use #scrate::traits::{IsSubType, ExtrinsicCall}; + use #scrate::sp_runtime::traits::Block as _; + + let mut num_inherents = 0u32; + + for (i, xt) in block.extrinsics().iter().enumerate() { + if >::is_inherent(xt) { + if num_inherents != i as u32 { + return Err(i as u32); + } + + num_inherents += 1; // Safe since we are in an `enumerate` loop. + } + } + + Ok(num_inherents) + } + } + } +} diff --git a/support/procedural-fork/src/construct_runtime/expand/lock_id.rs b/support/procedural-fork/src/construct_runtime/expand/lock_id.rs new file mode 100644 index 000000000..e67c0da00 --- /dev/null +++ b/support/procedural-fork/src/construct_runtime/expand/lock_id.rs @@ -0,0 +1,64 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +use super::composite_helper; +use crate::construct_runtime::Pallet; +use proc_macro2::TokenStream; +use quote::quote; + +pub fn expand_outer_lock_id(pallet_decls: &[Pallet], scrate: &TokenStream) -> TokenStream { + let mut conversion_fns = Vec::new(); + let mut lock_id_variants = Vec::new(); + for decl in pallet_decls { + if let Some(_) = decl.find_part("LockId") { + let variant_name = &decl.name; + let path = &decl.path; + let index = decl.index; + let instance = decl.instance.as_ref(); + + conversion_fns.push(composite_helper::expand_conversion_fn( + "LockId", + path, + instance, + variant_name, + )); + + lock_id_variants.push(composite_helper::expand_variant( + "LockId", + index, + path, + instance, + variant_name, + )); + } + } + + quote! { + /// An identifier for each lock placed on funds. + #[derive( + Copy, Clone, Eq, PartialEq, + #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, + #scrate::__private::scale_info::TypeInfo, + #scrate::__private::RuntimeDebug, + )] + pub enum RuntimeLockId { + #( #lock_id_variants )* + } + + #( #conversion_fns )* + } +} diff --git a/support/procedural-fork/src/construct_runtime/expand/metadata.rs b/support/procedural-fork/src/construct_runtime/expand/metadata.rs new file mode 100644 index 000000000..0e76f9a92 --- /dev/null +++ b/support/procedural-fork/src/construct_runtime/expand/metadata.rs @@ -0,0 +1,258 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +use crate::construct_runtime::{parse::PalletPath, Pallet}; +use proc_macro2::TokenStream; +use quote::quote; +use std::str::FromStr; +use syn::Ident; + +pub fn expand_runtime_metadata( + runtime: &Ident, + pallet_declarations: &[Pallet], + scrate: &TokenStream, + extrinsic: &TokenStream, + system_path: &PalletPath, +) -> TokenStream { + let pallets = pallet_declarations + .iter() + .filter_map(|pallet_declaration| { + pallet_declaration.find_part("Pallet").map(|_| { + let filtered_names: Vec<_> = pallet_declaration + .pallet_parts() + .iter() + .filter(|part| part.name() != "Pallet") + .map(|part| part.name()) + .collect(); + (pallet_declaration, filtered_names) + }) + }) + .map(|(decl, filtered_names)| { + let name = &decl.name; + let index = &decl.index; + let storage = expand_pallet_metadata_storage(&filtered_names, runtime, decl); + let calls = expand_pallet_metadata_calls(&filtered_names, runtime, decl); + let event = expand_pallet_metadata_events(&filtered_names, runtime, scrate, decl); + let constants = expand_pallet_metadata_constants(runtime, decl); + let errors = expand_pallet_metadata_errors(runtime, decl); + let docs = expand_pallet_metadata_docs(runtime, decl); + let attr = decl.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); + + quote! { + #attr + #scrate::__private::metadata_ir::PalletMetadataIR { + name: stringify!(#name), + index: #index, + storage: #storage, + calls: #calls, + event: #event, + constants: #constants, + error: #errors, + docs: #docs, + } + } + }) + .collect::>(); + + quote! { + impl #runtime { + fn metadata_ir() -> #scrate::__private::metadata_ir::MetadataIR { + // Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata. + // The function is implemented by calling `impl_runtime_apis!`. + // + // However, the `construct_runtime!` may be called without calling `impl_runtime_apis!`. + // Rely on the `Deref` trait to differentiate between a runtime that implements + // APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro construct_runtime!). + // + // Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function. + // `InternalConstructRuntime` is implemented by the `construct_runtime!` for Runtime references (`& Runtime`), + // while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`). + // + // Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!` + // when both macros are called; and will resolve an empty `runtime_metadata` when only the `construct_runtime!` + // is called. + // + // `Deref` needs a reference for resolving the function call. + let rt = #runtime; + + let ty = #scrate::__private::scale_info::meta_type::<#extrinsic>(); + let address_ty = #scrate::__private::scale_info::meta_type::< + <<#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::SignaturePayload as #scrate::sp_runtime::traits::SignaturePayload>::SignatureAddress + >(); + let call_ty = #scrate::__private::scale_info::meta_type::< + <#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::Call + >(); + let signature_ty = #scrate::__private::scale_info::meta_type::< + <<#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::SignaturePayload as #scrate::sp_runtime::traits::SignaturePayload>::Signature + >(); + let extra_ty = #scrate::__private::scale_info::meta_type::< + <<#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::SignaturePayload as #scrate::sp_runtime::traits::SignaturePayload>::SignatureExtra + >(); + + #scrate::__private::metadata_ir::MetadataIR { + pallets: #scrate::__private::sp_std::vec![ #(#pallets),* ], + extrinsic: #scrate::__private::metadata_ir::ExtrinsicMetadataIR { + ty, + version: <#extrinsic as #scrate::sp_runtime::traits::ExtrinsicMetadata>::VERSION, + address_ty, + call_ty, + signature_ty, + extra_ty, + signed_extensions: < + < + #extrinsic as #scrate::sp_runtime::traits::ExtrinsicMetadata + >::SignedExtensions as #scrate::sp_runtime::traits::SignedExtension + >::metadata() + .into_iter() + .map(|meta| #scrate::__private::metadata_ir::SignedExtensionMetadataIR { + identifier: meta.identifier, + ty: meta.ty, + additional_signed: meta.additional_signed, + }) + .collect(), + }, + ty: #scrate::__private::scale_info::meta_type::<#runtime>(), + apis: (&rt).runtime_metadata(), + outer_enums: #scrate::__private::metadata_ir::OuterEnumsIR { + call_enum_ty: #scrate::__private::scale_info::meta_type::< + <#runtime as #system_path::Config>::RuntimeCall + >(), + event_enum_ty: #scrate::__private::scale_info::meta_type::(), + error_enum_ty: #scrate::__private::scale_info::meta_type::(), + } + } + } + + pub fn metadata() -> #scrate::__private::metadata::RuntimeMetadataPrefixed { + // Note: this always returns the V14 version. The runtime API function + // must be deprecated. + #scrate::__private::metadata_ir::into_v14(#runtime::metadata_ir()) + } + + pub fn metadata_at_version(version: u32) -> Option<#scrate::__private::OpaqueMetadata> { + #scrate::__private::metadata_ir::into_version(#runtime::metadata_ir(), version).map(|prefixed| { + #scrate::__private::OpaqueMetadata::new(prefixed.into()) + }) + } + + pub fn metadata_versions() -> #scrate::__private::sp_std::vec::Vec { + #scrate::__private::metadata_ir::supported_versions() + } + } + } +} + +fn expand_pallet_metadata_storage( + filtered_names: &[&'static str], + runtime: &Ident, + decl: &Pallet, +) -> TokenStream { + if filtered_names.contains(&"Storage") { + let instance = decl.instance.as_ref().into_iter(); + let path = &decl.path; + + quote! { + Some(#path::Pallet::<#runtime #(, #path::#instance)*>::storage_metadata()) + } + } else { + quote!(None) + } +} + +fn expand_pallet_metadata_calls( + filtered_names: &[&'static str], + runtime: &Ident, + decl: &Pallet, +) -> TokenStream { + if filtered_names.contains(&"Call") { + let instance = decl.instance.as_ref().into_iter(); + let path = &decl.path; + + quote! { + Some(#path::Pallet::<#runtime #(, #path::#instance)*>::call_functions()) + } + } else { + quote!(None) + } +} + +fn expand_pallet_metadata_events( + filtered_names: &[&'static str], + runtime: &Ident, + scrate: &TokenStream, + decl: &Pallet, +) -> TokenStream { + if filtered_names.contains(&"Event") { + let path = &decl.path; + let part_is_generic = !decl + .find_part("Event") + .expect("Event part exists; qed") + .generics + .params + .is_empty(); + let pallet_event = match (decl.instance.as_ref(), part_is_generic) { + (Some(inst), true) => quote!(#path::Event::<#runtime, #path::#inst>), + (Some(inst), false) => quote!(#path::Event::<#path::#inst>), + (None, true) => quote!(#path::Event::<#runtime>), + (None, false) => quote!(#path::Event), + }; + + quote! { + Some( + #scrate::__private::metadata_ir::PalletEventMetadataIR { + ty: #scrate::__private::scale_info::meta_type::<#pallet_event>() + } + ) + } + } else { + quote!(None) + } +} + +fn expand_pallet_metadata_constants(runtime: &Ident, decl: &Pallet) -> TokenStream { + let path = &decl.path; + let instance = decl.instance.as_ref().into_iter(); + + quote! { + #path::Pallet::<#runtime #(, #path::#instance)*>::pallet_constants_metadata() + } +} + +fn expand_pallet_metadata_errors(runtime: &Ident, decl: &Pallet) -> TokenStream { + let path = &decl.path; + let instance = decl.instance.as_ref().into_iter(); + + quote! { + #path::Pallet::<#runtime #(, #path::#instance)*>::error_metadata() + } +} + +fn expand_pallet_metadata_docs(runtime: &Ident, decl: &Pallet) -> TokenStream { + let path = &decl.path; + let instance = decl.instance.as_ref().into_iter(); + + quote! { + #path::Pallet::<#runtime #(, #path::#instance)*>::pallet_documentation_metadata() + } +} diff --git a/support/procedural-fork/src/construct_runtime/expand/mod.rs b/support/procedural-fork/src/construct_runtime/expand/mod.rs new file mode 100644 index 000000000..88f9a3c6e --- /dev/null +++ b/support/procedural-fork/src/construct_runtime/expand/mod.rs @@ -0,0 +1,43 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +mod call; +pub mod composite_helper; +mod config; +mod freeze_reason; +mod hold_reason; +mod inherent; +mod lock_id; +mod metadata; +mod origin; +mod outer_enums; +mod slash_reason; +mod task; +mod unsigned; + +pub use call::expand_outer_dispatch; +pub use config::expand_outer_config; +pub use freeze_reason::expand_outer_freeze_reason; +pub use hold_reason::expand_outer_hold_reason; +pub use inherent::expand_outer_inherent; +pub use lock_id::expand_outer_lock_id; +pub use metadata::expand_runtime_metadata; +pub use origin::expand_outer_origin; +pub use outer_enums::{expand_outer_enum, OuterEnumType}; +pub use slash_reason::expand_outer_slash_reason; +pub use task::expand_outer_task; +pub use unsigned::expand_outer_validate_unsigned; diff --git a/support/procedural-fork/src/construct_runtime/expand/origin.rs b/support/procedural-fork/src/construct_runtime/expand/origin.rs new file mode 100644 index 000000000..83049919d --- /dev/null +++ b/support/procedural-fork/src/construct_runtime/expand/origin.rs @@ -0,0 +1,455 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +use crate::construct_runtime::{Pallet, SYSTEM_PALLET_NAME}; +use proc_macro2::TokenStream; +use quote::quote; +use std::str::FromStr; +use syn::{Generics, Ident}; + +pub fn expand_outer_origin( + runtime: &Ident, + system_pallet: &Pallet, + pallets: &[Pallet], + scrate: &TokenStream, +) -> syn::Result { + let mut caller_variants = TokenStream::new(); + let mut pallet_conversions = TokenStream::new(); + let mut query_origin_part_macros = Vec::new(); + + for pallet_decl in pallets.iter().filter(|pallet| pallet.name != SYSTEM_PALLET_NAME) { + if let Some(pallet_entry) = pallet_decl.find_part("Origin") { + let instance = pallet_decl.instance.as_ref(); + let index = pallet_decl.index; + let generics = &pallet_entry.generics; + let name = &pallet_decl.name; + let path = &pallet_decl.path; + + if instance.is_some() && generics.params.is_empty() { + let msg = format!( + "Instantiable pallet with no generic `Origin` cannot \ + be constructed: pallet `{}` must have generic `Origin`", + name + ); + return Err(syn::Error::new(name.span(), msg)) + } + + caller_variants.extend(expand_origin_caller_variant( + runtime, + pallet_decl, + index, + instance, + generics, + )); + pallet_conversions.extend(expand_origin_pallet_conversions( + scrate, + runtime, + pallet_decl, + instance, + generics, + )); + query_origin_part_macros.push(quote! { + #path::__substrate_origin_check::is_origin_part_defined!(#name); + }); + } + } + + let system_path = &system_pallet.path; + + let system_index = system_pallet.index; + + let system_path_name = system_path.module_name(); + + let doc_string = get_intra_doc_string( + "Origin is always created with the base filter configured in", + &system_path_name, + ); + + let doc_string_none_origin = + get_intra_doc_string("Create with system none origin and", &system_path_name); + + let doc_string_root_origin = + get_intra_doc_string("Create with system root origin and", &system_path_name); + + let doc_string_signed_origin = + get_intra_doc_string("Create with system signed origin and", &system_path_name); + + let doc_string_runtime_origin = + get_intra_doc_string("Convert to runtime origin, using as filter:", &system_path_name); + + let doc_string_runtime_origin_with_caller = get_intra_doc_string( + "Convert to runtime origin with caller being system signed or none and use filter", + &system_path_name, + ); + + Ok(quote! { + #( #query_origin_part_macros )* + + /// The runtime origin type representing the origin of a call. + /// + #[doc = #doc_string] + #[derive(Clone)] + pub struct RuntimeOrigin { + pub caller: OriginCaller, + filter: #scrate::__private::sp_std::rc::Rc::RuntimeCall) -> bool>>, + } + + #[cfg(not(feature = "std"))] + impl #scrate::__private::sp_std::fmt::Debug for RuntimeOrigin { + fn fmt( + &self, + fmt: &mut #scrate::__private::sp_std::fmt::Formatter, + ) -> #scrate::__private::sp_std::result::Result<(), #scrate::__private::sp_std::fmt::Error> { + fmt.write_str("") + } + } + + #[cfg(feature = "std")] + impl #scrate::__private::sp_std::fmt::Debug for RuntimeOrigin { + fn fmt( + &self, + fmt: &mut #scrate::__private::sp_std::fmt::Formatter, + ) -> #scrate::__private::sp_std::result::Result<(), #scrate::__private::sp_std::fmt::Error> { + fmt.debug_struct("Origin") + .field("caller", &self.caller) + .field("filter", &"[function ptr]") + .finish() + } + } + + impl #scrate::traits::OriginTrait for RuntimeOrigin { + type Call = <#runtime as #system_path::Config>::RuntimeCall; + type PalletsOrigin = OriginCaller; + type AccountId = <#runtime as #system_path::Config>::AccountId; + + fn add_filter(&mut self, filter: impl Fn(&Self::Call) -> bool + 'static) { + let f = self.filter.clone(); + + self.filter = #scrate::__private::sp_std::rc::Rc::new(Box::new(move |call| { + f(call) && filter(call) + })); + } + + fn reset_filter(&mut self) { + let filter = < + <#runtime as #system_path::Config>::BaseCallFilter + as #scrate::traits::Contains<<#runtime as #system_path::Config>::RuntimeCall> + >::contains; + + self.filter = #scrate::__private::sp_std::rc::Rc::new(Box::new(filter)); + } + + fn set_caller_from(&mut self, other: impl Into) { + self.caller = other.into().caller; + } + + fn filter_call(&self, call: &Self::Call) -> bool { + match self.caller { + // Root bypasses all filters + OriginCaller::system(#system_path::Origin::<#runtime>::Root) => true, + _ => (self.filter)(call), + } + } + + fn caller(&self) -> &Self::PalletsOrigin { + &self.caller + } + + fn into_caller(self) -> Self::PalletsOrigin { + self.caller + } + + fn try_with_caller( + mut self, + f: impl FnOnce(Self::PalletsOrigin) -> Result, + ) -> Result { + match f(self.caller) { + Ok(r) => Ok(r), + Err(caller) => { self.caller = caller; Err(self) } + } + } + + fn none() -> Self { + #system_path::RawOrigin::None.into() + } + + fn root() -> Self { + #system_path::RawOrigin::Root.into() + } + + fn signed(by: Self::AccountId) -> Self { + #system_path::RawOrigin::Signed(by).into() + } + } + + #[derive( + Clone, PartialEq, Eq, #scrate::__private::RuntimeDebug, #scrate::__private::codec::Encode, + #scrate::__private::codec::Decode, #scrate::__private::scale_info::TypeInfo, #scrate::__private::codec::MaxEncodedLen, + )] + #[allow(non_camel_case_types)] + pub enum OriginCaller { + #[codec(index = #system_index)] + system(#system_path::Origin<#runtime>), + #caller_variants + #[allow(dead_code)] + Void(#scrate::__private::Void) + } + + // For backwards compatibility and ease of accessing these functions. + #[allow(dead_code)] + impl RuntimeOrigin { + #[doc = #doc_string_none_origin] + pub fn none() -> Self { + ::none() + } + + #[doc = #doc_string_root_origin] + pub fn root() -> Self { + ::root() + } + + #[doc = #doc_string_signed_origin] + pub fn signed(by: <#runtime as #system_path::Config>::AccountId) -> Self { + ::signed(by) + } + } + + impl From<#system_path::Origin<#runtime>> for OriginCaller { + fn from(x: #system_path::Origin<#runtime>) -> Self { + OriginCaller::system(x) + } + } + + impl #scrate::traits::CallerTrait<<#runtime as #system_path::Config>::AccountId> for OriginCaller { + fn into_system(self) -> Option<#system_path::RawOrigin<<#runtime as #system_path::Config>::AccountId>> { + match self { + OriginCaller::system(x) => Some(x), + _ => None, + } + } + fn as_system_ref(&self) -> Option<&#system_path::RawOrigin<<#runtime as #system_path::Config>::AccountId>> { + match &self { + OriginCaller::system(o) => Some(o), + _ => None, + } + } + } + + impl TryFrom for #system_path::Origin<#runtime> { + type Error = OriginCaller; + fn try_from(x: OriginCaller) + -> #scrate::__private::sp_std::result::Result<#system_path::Origin<#runtime>, OriginCaller> + { + if let OriginCaller::system(l) = x { + Ok(l) + } else { + Err(x) + } + } + } + + impl From<#system_path::Origin<#runtime>> for RuntimeOrigin { + + #[doc = #doc_string_runtime_origin] + fn from(x: #system_path::Origin<#runtime>) -> Self { + let o: OriginCaller = x.into(); + o.into() + } + } + + impl From for RuntimeOrigin { + fn from(x: OriginCaller) -> Self { + let mut o = RuntimeOrigin { + caller: x, + filter: #scrate::__private::sp_std::rc::Rc::new(Box::new(|_| true)), + }; + + #scrate::traits::OriginTrait::reset_filter(&mut o); + + o + } + } + + impl From for #scrate::__private::sp_std::result::Result<#system_path::Origin<#runtime>, RuntimeOrigin> { + /// NOTE: converting to pallet origin loses the origin filter information. + fn from(val: RuntimeOrigin) -> Self { + if let OriginCaller::system(l) = val.caller { + Ok(l) + } else { + Err(val) + } + } + } + impl From::AccountId>> for RuntimeOrigin { + #[doc = #doc_string_runtime_origin_with_caller] + fn from(x: Option<<#runtime as #system_path::Config>::AccountId>) -> Self { + <#system_path::Origin<#runtime>>::from(x).into() + } + } + + #pallet_conversions + }) +} + +fn expand_origin_caller_variant( + runtime: &Ident, + pallet: &Pallet, + index: u8, + instance: Option<&Ident>, + generics: &Generics, +) -> TokenStream { + let part_is_generic = !generics.params.is_empty(); + let variant_name = &pallet.name; + let path = &pallet.path; + let attr = pallet.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); + + match instance { + Some(inst) if part_is_generic => quote! { + #attr + #[codec(index = #index)] + #variant_name(#path::Origin<#runtime, #path::#inst>), + }, + Some(inst) => quote! { + #attr + #[codec(index = #index)] + #variant_name(#path::Origin<#path::#inst>), + }, + None if part_is_generic => quote! { + #attr + #[codec(index = #index)] + #variant_name(#path::Origin<#runtime>), + }, + None => quote! { + #attr + #[codec(index = #index)] + #variant_name(#path::Origin), + }, + } +} + +fn expand_origin_pallet_conversions( + scrate: &TokenStream, + runtime: &Ident, + pallet: &Pallet, + instance: Option<&Ident>, + generics: &Generics, +) -> TokenStream { + let path = &pallet.path; + let variant_name = &pallet.name; + + let part_is_generic = !generics.params.is_empty(); + let pallet_origin = match instance { + Some(inst) if part_is_generic => quote!(#path::Origin<#runtime, #path::#inst>), + Some(inst) => quote!(#path::Origin<#path::#inst>), + None if part_is_generic => quote!(#path::Origin<#runtime>), + None => quote!(#path::Origin), + }; + + let doc_string = get_intra_doc_string(" Convert to runtime origin using", &path.module_name()); + let attr = pallet.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); + + quote! { + #attr + impl From<#pallet_origin> for OriginCaller { + fn from(x: #pallet_origin) -> Self { + OriginCaller::#variant_name(x) + } + } + + #attr + impl From<#pallet_origin> for RuntimeOrigin { + #[doc = #doc_string] + fn from(x: #pallet_origin) -> Self { + let x: OriginCaller = x.into(); + x.into() + } + } + + #attr + impl From for #scrate::__private::sp_std::result::Result<#pallet_origin, RuntimeOrigin> { + /// NOTE: converting to pallet origin loses the origin filter information. + fn from(val: RuntimeOrigin) -> Self { + if let OriginCaller::#variant_name(l) = val.caller { + Ok(l) + } else { + Err(val) + } + } + } + + #attr + impl TryFrom for #pallet_origin { + type Error = OriginCaller; + fn try_from( + x: OriginCaller, + ) -> #scrate::__private::sp_std::result::Result<#pallet_origin, OriginCaller> { + if let OriginCaller::#variant_name(l) = x { + Ok(l) + } else { + Err(x) + } + } + } + + #attr + impl<'a> TryFrom<&'a OriginCaller> for &'a #pallet_origin { + type Error = (); + fn try_from( + x: &'a OriginCaller, + ) -> #scrate::__private::sp_std::result::Result<&'a #pallet_origin, ()> { + if let OriginCaller::#variant_name(l) = x { + Ok(&l) + } else { + Err(()) + } + } + } + + #attr + impl<'a> TryFrom<&'a RuntimeOrigin> for &'a #pallet_origin { + type Error = (); + fn try_from( + x: &'a RuntimeOrigin, + ) -> #scrate::__private::sp_std::result::Result<&'a #pallet_origin, ()> { + if let OriginCaller::#variant_name(l) = &x.caller { + Ok(&l) + } else { + Err(()) + } + } + } + } +} + +// Get the actual documentation using the doc information and system path name +fn get_intra_doc_string(doc_info: &str, system_path_name: &String) -> String { + format!(" {} [`{}::Config::BaseCallFilter`].", doc_info, system_path_name) +} diff --git a/support/procedural-fork/src/construct_runtime/expand/outer_enums.rs b/support/procedural-fork/src/construct_runtime/expand/outer_enums.rs new file mode 100644 index 000000000..80b242ccb --- /dev/null +++ b/support/procedural-fork/src/construct_runtime/expand/outer_enums.rs @@ -0,0 +1,279 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +use crate::construct_runtime::Pallet; +use proc_macro2::{Span, TokenStream}; +use quote::{quote, ToTokens}; +use std::str::FromStr; +use syn::{Generics, Ident}; + +/// Represents the types supported for creating an outer enum. +#[derive(Clone, Copy, PartialEq)] +pub enum OuterEnumType { + /// Collects the Event enums from all pallets. + Event, + /// Collects the Error enums from all pallets. + Error, +} + +impl OuterEnumType { + /// The name of the structure this enum represents. + fn struct_name(&self) -> &str { + match self { + OuterEnumType::Event => "RuntimeEvent", + OuterEnumType::Error => "RuntimeError", + } + } + + /// The name of the variant (ie `Event` or `Error`). + fn variant_name(&self) -> &str { + match self { + OuterEnumType::Event => "Event", + OuterEnumType::Error => "Error", + } + } +} + +impl ToTokens for OuterEnumType { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + OuterEnumType::Event => quote!(Event).to_tokens(tokens), + OuterEnumType::Error => quote!(Error).to_tokens(tokens), + } + } +} + +/// Create an outer enum that encapsulates all pallets as variants. +/// +/// Each variant represents a pallet and contains the corresponding type declared with either: +/// - #[pallet::event] for the [`OuterEnumType::Event`] variant +/// - #[pallet::error] for the [`OuterEnumType::Error`] variant +/// +/// The name of the outer enum is prefixed with Runtime, resulting in names like RuntimeEvent +/// or RuntimeError. +/// +/// This structure facilitates the decoding process by leveraging the metadata. +/// +/// # Example +/// +/// The code generate looks like the following for [`OuterEnumType::Event`]. +/// +/// ```ignore +/// enum RuntimeEvent { +/// #[codec(index = 0)] +/// System(pallet_system::Event), +/// +/// #[codec(index = 5)] +/// Balances(pallet_system::Event), +/// } +/// ``` +/// +/// Notice that the pallet index is preserved using the `#[codec(index = ..)]` attribute. +pub fn expand_outer_enum( + runtime: &Ident, + pallet_decls: &[Pallet], + scrate: &TokenStream, + enum_ty: OuterEnumType, +) -> syn::Result { + // Stores all pallet variants. + let mut enum_variants = TokenStream::new(); + // Generates the enum conversion between the `Runtime` outer enum and the pallet's enum. + let mut enum_conversions = TokenStream::new(); + // Specific for events to query via `is_event_part_defined!`. + let mut query_enum_part_macros = Vec::new(); + + let enum_name_str = enum_ty.variant_name(); + let enum_name_ident = Ident::new(enum_ty.struct_name(), Span::call_site()); + + for pallet_decl in pallet_decls { + let Some(pallet_entry) = pallet_decl.find_part(enum_name_str) else { continue }; + + let path = &pallet_decl.path; + let pallet_name = &pallet_decl.name; + let index = pallet_decl.index; + let instance = pallet_decl.instance.as_ref(); + let generics = &pallet_entry.generics; + + if instance.is_some() && generics.params.is_empty() { + let msg = format!( + "Instantiable pallet with no generic `{}` cannot \ + be constructed: pallet `{}` must have generic `{}`", + enum_name_str, pallet_name, enum_name_str, + ); + return Err(syn::Error::new(pallet_name.span(), msg)) + } + + let part_is_generic = !generics.params.is_empty(); + let pallet_enum = match (instance, part_is_generic) { + (Some(inst), true) => quote!(#path::#enum_ty::<#runtime, #path::#inst>), + (Some(inst), false) => quote!(#path::#enum_ty::<#path::#inst>), + (None, true) => quote!(#path::#enum_ty::<#runtime>), + (None, false) => quote!(#path::#enum_ty), + }; + + enum_variants.extend(expand_enum_variant( + runtime, + pallet_decl, + index, + instance, + generics, + enum_ty, + )); + enum_conversions.extend(expand_enum_conversion( + pallet_decl, + &pallet_enum, + &enum_name_ident, + )); + + if enum_ty == OuterEnumType::Event { + query_enum_part_macros.push(quote! { + #path::__substrate_event_check::is_event_part_defined!(#pallet_name); + }); + } + } + + // Derives specific for the event. + let event_custom_derives = + if enum_ty == OuterEnumType::Event { quote!(Clone, PartialEq, Eq,) } else { quote!() }; + + // Implementation specific for errors. + let error_custom_impl = generate_error_impl(scrate, enum_ty); + + Ok(quote! { + #( #query_enum_part_macros )* + + #[derive( + #event_custom_derives + #scrate::__private::codec::Encode, + #scrate::__private::codec::Decode, + #scrate::__private::scale_info::TypeInfo, + #scrate::__private::RuntimeDebug, + )] + #[allow(non_camel_case_types)] + pub enum #enum_name_ident { + #enum_variants + } + + #enum_conversions + + #error_custom_impl + }) +} + +fn expand_enum_variant( + runtime: &Ident, + pallet: &Pallet, + index: u8, + instance: Option<&Ident>, + generics: &Generics, + enum_ty: OuterEnumType, +) -> TokenStream { + let path = &pallet.path; + let variant_name = &pallet.name; + let part_is_generic = !generics.params.is_empty(); + let attr = pallet.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); + + match instance { + Some(inst) if part_is_generic => quote! { + #attr + #[codec(index = #index)] + #variant_name(#path::#enum_ty<#runtime, #path::#inst>), + }, + Some(inst) => quote! { + #attr + #[codec(index = #index)] + #variant_name(#path::#enum_ty<#path::#inst>), + }, + None if part_is_generic => quote! { + #attr + #[codec(index = #index)] + #variant_name(#path::#enum_ty<#runtime>), + }, + None => quote! { + #attr + #[codec(index = #index)] + #variant_name(#path::#enum_ty), + }, + } +} + +fn expand_enum_conversion( + pallet: &Pallet, + pallet_enum: &TokenStream, + enum_name_ident: &Ident, +) -> TokenStream { + let variant_name = &pallet.name; + let attr = pallet.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); + + quote! { + #attr + impl From<#pallet_enum> for #enum_name_ident { + fn from(x: #pallet_enum) -> Self { + #enum_name_ident + ::#variant_name(x) + } + } + #attr + impl TryInto<#pallet_enum> for #enum_name_ident { + type Error = (); + + fn try_into(self) -> ::core::result::Result<#pallet_enum, Self::Error> { + match self { + Self::#variant_name(evt) => Ok(evt), + _ => Err(()), + } + } + } + } +} + +fn generate_error_impl(scrate: &TokenStream, enum_ty: OuterEnumType) -> TokenStream { + // Implementation is specific to `Error`s. + if enum_ty == OuterEnumType::Event { + return quote! {} + } + + let enum_name_ident = Ident::new(enum_ty.struct_name(), Span::call_site()); + + quote! { + impl #enum_name_ident { + /// Optionally convert the `DispatchError` into the `RuntimeError`. + /// + /// Returns `Some` if the error matches the `DispatchError::Module` variant, otherwise `None`. + pub fn from_dispatch_error(err: #scrate::sp_runtime::DispatchError) -> Option { + let #scrate::sp_runtime::DispatchError::Module(module_error) = err else { return None }; + + let bytes = #scrate::__private::codec::Encode::encode(&module_error); + #scrate::__private::codec::Decode::decode(&mut &bytes[..]).ok() + } + } + } +} diff --git a/support/procedural-fork/src/construct_runtime/expand/slash_reason.rs b/support/procedural-fork/src/construct_runtime/expand/slash_reason.rs new file mode 100644 index 000000000..892b842b1 --- /dev/null +++ b/support/procedural-fork/src/construct_runtime/expand/slash_reason.rs @@ -0,0 +1,64 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +use super::composite_helper; +use crate::construct_runtime::Pallet; +use proc_macro2::TokenStream; +use quote::quote; + +pub fn expand_outer_slash_reason(pallet_decls: &[Pallet], scrate: &TokenStream) -> TokenStream { + let mut conversion_fns = Vec::new(); + let mut slash_reason_variants = Vec::new(); + for decl in pallet_decls { + if let Some(_) = decl.find_part("SlashReason") { + let variant_name = &decl.name; + let path = &decl.path; + let index = decl.index; + let instance = decl.instance.as_ref(); + + conversion_fns.push(composite_helper::expand_conversion_fn( + "SlashReason", + path, + instance, + variant_name, + )); + + slash_reason_variants.push(composite_helper::expand_variant( + "SlashReason", + index, + path, + instance, + variant_name, + )); + } + } + + quote! { + /// A reason for slashing funds. + #[derive( + Copy, Clone, Eq, PartialEq, + #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, + #scrate::__private::scale_info::TypeInfo, + #scrate::__private::RuntimeDebug, + )] + pub enum RuntimeSlashReason { + #( #slash_reason_variants )* + } + + #( #conversion_fns )* + } +} diff --git a/support/procedural-fork/src/construct_runtime/expand/task.rs b/support/procedural-fork/src/construct_runtime/expand/task.rs new file mode 100644 index 000000000..6531c0e9e --- /dev/null +++ b/support/procedural-fork/src/construct_runtime/expand/task.rs @@ -0,0 +1,131 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +use crate::construct_runtime::Pallet; +use proc_macro2::{Ident, TokenStream as TokenStream2}; +use quote::quote; + +/// Expands aggregate `RuntimeTask` enum. +pub fn expand_outer_task( + runtime_name: &Ident, + pallet_decls: &[Pallet], + scrate: &TokenStream2, +) -> TokenStream2 { + let mut from_impls = Vec::new(); + let mut task_variants = Vec::new(); + let mut variant_names = Vec::new(); + let mut task_paths = Vec::new(); + for decl in pallet_decls { + if decl.find_part("Task").is_none() { + continue + } + + let variant_name = &decl.name; + let path = &decl.path; + let index = decl.index; + + from_impls.push(quote! { + impl From<#path::Task<#runtime_name>> for RuntimeTask { + fn from(hr: #path::Task<#runtime_name>) -> Self { + RuntimeTask::#variant_name(hr) + } + } + + impl TryInto<#path::Task<#runtime_name>> for RuntimeTask { + type Error = (); + + fn try_into(self) -> Result<#path::Task<#runtime_name>, Self::Error> { + match self { + RuntimeTask::#variant_name(hr) => Ok(hr), + _ => Err(()), + } + } + } + }); + + task_variants.push(quote! { + #[codec(index = #index)] + #variant_name(#path::Task<#runtime_name>), + }); + + variant_names.push(quote!(#variant_name)); + + task_paths.push(quote!(#path::Task)); + } + + let prelude = quote!(#scrate::traits::tasks::__private); + + const INCOMPLETE_MATCH_QED: &'static str = + "cannot have an instantiated RuntimeTask without some Task variant in the runtime. QED"; + + let output = quote! { + /// An aggregation of all `Task` enums across all pallets included in the current runtime. + #[derive( + Clone, Eq, PartialEq, + #scrate::__private::codec::Encode, + #scrate::__private::codec::Decode, + #scrate::__private::scale_info::TypeInfo, + #scrate::__private::RuntimeDebug, + )] + pub enum RuntimeTask { + #( #task_variants )* + } + + #[automatically_derived] + impl #scrate::traits::Task for RuntimeTask { + type Enumeration = #prelude::IntoIter; + + fn is_valid(&self) -> bool { + match self { + #(RuntimeTask::#variant_names(val) => val.is_valid(),)* + _ => unreachable!(#INCOMPLETE_MATCH_QED), + } + } + + fn run(&self) -> Result<(), #scrate::traits::tasks::__private::DispatchError> { + match self { + #(RuntimeTask::#variant_names(val) => val.run(),)* + _ => unreachable!(#INCOMPLETE_MATCH_QED), + } + } + + fn weight(&self) -> #scrate::pallet_prelude::Weight { + match self { + #(RuntimeTask::#variant_names(val) => val.weight(),)* + _ => unreachable!(#INCOMPLETE_MATCH_QED), + } + } + + fn task_index(&self) -> u32 { + match self { + #(RuntimeTask::#variant_names(val) => val.task_index(),)* + _ => unreachable!(#INCOMPLETE_MATCH_QED), + } + } + + fn iter() -> Self::Enumeration { + let mut all_tasks = Vec::new(); + #(all_tasks.extend(#task_paths::iter().map(RuntimeTask::from).collect::>());)* + all_tasks.into_iter() + } + } + + #( #from_impls )* + }; + + output +} diff --git a/support/procedural-fork/src/construct_runtime/expand/unsigned.rs b/support/procedural-fork/src/construct_runtime/expand/unsigned.rs new file mode 100644 index 000000000..33aadba0d --- /dev/null +++ b/support/procedural-fork/src/construct_runtime/expand/unsigned.rs @@ -0,0 +1,89 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +use crate::construct_runtime::Pallet; +use proc_macro2::TokenStream; +use quote::quote; +use std::str::FromStr; +use syn::Ident; + +pub fn expand_outer_validate_unsigned( + runtime: &Ident, + pallet_decls: &[Pallet], + scrate: &TokenStream, +) -> TokenStream { + let mut pallet_names = Vec::new(); + let mut pallet_attrs = Vec::new(); + let mut query_validate_unsigned_part_macros = Vec::new(); + + for pallet_decl in pallet_decls { + if pallet_decl.exists_part("ValidateUnsigned") { + let name = &pallet_decl.name; + let path = &pallet_decl.path; + let attr = pallet_decl.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); + + pallet_names.push(name); + pallet_attrs.push(attr); + query_validate_unsigned_part_macros.push(quote! { + #path::__substrate_validate_unsigned_check::is_validate_unsigned_part_defined!(#name); + }); + } + } + + quote! { + #( #query_validate_unsigned_part_macros )* + + impl #scrate::unsigned::ValidateUnsigned for #runtime { + type Call = RuntimeCall; + + fn pre_dispatch(call: &Self::Call) -> Result<(), #scrate::unsigned::TransactionValidityError> { + #[allow(unreachable_patterns)] + match call { + #( + #pallet_attrs + RuntimeCall::#pallet_names(inner_call) => #pallet_names::pre_dispatch(inner_call), + )* + // pre-dispatch should not stop inherent extrinsics, validation should prevent + // including arbitrary (non-inherent) extrinsics to blocks. + _ => Ok(()), + } + } + + fn validate_unsigned( + #[allow(unused_variables)] + source: #scrate::unsigned::TransactionSource, + call: &Self::Call, + ) -> #scrate::unsigned::TransactionValidity { + #[allow(unreachable_patterns)] + match call { + #( + #pallet_attrs + RuntimeCall::#pallet_names(inner_call) => #pallet_names::validate_unsigned(source, inner_call), + )* + _ => #scrate::unsigned::UnknownTransaction::NoUnsignedValidator.into(), + } + } + } + } +} diff --git a/support/procedural-fork/src/construct_runtime/mod.rs b/support/procedural-fork/src/construct_runtime/mod.rs new file mode 100644 index 000000000..b083abbb2 --- /dev/null +++ b/support/procedural-fork/src/construct_runtime/mod.rs @@ -0,0 +1,809 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Implementation of `construct_runtime`. +//! +//! `construct_runtime` implementation is recursive and can generate code which will call itself in +//! order to get all the pallet parts for each pallet. +//! +//! Pallets can define their parts: +//! - Implicitly: `System: frame_system` +//! - Explicitly: `System: frame_system::{Pallet, Call}` +//! +//! The `construct_runtime` transitions from the implicit definition to the explicit one. +//! From the explicit state, Substrate expands the pallets with additional information +//! that is to be included in the runtime metadata. This expansion makes visible some extra +//! parts of the pallets, mainly the `Error` if defined. The expanded state looks like +//! `System: frame_system expanded::{Error} ::{Pallet, Call}` and concatenates the extra expanded +//! parts with the user-provided parts. For example, the `Pallet`, `Call` and `Error` parts are +//! collected. +//! +//! Pallets must provide the `tt_extra_parts` and `tt_default_parts` macros for these transitions. +//! These are automatically implemented by the `#[pallet::pallet]` macro. +//! +//! This macro also generates the following enums for ease of decoding: +//! - `enum RuntimeCall`: This type contains the information needed to decode extrinsics. +//! - `enum RuntimeEvent`: This type contains the information needed to decode events. +//! - `enum RuntimeError`: While this cannot be used directly to decode `sp_runtime::DispatchError` +//! from the chain, it contains the information needed to decode the +//! `sp_runtime::DispatchError::Module`. +//! +//! # State Transitions +//! +//! ```ignore +//! +----------+ +//! | Implicit | -----------+ +//! +----------+ | +//! | | +//! v v +//! +----------+ +------------------+ +//! | Explicit | --> | ExplicitExpanded | +//! +----------+ +------------------+ +//! ``` +//! +//! When all pallet parts are implicit, then the `construct_runtime!` macro expands to its final +//! state, the `ExplicitExpanded`. Otherwise, all implicit parts are converted to an explicit +//! expanded part allow the `construct_runtime!` to expand any remaining explicit parts to an +//! explicit expanded part. +//! +//! # Implicit to Explicit +//! +//! The `construct_runtime` macro transforms the implicit declaration of each pallet +//! `System: frame_system` to an explicit one `System: frame_system::{Pallet, Call}` using the +//! `tt_default_parts` macro. +//! +//! The `tt_default_parts` macro exposes a comma separated list of pallet parts. For example, the +//! `Event` part is exposed only if the pallet implements an event via `#[pallet::event]` macro. +//! The tokens generated by this macro are ` expanded :: { Pallet, Call }` for our example. +//! +//! The `match_and_insert` macro takes in 3 arguments: +//! - target: This is the `TokenStream` that contains the `construct_runtime!` macro. +//! - pattern: The pattern to match against in the target stream. +//! - tokens: The tokens to added after the pattern match. +//! +//! The `construct_runtime` macro uses the `tt_call` to get the default pallet parts via +//! the `tt_default_parts` macro defined by each pallet. The pallet parts are then returned as +//! input to the `match_and_replace` macro. +//! The `match_and_replace` then will modify the the `construct_runtime!` to expand the implicit +//! definition to the explicit one. +//! +//! For example, +//! +//! ```ignore +//! construct_runtime!( +//! //... +//! { +//! System: frame_system = 0, // Implicit definition of parts +//! Balances: pallet_balances = 1, // Implicit definition of parts +//! } +//! ); +//! ``` +//! This call has some implicit pallet parts, thus it will expand to: +//! ```ignore +//! frame_support::__private::tt_call! { +//! macro = [{ pallet_balances::tt_default_parts }] +//! ~~> frame_support::match_and_insert! { +//! target = [{ +//! frame_support::__private::tt_call! { +//! macro = [{ frame_system::tt_default_parts }] +//! ~~> frame_support::match_and_insert! { +//! target = [{ +//! construct_runtime!( +//! //... +//! { +//! System: frame_system = 0, +//! Balances: pallet_balances = 1, +//! } +//! ); +//! }] +//! pattern = [{ System: frame_system }] +//! } +//! } +//! }] +//! pattern = [{ Balances: pallet_balances }] +//! } +//! } +//! ``` +//! `tt_default_parts` must be defined. It returns the pallet parts inside some tokens, and +//! then `tt_call` will pipe the returned pallet parts into the input of `match_and_insert`. +//! Thus `match_and_insert` will initially receive the following inputs: +//! ```ignore +//! frame_support::match_and_insert! { +//! target = [{ +//! frame_support::match_and_insert! { +//! target = [{ +//! construct_runtime!( +//! //... +//! { +//! System: frame_system = 0, +//! Balances: pallet_balances = 1, +//! } +//! ) +//! }] +//! pattern = [{ System: frame_system }] +//! tokens = [{ ::{Pallet, Call} }] +//! } +//! }] +//! pattern = [{ Balances: pallet_balances }] +//! tokens = [{ ::{Pallet, Call} }] +//! } +//! ``` +//! After dealing with `pallet_balances`, the inner `match_and_insert` will expand to: +//! ```ignore +//! frame_support::match_and_insert! { +//! target = [{ +//! construct_runtime!( +//! //... +//! { +//! System: frame_system = 0, // Implicit definition of parts +//! Balances: pallet_balances::{Pallet, Call} = 1, // Explicit definition of parts +//! } +//! ) +//! }] +//! pattern = [{ System: frame_system }] +//! tokens = [{ ::{Pallet, Call} }] +//! } +//! ``` +//! +//! Which will then finally expand to the following: +//! ```ignore +//! construct_runtime!( +//! //... +//! { +//! System: frame_system::{Pallet, Call}, +//! Balances: pallet_balances::{Pallet, Call}, +//! } +//! ) +//! ``` +//! +//! This call has no implicit pallet parts, thus it will expand to the runtime construction: +//! ```ignore +//! pub enum Runtime { ... } +//! pub struct Call { ... } +//! impl Call ... +//! pub enum Origin { ... } +//! ... +//! ``` +//! +//! Visualizing the entire flow of `construct_runtime!`, it would look like the following: +//! +//! ```ignore +//! +--------------------+ +---------------------+ +-------------------+ +//! | | | (defined in pallet) | | | +//! | construct_runtime! | --> | tt_default_parts! | --> | match_and_insert! | +//! | w/ no pallet parts | | | | | +//! +--------------------+ +---------------------+ +-------------------+ +//! +//! +--------------------+ +//! | | +//! --> | construct_runtime! | +//! | w/ pallet parts | +//! +--------------------+ +//! ``` +//! +//! # Explicit to Explicit Expanded +//! +//! Users normally do not care about this transition. +//! +//! Similarly to the previous transition, the macro expansion transforms `System: +//! frame_system::{Pallet, Call}` into `System: frame_system expanded::{Error} ::{Pallet, Call}`. +//! The `expanded` section adds extra parts that the Substrate would like to expose for each pallet +//! by default. This is done to expose the appropriate types for metadata construction. +//! +//! This time, instead of calling `tt_default_parts` we are using the `tt_extra_parts` macro. +//! This macro returns the ` :: expanded { Error }` list of additional parts we would like to +//! expose. + +pub(crate) mod expand; +pub(crate) mod parse; + +use crate::pallet::parse::helper::two128_str; +use cfg_expr::Predicate; +use frame_support_procedural_tools::{ + generate_access_from_frame_or_crate, generate_crate_access, generate_hidden_includes, +}; +use itertools::Itertools; +use parse::{ExplicitRuntimeDeclaration, ImplicitRuntimeDeclaration, Pallet, RuntimeDeclaration}; +use proc_macro::TokenStream; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use std::{collections::HashSet, str::FromStr}; +use syn::{spanned::Spanned, Ident, Result}; + +/// The fixed name of the system pallet. +const SYSTEM_PALLET_NAME: &str = "System"; + +/// Implementation of `construct_runtime` macro. Either expand to some code which will call +/// `construct_runtime` again, or expand to the final runtime definition. +pub fn construct_runtime(input: TokenStream) -> TokenStream { + let input_copy = input.clone(); + let definition = syn::parse_macro_input!(input as RuntimeDeclaration); + + let (check_pallet_number_res, res) = match definition { + RuntimeDeclaration::Implicit(implicit_def) => ( + check_pallet_number(input_copy.clone().into(), implicit_def.pallets.len()), + construct_runtime_implicit_to_explicit(input_copy.into(), implicit_def), + ), + RuntimeDeclaration::Explicit(explicit_decl) => ( + check_pallet_number(input_copy.clone().into(), explicit_decl.pallets.len()), + construct_runtime_explicit_to_explicit_expanded(input_copy.into(), explicit_decl), + ), + RuntimeDeclaration::ExplicitExpanded(explicit_decl) => ( + check_pallet_number(input_copy.into(), explicit_decl.pallets.len()), + construct_runtime_final_expansion(explicit_decl), + ), + }; + + let res = res.unwrap_or_else(|e| e.to_compile_error()); + + // We want to provide better error messages to the user and thus, handle the error here + // separately. If there is an error, we print the error and still generate all of the code to + // get in overall less errors for the user. + let res = if let Err(error) = check_pallet_number_res { + let error = error.to_compile_error(); + + quote! { + #error + + #res + } + } else { + res + }; + + let res = expander::Expander::new("construct_runtime") + .dry(std::env::var("EXPAND_MACROS").is_err()) + .verbose(true) + .write_to_out_dir(res) + .expect("Does not fail because of IO in OUT_DIR; qed"); + + res.into() +} + +/// All pallets that have implicit pallet parts (ie `System: frame_system`) are +/// expanded with the default parts defined by the pallet's `tt_default_parts` macro. +/// +/// This function transforms the [`RuntimeDeclaration::Implicit`] into +/// [`RuntimeDeclaration::Explicit`] that is not yet fully expanded. +/// +/// For more details, please refer to the root documentation. +fn construct_runtime_implicit_to_explicit( + input: TokenStream2, + definition: ImplicitRuntimeDeclaration, +) -> Result { + let frame_support = generate_access_from_frame_or_crate("frame-support")?; + let mut expansion = quote::quote!( + #frame_support::construct_runtime! { #input } + ); + for pallet in definition.pallets.iter().filter(|pallet| pallet.pallet_parts.is_none()) { + let pallet_path = &pallet.path; + let pallet_name = &pallet.name; + let pallet_instance = pallet.instance.as_ref().map(|instance| quote::quote!(::<#instance>)); + expansion = quote::quote!( + #frame_support::__private::tt_call! { + macro = [{ #pallet_path::tt_default_parts }] + your_tt_return = [{ #frame_support::__private::tt_return }] + ~~> #frame_support::match_and_insert! { + target = [{ #expansion }] + pattern = [{ #pallet_name: #pallet_path #pallet_instance }] + } + } + ); + } + + Ok(expansion) +} + +/// All pallets that have +/// (I): explicit pallet parts (ie `System: frame_system::{Pallet, Call}`) and +/// (II): are not fully expanded (ie do not include the `Error` expansion part) +/// are fully expanded by including the parts from the pallet's `tt_extra_parts` macro. +/// +/// This function transforms the [`RuntimeDeclaration::Explicit`] that is not yet fully expanded +/// into [`RuntimeDeclaration::ExplicitExpanded`] fully expanded. +/// +/// For more details, please refer to the root documentation. +fn construct_runtime_explicit_to_explicit_expanded( + input: TokenStream2, + definition: ExplicitRuntimeDeclaration, +) -> Result { + let frame_support = generate_access_from_frame_or_crate("frame-support")?; + let mut expansion = quote::quote!( + #frame_support::construct_runtime! { #input } + ); + for pallet in definition.pallets.iter().filter(|pallet| !pallet.is_expanded) { + let pallet_path = &pallet.path; + let pallet_name = &pallet.name; + let pallet_instance = pallet.instance.as_ref().map(|instance| quote::quote!(::<#instance>)); + expansion = quote::quote!( + #frame_support::__private::tt_call! { + macro = [{ #pallet_path::tt_extra_parts }] + your_tt_return = [{ #frame_support::__private::tt_return }] + ~~> #frame_support::match_and_insert! { + target = [{ #expansion }] + pattern = [{ #pallet_name: #pallet_path #pallet_instance }] + } + } + ); + } + + Ok(expansion) +} + +/// All pallets have explicit definition of parts, this will expand to the runtime declaration. +fn construct_runtime_final_expansion( + definition: ExplicitRuntimeDeclaration, +) -> Result { + let ExplicitRuntimeDeclaration { name, pallets, pallets_token, where_section } = definition; + + let system_pallet = + pallets.iter().find(|decl| decl.name == SYSTEM_PALLET_NAME).ok_or_else(|| { + syn::Error::new( + pallets_token.span.join(), + "`System` pallet declaration is missing. \ + Please add this line: `System: frame_system,`", + ) + })?; + if !system_pallet.cfg_pattern.is_empty() { + return Err(syn::Error::new( + system_pallet.name.span(), + "`System` pallet declaration is feature gated, please remove any `#[cfg]` attributes", + )) + } + + let features = pallets + .iter() + .filter_map(|decl| { + (!decl.cfg_pattern.is_empty()).then(|| { + decl.cfg_pattern.iter().flat_map(|attr| { + attr.predicates().filter_map(|pred| match pred { + Predicate::Feature(feat) => Some(feat), + Predicate::Test => Some("test"), + _ => None, + }) + }) + }) + }) + .flatten() + .collect::>(); + + 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 frame_system = generate_access_from_frame_or_crate("frame-system")?; + let block = quote!(<#name as #frame_system::Config>::Block); + let unchecked_extrinsic = quote!(<#block as #scrate::sp_runtime::traits::Block>::Extrinsic); + + let outer_event = + expand::expand_outer_enum(&name, &pallets, &scrate, expand::OuterEnumType::Event)?; + let outer_error = + expand::expand_outer_enum(&name, &pallets, &scrate, expand::OuterEnumType::Error)?; + + let outer_origin = expand::expand_outer_origin(&name, system_pallet, &pallets, &scrate)?; + let all_pallets = decl_all_pallets(&name, pallets.iter(), &features); + let pallet_to_index = decl_pallet_runtime_setup(&name, &pallets, &scrate); + + let dispatch = expand::expand_outer_dispatch(&name, system_pallet, &pallets, &scrate); + let tasks = expand::expand_outer_task(&name, &pallets, &scrate); + let metadata = expand::expand_runtime_metadata( + &name, + &pallets, + &scrate, + &unchecked_extrinsic, + &system_pallet.path, + ); + let outer_config = expand::expand_outer_config(&name, &pallets, &scrate); + let inherent = + expand::expand_outer_inherent(&name, &block, &unchecked_extrinsic, &pallets, &scrate); + let validate_unsigned = expand::expand_outer_validate_unsigned(&name, &pallets, &scrate); + let freeze_reason = expand::expand_outer_freeze_reason(&pallets, &scrate); + let hold_reason = expand::expand_outer_hold_reason(&pallets, &scrate); + let lock_id = expand::expand_outer_lock_id(&pallets, &scrate); + let slash_reason = expand::expand_outer_slash_reason(&pallets, &scrate); + let integrity_test = decl_integrity_test(&scrate); + let static_assertions = decl_static_assertions(&name, &pallets, &scrate); + + let warning = where_section.map_or(None, |where_section| { + Some( + proc_macro_warning::Warning::new_deprecated("WhereSection") + .old("use a `where` clause in `construct_runtime`") + .new( + "use `frame_system::Config` to set the `Block` type and delete this clause. + It is planned to be removed in December 2023", + ) + .help_links(&["https://github.com/paritytech/substrate/pull/14437"]) + .span(where_section.span) + .build_or_panic(), + ) + }); + + let res = quote!( + #warning + + #scrate_decl + + // Prevent UncheckedExtrinsic to print unused warning. + const _: () = { + #[allow(unused)] + type __hidden_use_of_unchecked_extrinsic = #unchecked_extrinsic; + }; + + #[derive( + Clone, Copy, PartialEq, Eq, #scrate::sp_runtime::RuntimeDebug, + #scrate::__private::scale_info::TypeInfo + )] + pub struct #name; + impl #scrate::sp_runtime::traits::GetRuntimeBlockType for #name { + type RuntimeBlock = #block; + } + + // Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata. + // The function is implemented by calling `impl_runtime_apis!`. + // + // However, the `construct_runtime!` may be called without calling `impl_runtime_apis!`. + // Rely on the `Deref` trait to differentiate between a runtime that implements + // APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro construct_runtime!). + // + // Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function. + // `InternalConstructRuntime` is implemented by the `construct_runtime!` for Runtime references (`& Runtime`), + // while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`). + // + // Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!` + // when both macros are called; and will resolve an empty `runtime_metadata` when only the `construct_runtime!` + // is called. + + #[doc(hidden)] + trait InternalConstructRuntime { + #[inline(always)] + fn runtime_metadata(&self) -> #scrate::__private::sp_std::vec::Vec<#scrate::__private::metadata_ir::RuntimeApiMetadataIR> { + Default::default() + } + } + #[doc(hidden)] + impl InternalConstructRuntime for &#name {} + + #outer_event + + #outer_error + + #outer_origin + + #all_pallets + + #pallet_to_index + + #dispatch + + #tasks + + #metadata + + #outer_config + + #inherent + + #validate_unsigned + + #freeze_reason + + #hold_reason + + #lock_id + + #slash_reason + + #integrity_test + + #static_assertions + ); + + Ok(res) +} + +pub(crate) fn decl_all_pallets<'a>( + runtime: &'a Ident, + pallet_declarations: impl Iterator, + features: &HashSet<&str>, +) -> TokenStream2 { + let mut types = TokenStream2::new(); + + // Every feature set to the pallet names that should be included by this feature set. + let mut features_to_names = features + .iter() + .map(|f| *f) + .powerset() + .map(|feat| (HashSet::from_iter(feat), Vec::new())) + .collect::, Vec<_>)>>(); + + for pallet_declaration in pallet_declarations { + let type_name = &pallet_declaration.name; + let pallet = &pallet_declaration.path; + let mut generics = vec![quote!(#runtime)]; + generics.extend(pallet_declaration.instance.iter().map(|name| quote!(#pallet::#name))); + let mut attrs = Vec::new(); + for cfg in &pallet_declaration.cfg_pattern { + let feat = format!("#[cfg({})]\n", cfg.original()); + attrs.extend(TokenStream2::from_str(&feat).expect("was parsed successfully; qed")); + } + let type_decl = quote!( + #(#attrs)* + pub type #type_name = #pallet::Pallet <#(#generics),*>; + ); + types.extend(type_decl); + + if pallet_declaration.cfg_pattern.is_empty() { + for (_, names) in features_to_names.iter_mut() { + names.push(&pallet_declaration.name); + } + } else { + for (feature_set, names) in &mut features_to_names { + // Rust tidbit: if we have multiple `#[cfg]` feature on the same item, then the + // predicates listed in all `#[cfg]` attributes are effectively joined by `and()`, + // meaning that all of them must match in order to activate the item + let is_feature_active = pallet_declaration.cfg_pattern.iter().all(|expr| { + expr.eval(|pred| match pred { + Predicate::Feature(f) => feature_set.contains(f), + Predicate::Test => feature_set.contains(&"test"), + _ => false, + }) + }); + + if is_feature_active { + names.push(&pallet_declaration.name); + } + } + } + } + + // All possible features. This will be used below for the empty feature set. + let mut all_features = features_to_names + .iter() + .flat_map(|f| f.0.iter().cloned()) + .collect::>(); + let attribute_to_names = features_to_names + .into_iter() + .map(|(mut features, names)| { + // If this is the empty feature set, it needs to be changed to negate all available + // features. So, we ensure that there is some type declared when all features are not + // enabled. + if features.is_empty() { + let test_cfg = all_features.remove("test").then_some(quote!(test)).into_iter(); + let features = all_features.iter(); + let attr = quote!(#[cfg(all( #(not(#test_cfg)),* #(not(feature = #features)),* ))]); + + (attr, names) + } else { + let test_cfg = features.remove("test").then_some(quote!(test)).into_iter(); + let disabled_features = all_features.difference(&features); + let features = features.iter(); + let attr = quote!(#[cfg(all( #(#test_cfg,)* #(feature = #features,)* #(not(feature = #disabled_features)),* ))]); + + (attr, names) + } + }) + .collect::>(); + + let all_pallets_without_system = attribute_to_names.iter().map(|(attr, names)| { + let names = names.iter().filter(|n| **n != SYSTEM_PALLET_NAME); + quote! { + #attr + /// All pallets included in the runtime as a nested tuple of types. + /// Excludes the System pallet. + pub type AllPalletsWithoutSystem = ( #(#names,)* ); + } + }); + + let all_pallets_with_system = attribute_to_names.iter().map(|(attr, names)| { + quote! { + #attr + /// All pallets included in the runtime as a nested tuple of types. + pub type AllPalletsWithSystem = ( #(#names,)* ); + } + }); + + quote!( + #types + + #( #all_pallets_with_system )* + + #( #all_pallets_without_system )* + ) +} + +pub(crate) fn decl_pallet_runtime_setup( + runtime: &Ident, + pallet_declarations: &[Pallet], + scrate: &TokenStream2, +) -> TokenStream2 { + let names = pallet_declarations.iter().map(|d| &d.name).collect::>(); + let name_strings = pallet_declarations.iter().map(|d| d.name.to_string()); + let name_hashes = pallet_declarations.iter().map(|d| two128_str(&d.name.to_string())); + let module_names = pallet_declarations.iter().map(|d| d.path.module_name()); + let indices = pallet_declarations.iter().map(|pallet| pallet.index as usize); + let pallet_structs = pallet_declarations + .iter() + .map(|pallet| { + let path = &pallet.path; + match pallet.instance.as_ref() { + Some(inst) => quote!(#path::Pallet<#runtime, #path::#inst>), + None => quote!(#path::Pallet<#runtime>), + } + }) + .collect::>(); + let pallet_attrs = pallet_declarations + .iter() + .map(|pallet| { + pallet.cfg_pattern.iter().fold(TokenStream2::new(), |acc, pattern| { + let attr = TokenStream2::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }) + }) + .collect::>(); + + quote!( + /// Provides an implementation of `PalletInfo` to provide information + /// about the pallet setup in the runtime. + pub struct PalletInfo; + + impl #scrate::traits::PalletInfo for PalletInfo { + + fn index() -> Option { + let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); + #( + #pallet_attrs + if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { + return Some(#indices) + } + )* + + None + } + + fn name() -> Option<&'static str> { + let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); + #( + #pallet_attrs + if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { + return Some(#name_strings) + } + )* + + None + } + + fn name_hash() -> Option<[u8; 16]> { + let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); + #( + #pallet_attrs + if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { + return Some(#name_hashes) + } + )* + + None + } + + fn module_name() -> Option<&'static str> { + let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); + #( + #pallet_attrs + if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { + return Some(#module_names) + } + )* + + None + } + + fn crate_version() -> Option<#scrate::traits::CrateVersion> { + let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); + #( + #pallet_attrs + if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { + return Some( + <#pallet_structs as #scrate::traits::PalletInfoAccess>::crate_version() + ) + } + )* + + None + } + } + ) +} + +pub(crate) fn decl_integrity_test(scrate: &TokenStream2) -> TokenStream2 { + quote!( + #[cfg(test)] + mod __construct_runtime_integrity_test { + use super::*; + + #[test] + pub fn runtime_integrity_tests() { + #scrate::__private::sp_tracing::try_init_simple(); + ::integrity_test(); + } + } + ) +} + +pub(crate) fn decl_static_assertions( + runtime: &Ident, + pallet_decls: &[Pallet], + scrate: &TokenStream2, +) -> TokenStream2 { + let error_encoded_size_check = pallet_decls.iter().map(|decl| { + let path = &decl.path; + let assert_message = format!( + "The maximum encoded size of the error type in the `{}` pallet exceeds \ + `MAX_MODULE_ERROR_ENCODED_SIZE`", + decl.name, + ); + + quote! { + #scrate::__private::tt_call! { + macro = [{ #path::tt_error_token }] + your_tt_return = [{ #scrate::__private::tt_return }] + ~~> #scrate::assert_error_encoded_size! { + path = [{ #path }] + runtime = [{ #runtime }] + assert_message = [{ #assert_message }] + } + } + } + }); + + quote! { + #(#error_encoded_size_check)* + } +} + +pub(crate) fn check_pallet_number(input: TokenStream2, pallet_num: usize) -> Result<()> { + let max_pallet_num = { + if cfg!(feature = "tuples-96") { + 96 + } else if cfg!(feature = "tuples-128") { + 128 + } else { + 64 + } + }; + + if pallet_num > max_pallet_num { + let no_feature = max_pallet_num == 128; + return Err(syn::Error::new( + input.span(), + format!( + "{} To increase this limit, enable the tuples-{} feature of [frame_support]. {}", + "The number of pallets exceeds the maximum number of tuple elements.", + max_pallet_num + 32, + if no_feature { + "If the feature does not exist - it needs to be implemented." + } else { + "" + }, + ), + )) + } + + Ok(()) +} diff --git a/support/procedural-fork/src/construct_runtime/parse.rs b/support/procedural-fork/src/construct_runtime/parse.rs new file mode 100644 index 000000000..31866c787 --- /dev/null +++ b/support/procedural-fork/src/construct_runtime/parse.rs @@ -0,0 +1,786 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use frame_support_procedural_tools::syn_ext as ext; +use proc_macro2::{Span, TokenStream}; +use quote::ToTokens; +use std::collections::{HashMap, HashSet}; +use syn::{ + ext::IdentExt, + parse::{Parse, ParseStream}, + punctuated::Punctuated, + spanned::Spanned, + token, Attribute, Error, Ident, Path, Result, Token, +}; + +mod keyword { + syn::custom_keyword!(Block); + syn::custom_keyword!(NodeBlock); + syn::custom_keyword!(UncheckedExtrinsic); + syn::custom_keyword!(Pallet); + syn::custom_keyword!(Call); + syn::custom_keyword!(Storage); + syn::custom_keyword!(Event); + syn::custom_keyword!(Error); + syn::custom_keyword!(Config); + syn::custom_keyword!(Origin); + syn::custom_keyword!(Inherent); + syn::custom_keyword!(ValidateUnsigned); + syn::custom_keyword!(FreezeReason); + syn::custom_keyword!(HoldReason); + syn::custom_keyword!(Task); + syn::custom_keyword!(LockId); + syn::custom_keyword!(SlashReason); + syn::custom_keyword!(exclude_parts); + syn::custom_keyword!(use_parts); + syn::custom_keyword!(expanded); +} + +/// Declaration of a runtime. +/// +/// Pallet declare their part either explicitly or implicitly (using no part declaration) +/// If all pallet have explicit parts then the runtime declaration is explicit, otherwise it is +/// implicit. +#[derive(Debug)] +pub enum RuntimeDeclaration { + Implicit(ImplicitRuntimeDeclaration), + Explicit(ExplicitRuntimeDeclaration), + ExplicitExpanded(ExplicitRuntimeDeclaration), +} + +/// Declaration of a runtime with some pallet with implicit declaration of parts. +#[derive(Debug)] +pub struct ImplicitRuntimeDeclaration { + pub name: Ident, + pub where_section: Option, + pub pallets: Vec, +} + +/// Declaration of a runtime with all pallet having explicit declaration of parts. +#[derive(Debug)] +pub struct ExplicitRuntimeDeclaration { + pub name: Ident, + pub where_section: Option, + pub pallets: Vec, + pub pallets_token: token::Brace, +} + +impl Parse for RuntimeDeclaration { + fn parse(input: ParseStream) -> Result { + input.parse::()?; + + // Support either `enum` or `struct`. + if input.peek(Token![struct]) { + input.parse::()?; + } else { + input.parse::()?; + } + + let name = input.parse::()?; + let where_section = if input.peek(token::Where) { Some(input.parse()?) } else { None }; + let pallets = + input.parse::>>()?; + let pallets_token = pallets.token; + + match convert_pallets(pallets.content.inner.into_iter().collect())? { + PalletsConversion::Implicit(pallets) => + Ok(RuntimeDeclaration::Implicit(ImplicitRuntimeDeclaration { + name, + where_section, + pallets, + })), + PalletsConversion::Explicit(pallets) => + Ok(RuntimeDeclaration::Explicit(ExplicitRuntimeDeclaration { + name, + where_section, + pallets, + pallets_token, + })), + PalletsConversion::ExplicitExpanded(pallets) => + Ok(RuntimeDeclaration::ExplicitExpanded(ExplicitRuntimeDeclaration { + name, + where_section, + pallets, + pallets_token, + })), + } + } +} + +#[derive(Debug)] +pub struct WhereSection { + pub span: Span, + 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 declaration for `{:?}`.", + kind, kind + ); + return Err(Error::new(*kind_span, msg)) + } + Ok(Self { span: input.span(), 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()? + }, + }) + } +} + +/// The declaration of a pallet. +#[derive(Debug, Clone)] +pub struct PalletDeclaration { + /// Is this pallet fully expanded? + pub is_expanded: bool, + /// The name of the pallet, e.g.`System` in `System: frame_system`. + pub name: Ident, + /// Optional attributes tagged right above a pallet declaration. + pub attrs: Vec, + /// Optional fixed index, e.g. `MyPallet ... = 3,`. + pub index: Option, + /// The path of the pallet, e.g. `frame_system` in `System: frame_system`. + pub path: PalletPath, + /// The instance of the pallet, e.g. `Instance1` in `Council: pallet_collective::`. + pub instance: Option, + /// The declared pallet parts, + /// e.g. `Some([Pallet, Call])` for `System: system::{Pallet, Call}` + /// or `None` for `System: system`. + pub pallet_parts: Option>, + /// The specified parts, either use_parts or exclude_parts. + pub specified_parts: SpecifiedParts, +} + +/// The possible declaration of pallet parts to use. +#[derive(Debug, Clone)] +pub enum SpecifiedParts { + /// Use all the pallet parts except those specified. + Exclude(Vec), + /// Use only the specified pallet parts. + Use(Vec), + /// Use the all the pallet parts. + All, +} + +impl Parse for PalletDeclaration { + fn parse(input: ParseStream) -> Result { + let attrs = input.call(Attribute::parse_outer)?; + + let name = input.parse()?; + let _: Token![:] = input.parse()?; + let path = input.parse()?; + + // Parse for instance. + 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 if !(input.peek(Token![::]) && input.peek3(token::Brace)) && + !input.peek(keyword::expanded) && + !input.peek(keyword::exclude_parts) && + !input.peek(keyword::use_parts) && + !input.peek(Token![=]) && + !input.peek(Token![,]) && + !input.is_empty() + { + return Err(input.error( + "Unexpected tokens, expected one of `::$ident` `::{`, `exclude_parts`, `use_parts`, `=`, `,`", + )); + } else { + None + }; + + // Check if the pallet is fully expanded. + let (is_expanded, extra_parts) = if input.peek(keyword::expanded) { + let _: keyword::expanded = input.parse()?; + let _: Token![::] = input.parse()?; + (true, parse_pallet_parts(input)?) + } else { + (false, vec![]) + }; + + // Parse for explicit parts + let pallet_parts = if input.peek(Token![::]) && input.peek3(token::Brace) { + let _: Token![::] = input.parse()?; + let mut parts = parse_pallet_parts(input)?; + parts.extend(extra_parts.into_iter()); + Some(parts) + } else if !input.peek(keyword::exclude_parts) && + !input.peek(keyword::use_parts) && + !input.peek(Token![=]) && + !input.peek(Token![,]) && + !input.is_empty() + { + return Err(input.error( + "Unexpected tokens, expected one of `::{`, `exclude_parts`, `use_parts`, `=`, `,`", + )) + } else { + is_expanded.then_some(extra_parts) + }; + + // Parse for specified parts + let specified_parts = if input.peek(keyword::exclude_parts) { + let _: keyword::exclude_parts = input.parse()?; + SpecifiedParts::Exclude(parse_pallet_parts_no_generic(input)?) + } else if input.peek(keyword::use_parts) { + let _: keyword::use_parts = input.parse()?; + SpecifiedParts::Use(parse_pallet_parts_no_generic(input)?) + } else if !input.peek(Token![=]) && !input.peek(Token![,]) && !input.is_empty() { + return Err(input.error("Unexpected tokens, expected one of `exclude_parts`, `=`, `,`")) + } else { + SpecifiedParts::All + }; + + // Parse for pallet index + let index = if input.peek(Token![=]) { + input.parse::()?; + let index = input.parse::()?; + let index = index.base10_parse::()?; + Some(index) + } else if !input.peek(Token![,]) && !input.is_empty() { + return Err(input.error("Unexpected tokens, expected one of `=`, `,`")) + } else { + None + }; + + Ok(Self { is_expanded, attrs, name, path, instance, pallet_parts, specified_parts, index }) + } +} + +/// A struct representing a path to a pallet. `PalletPath` is almost identical to the standard +/// Rust path with a few restrictions: +/// - No leading colons allowed +/// - Path segments can only consist of identifiers separated by colons +#[derive(Debug, Clone)] +pub struct PalletPath { + pub inner: Path, +} + +impl PalletPath { + pub fn module_name(&self) -> String { + self.inner.segments.iter().fold(String::new(), |mut acc, segment| { + if !acc.is_empty() { + acc.push_str("::"); + } + acc.push_str(&segment.ident.to_string()); + acc + }) + } +} + +impl Parse for PalletPath { + fn parse(input: ParseStream) -> Result { + let mut res = + PalletPath { inner: Path { leading_colon: None, segments: Punctuated::new() } }; + + let lookahead = input.lookahead1(); + if lookahead.peek(Token![crate]) || + lookahead.peek(Token![self]) || + lookahead.peek(Token![super]) || + lookahead.peek(Ident) + { + let ident = input.call(Ident::parse_any)?; + res.inner.segments.push(ident.into()); + } else { + return Err(lookahead.error()) + } + + while input.peek(Token![::]) && input.peek3(Ident) { + input.parse::()?; + let ident = input.parse::()?; + res.inner.segments.push(ident.into()); + } + Ok(res) + } +} + +impl quote::ToTokens for PalletPath { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.inner.to_tokens(tokens); + } +} + +/// Parse [`PalletPart`]'s from a braces enclosed list that is split by commas, e.g. +/// +/// `{ Call, Event }` +fn parse_pallet_parts(input: ParseStream) -> Result> { + let pallet_parts: ext::Braces> = input.parse()?; + + let mut resolved = HashSet::new(); + for part in pallet_parts.content.inner.iter() { + if !resolved.insert(part.name()) { + let msg = format!( + "`{}` was already declared before. Please remove the duplicate declaration", + part.name(), + ); + return Err(Error::new(part.keyword.span(), msg)) + } + } + + Ok(pallet_parts.content.inner.into_iter().collect()) +} + +#[derive(Debug, Clone)] +pub enum PalletPartKeyword { + Pallet(keyword::Pallet), + Call(keyword::Call), + Storage(keyword::Storage), + Event(keyword::Event), + Error(keyword::Error), + Config(keyword::Config), + Origin(keyword::Origin), + Inherent(keyword::Inherent), + ValidateUnsigned(keyword::ValidateUnsigned), + FreezeReason(keyword::FreezeReason), + HoldReason(keyword::HoldReason), + Task(keyword::Task), + LockId(keyword::LockId), + SlashReason(keyword::SlashReason), +} + +impl Parse for PalletPartKeyword { + fn parse(input: ParseStream) -> Result { + let lookahead = input.lookahead1(); + + if lookahead.peek(keyword::Pallet) { + Ok(Self::Pallet(input.parse()?)) + } else if lookahead.peek(keyword::Call) { + Ok(Self::Call(input.parse()?)) + } else if lookahead.peek(keyword::Storage) { + Ok(Self::Storage(input.parse()?)) + } else if lookahead.peek(keyword::Event) { + Ok(Self::Event(input.parse()?)) + } else if lookahead.peek(keyword::Error) { + Ok(Self::Error(input.parse()?)) + } else if lookahead.peek(keyword::Config) { + Ok(Self::Config(input.parse()?)) + } else if lookahead.peek(keyword::Origin) { + Ok(Self::Origin(input.parse()?)) + } else if lookahead.peek(keyword::Inherent) { + Ok(Self::Inherent(input.parse()?)) + } else if lookahead.peek(keyword::ValidateUnsigned) { + Ok(Self::ValidateUnsigned(input.parse()?)) + } else if lookahead.peek(keyword::FreezeReason) { + Ok(Self::FreezeReason(input.parse()?)) + } else if lookahead.peek(keyword::HoldReason) { + Ok(Self::HoldReason(input.parse()?)) + } else if lookahead.peek(keyword::Task) { + Ok(Self::Task(input.parse()?)) + } else if lookahead.peek(keyword::LockId) { + Ok(Self::LockId(input.parse()?)) + } else if lookahead.peek(keyword::SlashReason) { + Ok(Self::SlashReason(input.parse()?)) + } else { + Err(lookahead.error()) + } + } +} + +impl PalletPartKeyword { + /// Returns the name of `Self`. + fn name(&self) -> &'static str { + match self { + Self::Pallet(_) => "Pallet", + Self::Call(_) => "Call", + Self::Storage(_) => "Storage", + Self::Event(_) => "Event", + Self::Error(_) => "Error", + Self::Config(_) => "Config", + Self::Origin(_) => "Origin", + Self::Inherent(_) => "Inherent", + Self::ValidateUnsigned(_) => "ValidateUnsigned", + Self::FreezeReason(_) => "FreezeReason", + Self::HoldReason(_) => "HoldReason", + Self::Task(_) => "Task", + Self::LockId(_) => "LockId", + Self::SlashReason(_) => "SlashReason", + } + } + + /// Returns `true` if this pallet part is allowed to have generic arguments. + fn allows_generic(&self) -> bool { + Self::all_generic_arg().iter().any(|n| *n == self.name()) + } + + /// Returns the names of all pallet parts that allow to have a generic argument. + fn all_generic_arg() -> &'static [&'static str] { + &["Event", "Error", "Origin", "Config", "Task"] + } +} + +impl ToTokens for PalletPartKeyword { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + Self::Pallet(inner) => inner.to_tokens(tokens), + Self::Call(inner) => inner.to_tokens(tokens), + Self::Storage(inner) => inner.to_tokens(tokens), + Self::Event(inner) => inner.to_tokens(tokens), + Self::Error(inner) => inner.to_tokens(tokens), + Self::Config(inner) => inner.to_tokens(tokens), + Self::Origin(inner) => inner.to_tokens(tokens), + Self::Inherent(inner) => inner.to_tokens(tokens), + Self::ValidateUnsigned(inner) => inner.to_tokens(tokens), + Self::FreezeReason(inner) => inner.to_tokens(tokens), + Self::HoldReason(inner) => inner.to_tokens(tokens), + Self::Task(inner) => inner.to_tokens(tokens), + Self::LockId(inner) => inner.to_tokens(tokens), + Self::SlashReason(inner) => inner.to_tokens(tokens), + } + } +} + +#[derive(Debug, Clone)] +pub struct PalletPart { + pub keyword: PalletPartKeyword, + pub generics: syn::Generics, +} + +impl Parse for PalletPart { + fn parse(input: ParseStream) -> Result { + let keyword: PalletPartKeyword = input.parse()?; + + let generics: syn::Generics = input.parse()?; + if !generics.params.is_empty() && !keyword.allows_generic() { + let valid_generics = PalletPart::format_names(PalletPartKeyword::all_generic_arg()); + let msg = format!( + "`{}` is not allowed to have generics. \ + Only the following pallets are allowed to have generics: {}.", + keyword.name(), + valid_generics, + ); + return Err(syn::Error::new(keyword.span(), msg)) + } + + Ok(Self { keyword, generics }) + } +} + +impl PalletPart { + pub fn format_names(names: &[&'static str]) -> String { + let res: Vec<_> = names.iter().map(|s| format!("`{}`", s)).collect(); + res.join(", ") + } + + /// The name of this pallet part. + pub fn name(&self) -> &'static str { + self.keyword.name() + } +} + +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)) + } +} + +/// The declaration of a part without its generics +#[derive(Debug, Clone)] +pub struct PalletPartNoGeneric { + keyword: PalletPartKeyword, +} + +impl Parse for PalletPartNoGeneric { + fn parse(input: ParseStream) -> Result { + Ok(Self { keyword: input.parse()? }) + } +} + +/// Parse [`PalletPartNoGeneric`]'s from a braces enclosed list that is split by commas, e.g. +/// +/// `{ Call, Event }` +fn parse_pallet_parts_no_generic(input: ParseStream) -> Result> { + let pallet_parts: ext::Braces> = + input.parse()?; + + let mut resolved = HashSet::new(); + for part in pallet_parts.content.inner.iter() { + if !resolved.insert(part.keyword.name()) { + let msg = format!( + "`{}` was already declared before. Please remove the duplicate declaration", + part.keyword.name(), + ); + return Err(Error::new(part.keyword.span(), msg)) + } + } + + Ok(pallet_parts.content.inner.into_iter().collect()) +} + +/// The final definition of a pallet with the resulting fixed index and explicit parts. +#[derive(Debug, Clone)] +pub struct Pallet { + /// Is this pallet fully expanded? + pub is_expanded: bool, + /// The name of the pallet, e.g.`System` in `System: frame_system`. + pub name: Ident, + /// Either automatically inferred, or defined (e.g. `MyPallet ... = 3,`). + pub index: u8, + /// The path of the pallet, e.g. `frame_system` in `System: frame_system`. + pub path: PalletPath, + /// The instance of the pallet, e.g. `Instance1` in `Council: pallet_collective::`. + pub instance: Option, + /// The pallet parts to use for the pallet. + pub pallet_parts: Vec, + /// Expressions specified inside of a #[cfg] attribute. + pub cfg_pattern: Vec, +} + +impl Pallet { + /// Get resolved pallet parts + pub fn pallet_parts(&self) -> &[PalletPart] { + &self.pallet_parts + } + + /// Find matching parts + pub fn find_part(&self, name: &str) -> Option<&PalletPart> { + self.pallet_parts.iter().find(|part| part.name() == name) + } + + /// Return whether pallet contains part + pub fn exists_part(&self, name: &str) -> bool { + self.find_part(name).is_some() + } +} + +/// Result of a conversion of a declaration of pallets. +/// +/// # State Transitions +/// +/// ```ignore +/// +----------+ +----------+ +------------------+ +/// | Implicit | -> | Explicit | -> | ExplicitExpanded | +/// +----------+ +----------+ +------------------+ +/// ``` +enum PalletsConversion { + /// Pallets implicitly declare parts. + /// + /// `System: frame_system`. + Implicit(Vec), + /// Pallets explicitly declare parts. + /// + /// `System: frame_system::{Pallet, Call}` + /// + /// However, for backwards compatibility with Polkadot/Kusama + /// we must propagate some other parts to the pallet by default. + Explicit(Vec), + /// Pallets explicitly declare parts that are fully expanded. + /// + /// This is the end state that contains extra parts included by + /// default by Substrate. + /// + /// `System: frame_system expanded::{Error} ::{Pallet, Call}` + /// + /// For this example, the `Pallet`, `Call` and `Error` parts are collected. + ExplicitExpanded(Vec), +} + +/// Convert from the parsed pallet declaration to their final information. +/// +/// Check if all pallet have explicit declaration of their parts, if so then assign index to each +/// pallet using same rules as rust for fieldless enum. I.e. implicit are assigned number +/// incrementally from last explicit or 0. +fn convert_pallets(pallets: Vec) -> syn::Result { + if pallets.iter().any(|pallet| pallet.pallet_parts.is_none()) { + return Ok(PalletsConversion::Implicit(pallets)) + } + + let mut indices = HashMap::new(); + let mut last_index: Option = None; + let mut names = HashMap::new(); + let mut is_expanded = true; + + let pallets = pallets + .into_iter() + .map(|pallet| { + let final_index = match pallet.index { + Some(i) => i, + None => last_index.map_or(Some(0), |i| i.checked_add(1)).ok_or_else(|| { + let msg = "Pallet index doesn't fit into u8, index is 256"; + syn::Error::new(pallet.name.span(), msg) + })?, + }; + + last_index = Some(final_index); + + if let Some(used_pallet) = indices.insert(final_index, pallet.name.clone()) { + let msg = format!( + "Pallet indices are conflicting: Both pallets {} and {} are at index {}", + used_pallet, pallet.name, final_index, + ); + let mut err = syn::Error::new(used_pallet.span(), &msg); + err.combine(syn::Error::new(pallet.name.span(), msg)); + return Err(err) + } + + if let Some(used_pallet) = names.insert(pallet.name.clone(), pallet.name.span()) { + let msg = "Two pallets with the same name!"; + + let mut err = syn::Error::new(used_pallet, &msg); + err.combine(syn::Error::new(pallet.name.span(), &msg)); + return Err(err) + } + + let mut pallet_parts = pallet.pallet_parts.expect("Checked above"); + + let available_parts = + pallet_parts.iter().map(|part| part.keyword.name()).collect::>(); + + // Check parts are correctly specified + match &pallet.specified_parts { + SpecifiedParts::Exclude(parts) | SpecifiedParts::Use(parts) => + for part in parts { + if !available_parts.contains(part.keyword.name()) { + let msg = format!( + "Invalid pallet part specified, the pallet `{}` doesn't have the \ + `{}` part. Available parts are: {}.", + pallet.name, + part.keyword.name(), + pallet_parts.iter().fold(String::new(), |fold, part| { + if fold.is_empty() { + format!("`{}`", part.keyword.name()) + } else { + format!("{}, `{}`", fold, part.keyword.name()) + } + }) + ); + return Err(syn::Error::new(part.keyword.span(), msg)) + } + }, + SpecifiedParts::All => (), + } + + // Set only specified parts. + match pallet.specified_parts { + SpecifiedParts::Exclude(excluded_parts) => pallet_parts.retain(|part| { + !excluded_parts + .iter() + .any(|excluded_part| excluded_part.keyword.name() == part.keyword.name()) + }), + SpecifiedParts::Use(used_parts) => pallet_parts.retain(|part| { + used_parts.iter().any(|use_part| use_part.keyword.name() == part.keyword.name()) + }), + SpecifiedParts::All => (), + } + + let cfg_pattern = pallet + .attrs + .iter() + .map(|attr| { + if attr.path().segments.first().map_or(false, |s| s.ident != "cfg") { + let msg = "Unsupported attribute, only #[cfg] is supported on pallet \ + declarations in `construct_runtime`"; + return Err(syn::Error::new(attr.span(), msg)) + } + + attr.parse_args_with(|input: syn::parse::ParseStream| { + // Required, otherwise the parse stream doesn't advance and will result in + // an error. + let input = input.parse::()?; + cfg_expr::Expression::parse(&input.to_string()) + .map_err(|e| syn::Error::new(attr.span(), e.to_string())) + }) + }) + .collect::>>()?; + + is_expanded &= pallet.is_expanded; + + Ok(Pallet { + is_expanded: pallet.is_expanded, + name: pallet.name, + index: final_index, + path: pallet.path, + instance: pallet.instance, + cfg_pattern, + pallet_parts, + }) + }) + .collect::>>()?; + + if is_expanded { + Ok(PalletsConversion::ExplicitExpanded(pallets)) + } else { + Ok(PalletsConversion::Explicit(pallets)) + } +} diff --git a/support/procedural-fork/src/crate_version.rs b/support/procedural-fork/src/crate_version.rs new file mode 100644 index 000000000..8c8975a42 --- /dev/null +++ b/support/procedural-fork/src/crate_version.rs @@ -0,0 +1,54 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Implementation of macros related to crate versioning. + +use super::get_cargo_env_var; +use frame_support_procedural_tools::generate_access_from_frame_or_crate; +use proc_macro2::{Span, TokenStream}; +use syn::{Error, Result}; + +/// Create an error that will be shown by rustc at the call site of the macro. +fn create_error(message: &str) -> Error { + Error::new(Span::call_site(), message) +} + +/// Implementation of the `crate_to_crate_version!` macro. +pub fn crate_to_crate_version(input: proc_macro::TokenStream) -> Result { + if !input.is_empty() { + return Err(create_error("No arguments expected!")) + } + + let major_version = get_cargo_env_var::("CARGO_PKG_VERSION_MAJOR") + .map_err(|_| create_error("Major version needs to fit into `u16`"))?; + + let minor_version = get_cargo_env_var::("CARGO_PKG_VERSION_MINOR") + .map_err(|_| create_error("Minor version needs to fit into `u8`"))?; + + let patch_version = get_cargo_env_var::("CARGO_PKG_VERSION_PATCH") + .map_err(|_| create_error("Patch version needs to fit into `u8`"))?; + + let crate_ = generate_access_from_frame_or_crate("frame-support")?; + + Ok(quote::quote! { + #crate_::traits::CrateVersion { + major: #major_version, + minor: #minor_version, + patch: #patch_version, + } + }) +} diff --git a/support/procedural-fork/src/derive_impl.rs b/support/procedural-fork/src/derive_impl.rs new file mode 100644 index 000000000..54755f116 --- /dev/null +++ b/support/procedural-fork/src/derive_impl.rs @@ -0,0 +1,303 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Implementation of the `derive_impl` attribute macro. + +use derive_syn_parse::Parse; +use macro_magic::mm_core::ForeignPath; +use proc_macro2::TokenStream as TokenStream2; +use quote::{quote, ToTokens}; +use std::collections::HashSet; +use syn::{ + parse2, parse_quote, spanned::Spanned, token, Ident, ImplItem, ItemImpl, Path, Result, Token, +}; + +mod keyword { + syn::custom_keyword!(inject_runtime_type); + syn::custom_keyword!(no_aggregated_types); +} + +#[derive(derive_syn_parse::Parse, PartialEq, Eq)] +pub enum PalletAttrType { + #[peek(keyword::inject_runtime_type, name = "inject_runtime_type")] + RuntimeType(keyword::inject_runtime_type), +} + +#[derive(derive_syn_parse::Parse)] +pub struct PalletAttr { + _pound: Token![#], + #[bracket] + _bracket: token::Bracket, + #[inside(_bracket)] + typ: PalletAttrType, +} + +fn is_runtime_type(item: &syn::ImplItemType) -> bool { + item.attrs.iter().any(|attr| { + if let Ok(PalletAttr { typ: PalletAttrType::RuntimeType(_), .. }) = + parse2::(attr.into_token_stream()) + { + return true + } + false + }) +} + +#[derive(Parse, Debug)] +pub struct DeriveImplAttrArgs { + pub default_impl_path: Path, + _as: Option, + #[parse_if(_as.is_some())] + pub disambiguation_path: Option, + _comma: Option, + #[parse_if(_comma.is_some())] + pub no_aggregated_types: Option, +} + +impl ForeignPath for DeriveImplAttrArgs { + fn foreign_path(&self) -> &Path { + &self.default_impl_path + } +} + +impl ToTokens for DeriveImplAttrArgs { + fn to_tokens(&self, tokens: &mut TokenStream2) { + tokens.extend(self.default_impl_path.to_token_stream()); + tokens.extend(self._as.to_token_stream()); + tokens.extend(self.disambiguation_path.to_token_stream()); + tokens.extend(self._comma.to_token_stream()); + tokens.extend(self.no_aggregated_types.to_token_stream()); + } +} + +/// Gets the [`Ident`] representation of the given [`ImplItem`], if one exists. Otherwise +/// returns [`None`]. +/// +/// Used by [`combine_impls`] to determine whether we can compare [`ImplItem`]s by [`Ident`] +/// or not. +fn impl_item_ident(impl_item: &ImplItem) -> Option<&Ident> { + match impl_item { + ImplItem::Const(item) => Some(&item.ident), + ImplItem::Fn(item) => Some(&item.sig.ident), + ImplItem::Type(item) => Some(&item.ident), + ImplItem::Macro(item) => item.mac.path.get_ident(), + _ => None, + } +} + +/// The real meat behind `derive_impl`. Takes in a `local_impl`, which is the impl for which we +/// want to implement defaults (i.e. the one the attribute macro is attached to), and a +/// `foreign_impl`, which is the impl containing the defaults we want to use, and returns an +/// [`ItemImpl`] containing the final generated impl. +/// +/// This process has the following caveats: +/// * Colliding items that have an ident are not copied into `local_impl` +/// * Uncolliding items that have an ident are copied into `local_impl` but are qualified as `type +/// #ident = <#default_impl_path as #disambiguation_path>::#ident;` +/// * Items that lack an ident are de-duplicated so only unique items that lack an ident are copied +/// into `local_impl`. Items that lack an ident and also exist verbatim in `local_impl` are not +/// copied over. +fn combine_impls( + local_impl: ItemImpl, + foreign_impl: ItemImpl, + default_impl_path: Path, + disambiguation_path: Path, + inject_runtime_types: bool, +) -> ItemImpl { + let (existing_local_keys, existing_unsupported_items): (HashSet, HashSet) = + local_impl + .items + .iter() + .cloned() + .partition(|impl_item| impl_item_ident(impl_item).is_some()); + let existing_local_keys: HashSet = existing_local_keys + .into_iter() + .filter_map(|item| impl_item_ident(&item).cloned()) + .collect(); + let mut final_impl = local_impl; + let extended_items = foreign_impl.items.into_iter().filter_map(|item| { + if let Some(ident) = impl_item_ident(&item) { + if existing_local_keys.contains(&ident) { + // do not copy colliding items that have an ident + return None + } + if let ImplItem::Type(typ) = item.clone() { + let cfg_attrs = typ + .attrs + .iter() + .filter(|attr| attr.path().get_ident().map_or(false, |ident| ident == "cfg")) + .map(|attr| attr.to_token_stream()); + if is_runtime_type(&typ) { + let item: ImplItem = if inject_runtime_types { + parse_quote! { + #( #cfg_attrs )* + type #ident = #ident; + } + } else { + item + }; + return Some(item) + } + // modify and insert uncolliding type items + let modified_item: ImplItem = parse_quote! { + #( #cfg_attrs )* + type #ident = <#default_impl_path as #disambiguation_path>::#ident; + }; + return Some(modified_item) + } + // copy uncolliding non-type items that have an ident + Some(item) + } else { + // do not copy colliding items that lack an ident + (!existing_unsupported_items.contains(&item)) + // copy uncolliding items without an ident verbatim + .then_some(item) + } + }); + final_impl.items.extend(extended_items); + final_impl +} + +/// Computes the disambiguation path for the `derive_impl` attribute macro. +/// +/// When specified explicitly using `as [disambiguation_path]` in the macro attr, the +/// disambiguation is used as is. If not, we infer the disambiguation path from the +/// `foreign_impl_path` and the computed scope. +fn compute_disambiguation_path( + disambiguation_path: Option, + foreign_impl: ItemImpl, + default_impl_path: Path, +) -> Result { + match (disambiguation_path, foreign_impl.clone().trait_) { + (Some(disambiguation_path), _) => Ok(disambiguation_path), + (None, Some((_, foreign_impl_path, _))) => + if default_impl_path.segments.len() > 1 { + let scope = default_impl_path.segments.first(); + Ok(parse_quote!(#scope :: #foreign_impl_path)) + } else { + Ok(foreign_impl_path) + }, + _ => Err(syn::Error::new( + default_impl_path.span(), + "Impl statement must have a defined type being implemented \ + for a defined type such as `impl A for B`", + )), + } +} + +/// Internal implementation behind [`#[derive_impl(..)]`](`macro@crate::derive_impl`). +/// +/// `default_impl_path`: the module path of the external `impl` statement whose tokens we are +/// importing via `macro_magic` +/// +/// `foreign_tokens`: the tokens for the external `impl` statement +/// +/// `local_tokens`: the tokens for the local `impl` statement this attribute is attached to +/// +/// `disambiguation_path`: the module path of the external trait we will use to qualify +/// defaults imported from the external `impl` statement +pub fn derive_impl( + default_impl_path: TokenStream2, + foreign_tokens: TokenStream2, + local_tokens: TokenStream2, + disambiguation_path: Option, + no_aggregated_types: Option, +) -> Result { + let local_impl = parse2::(local_tokens)?; + let foreign_impl = parse2::(foreign_tokens)?; + let default_impl_path = parse2::(default_impl_path)?; + + let disambiguation_path = compute_disambiguation_path( + disambiguation_path, + foreign_impl.clone(), + default_impl_path.clone(), + )?; + + // generate the combined impl + let combined_impl = combine_impls( + local_impl, + foreign_impl, + default_impl_path, + disambiguation_path, + no_aggregated_types.is_none(), + ); + + Ok(quote!(#combined_impl)) +} + +#[test] +fn test_derive_impl_attr_args_parsing() { + parse2::(quote!( + some::path::TestDefaultConfig as some::path::DefaultConfig + )) + .unwrap(); + parse2::(quote!( + frame_system::prelude::testing::TestDefaultConfig as DefaultConfig + )) + .unwrap(); + parse2::(quote!(Something as some::path::DefaultConfig)).unwrap(); + parse2::(quote!(Something as DefaultConfig)).unwrap(); + parse2::(quote!(DefaultConfig)).unwrap(); + assert!(parse2::(quote!()).is_err()); + assert!(parse2::(quote!(Config Config)).is_err()); +} + +#[test] +fn test_runtime_type_with_doc() { + trait TestTrait { + type Test; + } + #[allow(unused)] + struct TestStruct; + let p = parse2::(quote!( + impl TestTrait for TestStruct { + /// Some doc + #[inject_runtime_type] + type Test = u32; + } + )) + .unwrap(); + for item in p.items { + if let ImplItem::Type(typ) = item { + assert_eq!(is_runtime_type(&typ), true); + } + } +} + +#[test] +fn test_disambiguation_path() { + let foreign_impl: ItemImpl = parse_quote!(impl SomeTrait for SomeType {}); + let default_impl_path: Path = parse_quote!(SomeScope::SomeType); + + // disambiguation path is specified + let disambiguation_path = compute_disambiguation_path( + Some(parse_quote!(SomeScope::SomePath)), + foreign_impl.clone(), + default_impl_path.clone(), + ); + assert_eq!(disambiguation_path.unwrap(), parse_quote!(SomeScope::SomePath)); + + // disambiguation path is not specified and the default_impl_path has more than one segment + let disambiguation_path = + compute_disambiguation_path(None, foreign_impl.clone(), default_impl_path.clone()); + assert_eq!(disambiguation_path.unwrap(), parse_quote!(SomeScope::SomeTrait)); + + // disambiguation path is not specified and the default_impl_path has only one segment + let disambiguation_path = + compute_disambiguation_path(None, foreign_impl.clone(), parse_quote!(SomeType)); + assert_eq!(disambiguation_path.unwrap(), parse_quote!(SomeTrait)); +} diff --git a/support/procedural-fork/src/dummy_part_checker.rs b/support/procedural-fork/src/dummy_part_checker.rs new file mode 100644 index 000000000..34d9a3e23 --- /dev/null +++ b/support/procedural-fork/src/dummy_part_checker.rs @@ -0,0 +1,79 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::COUNTER; +use proc_macro::TokenStream; + +pub fn generate_dummy_part_checker(input: TokenStream) -> TokenStream { + if !input.is_empty() { + return syn::Error::new(proc_macro2::Span::call_site(), "No arguments expected") + .to_compile_error() + .into() + } + + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + + let no_op_macro_ident = + syn::Ident::new(&format!("__dummy_part_checker_{}", count), proc_macro2::Span::call_site()); + + quote::quote!( + #[macro_export] + #[doc(hidden)] + macro_rules! #no_op_macro_ident { + ( $( $tt:tt )* ) => {}; + } + + #[doc(hidden)] + pub mod __substrate_genesis_config_check { + #[doc(hidden)] + pub use #no_op_macro_ident as is_genesis_config_defined; + #[doc(hidden)] + pub use #no_op_macro_ident as is_std_enabled_for_genesis; + } + + #[doc(hidden)] + pub mod __substrate_event_check { + #[doc(hidden)] + pub use #no_op_macro_ident as is_event_part_defined; + } + + #[doc(hidden)] + pub mod __substrate_inherent_check { + #[doc(hidden)] + pub use #no_op_macro_ident as is_inherent_part_defined; + } + + #[doc(hidden)] + pub mod __substrate_validate_unsigned_check { + #[doc(hidden)] + pub use #no_op_macro_ident as is_validate_unsigned_part_defined; + } + + #[doc(hidden)] + pub mod __substrate_call_check { + #[doc(hidden)] + pub use #no_op_macro_ident as is_call_part_defined; + } + + #[doc(hidden)] + pub mod __substrate_origin_check { + #[doc(hidden)] + pub use #no_op_macro_ident as is_origin_part_defined; + } + ) + .into() +} diff --git a/support/procedural-fork/src/dynamic_params.rs b/support/procedural-fork/src/dynamic_params.rs new file mode 100644 index 000000000..29399a885 --- /dev/null +++ b/support/procedural-fork/src/dynamic_params.rs @@ -0,0 +1,563 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Code for the `#[dynamic_params]`, `#[dynamic_pallet_params]` and +//! `#[dynamic_aggregated_params_internal]` macros. + +use frame_support_procedural_tools::generate_access_from_frame_or_crate; +use inflector::Inflector; +use proc_macro2::{Span, TokenStream}; +use quote::{format_ident, quote, ToTokens}; +use syn::{parse2, spanned::Spanned, visit_mut, visit_mut::VisitMut, Result, Token}; + +/// Parse and expand a `#[dynamic_params(..)]` module. +pub fn dynamic_params(attr: TokenStream, item: TokenStream) -> Result { + DynamicParamModAttr::parse(attr, item).map(ToTokens::into_token_stream) +} + +/// Parse and expand `#[dynamic_pallet_params(..)]` attribute. +pub fn dynamic_pallet_params(attr: TokenStream, item: TokenStream) -> Result { + DynamicPalletParamAttr::parse(attr, item).map(ToTokens::into_token_stream) +} + +/// Parse and expand `#[dynamic_aggregated_params_internal]` attribute. +pub fn dynamic_aggregated_params_internal( + _attr: TokenStream, + item: TokenStream, +) -> Result { + parse2::(item).map(ToTokens::into_token_stream) +} + +/// A top `#[dynamic_params(..)]` attribute together with a mod. +#[derive(derive_syn_parse::Parse)] +pub struct DynamicParamModAttr { + params_mod: syn::ItemMod, + meta: DynamicParamModAttrMeta, +} + +/// The inner meta of a `#[dynamic_params(..)]` attribute. +#[derive(derive_syn_parse::Parse)] +pub struct DynamicParamModAttrMeta { + name: syn::Ident, + _comma: Option, + #[parse_if(_comma.is_some())] + params_pallet: Option, +} + +impl DynamicParamModAttr { + pub fn parse(attr: TokenStream, item: TokenStream) -> Result { + let params_mod = parse2(item)?; + let meta = parse2(attr)?; + Ok(Self { params_mod, meta }) + } + + pub fn inner_mods(&self) -> Vec { + self.params_mod.content.as_ref().map_or(Vec::new(), |(_, items)| { + items + .iter() + .filter_map(|i| match i { + syn::Item::Mod(m) => Some(m), + _ => None, + }) + .cloned() + .collect() + }) + } +} + +impl ToTokens for DynamicParamModAttr { + fn to_tokens(&self, tokens: &mut TokenStream) { + let scrate = match crate_access() { + Ok(path) => path, + Err(err) => return tokens.extend(err), + }; + let (mut params_mod, name) = (self.params_mod.clone(), &self.meta.name); + let dynam_params_ident = ¶ms_mod.ident; + + let mut quoted_enum = quote! {}; + for m in self.inner_mods() { + let aggregate_name = + syn::Ident::new(&m.ident.to_string().to_class_case(), m.ident.span()); + let mod_name = &m.ident; + + let mut attrs = m.attrs.clone(); + attrs.retain(|attr| !attr.path().is_ident("dynamic_pallet_params")); + if let Err(err) = ensure_codec_index(&attrs, m.span()) { + tokens.extend(err.into_compile_error()); + return + } + + quoted_enum.extend(quote! { + #(#attrs)* + #aggregate_name(#dynam_params_ident::#mod_name::Parameters), + }); + } + + // Inject the outer args into the inner `#[dynamic_pallet_params(..)]` attribute. + if let Some(params_pallet) = &self.meta.params_pallet { + MacroInjectArgs { runtime_params: name.clone(), params_pallet: params_pallet.clone() } + .visit_item_mod_mut(&mut params_mod); + } + + tokens.extend(quote! { + #params_mod + + #[#scrate::dynamic_params::dynamic_aggregated_params_internal] + pub enum #name { + #quoted_enum + } + }); + } +} + +/// Ensure there is a `#[codec(index = ..)]` attribute. +fn ensure_codec_index(attrs: &Vec, span: Span) -> Result<()> { + let mut found = false; + + for attr in attrs.iter() { + if attr.path().is_ident("codec") { + let meta: syn::ExprAssign = attr.parse_args()?; + if meta.left.to_token_stream().to_string() == "index" { + found = true; + break + } + } + } + + if !found { + Err(syn::Error::new(span, "Missing explicit `#[codec(index = ..)]` attribute")) + } else { + Ok(()) + } +} + +/// Used to inject arguments into the inner `#[dynamic_pallet_params(..)]` attribute. +/// +/// This allows the outer `#[dynamic_params(..)]` attribute to specify some arguments that don't +/// need to be repeated every time. +struct MacroInjectArgs { + runtime_params: syn::Ident, + params_pallet: syn::Type, +} +impl VisitMut for MacroInjectArgs { + fn visit_item_mod_mut(&mut self, item: &mut syn::ItemMod) { + // Check if the mod has a `#[dynamic_pallet_params(..)]` attribute. + let attr = item.attrs.iter_mut().find(|attr| attr.path().is_ident("dynamic_pallet_params")); + + if let Some(attr) = attr { + match &attr.meta { + syn::Meta::Path(path) => + assert_eq!(path.to_token_stream().to_string(), "dynamic_pallet_params"), + _ => (), + } + + let runtime_params = &self.runtime_params; + let params_pallet = &self.params_pallet; + + attr.meta = syn::parse2::(quote! { + dynamic_pallet_params(#runtime_params, #params_pallet) + }) + .unwrap() + .into(); + } + + visit_mut::visit_item_mod_mut(self, item); + } +} +/// The helper attribute of a `#[dynamic_pallet_params(runtime_params, params_pallet)]` +/// attribute. +#[derive(derive_syn_parse::Parse)] +pub struct DynamicPalletParamAttr { + inner_mod: syn::ItemMod, + meta: DynamicPalletParamAttrMeta, +} + +/// The inner meta of a `#[dynamic_pallet_params(..)]` attribute. +#[derive(derive_syn_parse::Parse)] +pub struct DynamicPalletParamAttrMeta { + runtime_params: syn::Ident, + _comma: Token![,], + parameter_pallet: syn::Type, +} + +impl DynamicPalletParamAttr { + pub fn parse(attr: TokenStream, item: TokenStream) -> Result { + Ok(Self { inner_mod: parse2(item)?, meta: parse2(attr)? }) + } + + pub fn statics(&self) -> Vec { + self.inner_mod.content.as_ref().map_or(Vec::new(), |(_, items)| { + items + .iter() + .filter_map(|i| match i { + syn::Item::Static(s) => Some(s), + _ => None, + }) + .cloned() + .collect() + }) + } +} + +impl ToTokens for DynamicPalletParamAttr { + fn to_tokens(&self, tokens: &mut TokenStream) { + let scrate = match crate_access() { + Ok(path) => path, + Err(err) => return tokens.extend(err), + }; + let (params_mod, parameter_pallet, runtime_params) = + (&self.inner_mod, &self.meta.parameter_pallet, &self.meta.runtime_params); + + let aggregate_name = + syn::Ident::new(¶ms_mod.ident.to_string().to_class_case(), params_mod.ident.span()); + let (mod_name, vis) = (¶ms_mod.ident, ¶ms_mod.vis); + let statics = self.statics(); + + let (mut key_names, mut key_values, mut defaults, mut attrs, mut value_types): ( + Vec<_>, + Vec<_>, + Vec<_>, + Vec<_>, + Vec<_>, + ) = Default::default(); + + for s in statics.iter() { + if let Err(err) = ensure_codec_index(&s.attrs, s.span()) { + tokens.extend(err.into_compile_error()); + return + } + + key_names.push(&s.ident); + key_values.push(format_ident!("{}Value", &s.ident)); + defaults.push(&s.expr); + attrs.push(&s.attrs); + value_types.push(&s.ty); + } + + let key_ident = syn::Ident::new("ParametersKey", params_mod.ident.span()); + let value_ident = syn::Ident::new("ParametersValue", params_mod.ident.span()); + let runtime_key_ident = format_ident!("{}Key", runtime_params); + let runtime_value_ident = format_ident!("{}Value", runtime_params); + + tokens.extend(quote! { + pub mod #mod_name { + use super::*; + + #[doc(hidden)] + #[derive( + Clone, + PartialEq, + Eq, + #scrate::__private::codec::Encode, + #scrate::__private::codec::Decode, + #scrate::__private::codec::MaxEncodedLen, + #scrate::__private::RuntimeDebug, + #scrate::__private::scale_info::TypeInfo + )] + #vis enum Parameters { + #( + #(#attrs)* + #key_names(#key_names, Option<#value_types>), + )* + } + + #[doc(hidden)] + #[derive( + Clone, + PartialEq, + Eq, + #scrate::__private::codec::Encode, + #scrate::__private::codec::Decode, + #scrate::__private::codec::MaxEncodedLen, + #scrate::__private::RuntimeDebug, + #scrate::__private::scale_info::TypeInfo + )] + #vis enum #key_ident { + #( + #(#attrs)* + #key_names(#key_names), + )* + } + + #[doc(hidden)] + #[derive( + Clone, + PartialEq, + Eq, + #scrate::__private::codec::Encode, + #scrate::__private::codec::Decode, + #scrate::__private::codec::MaxEncodedLen, + #scrate::__private::RuntimeDebug, + #scrate::__private::scale_info::TypeInfo + )] + #vis enum #value_ident { + #( + #(#attrs)* + #key_names(#value_types), + )* + } + + impl #scrate::traits::dynamic_params::AggregatedKeyValue for Parameters { + type Key = #key_ident; + type Value = #value_ident; + + fn into_parts(self) -> (Self::Key, Option) { + match self { + #( + Parameters::#key_names(key, value) => { + (#key_ident::#key_names(key), value.map(#value_ident::#key_names)) + }, + )* + } + } + } + + #( + #[doc(hidden)] + #[derive( + Clone, + PartialEq, + Eq, + #scrate::__private::codec::Encode, + #scrate::__private::codec::Decode, + #scrate::__private::codec::MaxEncodedLen, + #scrate::__private::RuntimeDebug, + #scrate::__private::scale_info::TypeInfo + )] + #vis struct #key_names; + + impl #scrate::__private::Get<#value_types> for #key_names { + fn get() -> #value_types { + match + <#parameter_pallet as + #scrate::storage::StorageMap<#runtime_key_ident, #runtime_value_ident> + >::get(#runtime_key_ident::#aggregate_name(#key_ident::#key_names(#key_names))) + { + Some(#runtime_value_ident::#aggregate_name( + #value_ident::#key_names(inner))) => inner, + Some(_) => { + #scrate::defensive!("Unexpected value type at key - returning default"); + #defaults + }, + None => #defaults, + } + } + } + + impl #scrate::traits::dynamic_params::Key for #key_names { + type Value = #value_types; + type WrappedValue = #key_values; + } + + impl From<#key_names> for #key_ident { + fn from(key: #key_names) -> Self { + #key_ident::#key_names(key) + } + } + + impl TryFrom<#key_ident> for #key_names { + type Error = (); + + fn try_from(key: #key_ident) -> Result { + match key { + #key_ident::#key_names(key) => Ok(key), + _ => Err(()), + } + } + } + + #[doc(hidden)] + #[derive( + Clone, + PartialEq, + Eq, + #scrate::sp_runtime::RuntimeDebug, + )] + #vis struct #key_values(pub #value_types); + + impl From<#key_values> for #value_ident { + fn from(value: #key_values) -> Self { + #value_ident::#key_names(value.0) + } + } + + impl From<(#key_names, #value_types)> for Parameters { + fn from((key, value): (#key_names, #value_types)) -> Self { + Parameters::#key_names(key, Some(value)) + } + } + + impl From<#key_names> for Parameters { + fn from(key: #key_names) -> Self { + Parameters::#key_names(key, None) + } + } + + impl TryFrom<#value_ident> for #key_values { + type Error = (); + + fn try_from(value: #value_ident) -> Result { + match value { + #value_ident::#key_names(value) => Ok(#key_values(value)), + _ => Err(()), + } + } + } + + impl From<#key_values> for #value_types { + fn from(value: #key_values) -> Self { + value.0 + } + } + )* + } + }); + } +} + +#[derive(derive_syn_parse::Parse)] +pub struct DynamicParamAggregatedEnum { + aggregated_enum: syn::ItemEnum, +} + +impl ToTokens for DynamicParamAggregatedEnum { + fn to_tokens(&self, tokens: &mut TokenStream) { + let scrate = match crate_access() { + Ok(path) => path, + Err(err) => return tokens.extend(err), + }; + let params_enum = &self.aggregated_enum; + let (name, vis) = (¶ms_enum.ident, ¶ms_enum.vis); + + let (mut indices, mut param_names, mut param_types): (Vec<_>, Vec<_>, Vec<_>) = + Default::default(); + let mut attributes = Vec::new(); + for (i, variant) in params_enum.variants.iter().enumerate() { + indices.push(i); + param_names.push(&variant.ident); + attributes.push(&variant.attrs); + + param_types.push(match &variant.fields { + syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty, + _ => { + *tokens = quote! { compile_error!("Only unnamed enum variants with one inner item are supported") }; + return + }, + }); + } + + let params_key_ident = format_ident!("{}Key", params_enum.ident); + let params_value_ident = format_ident!("{}Value", params_enum.ident); + + tokens.extend(quote! { + #[doc(hidden)] + #[derive( + Clone, + PartialEq, + Eq, + #scrate::__private::codec::Encode, + #scrate::__private::codec::Decode, + #scrate::__private::codec::MaxEncodedLen, + #scrate::sp_runtime::RuntimeDebug, + #scrate::__private::scale_info::TypeInfo + )] + #vis enum #name { + #( + //#[codec(index = #indices)] + #(#attributes)* + #param_names(#param_types), + )* + } + + #[doc(hidden)] + #[derive( + Clone, + PartialEq, + Eq, + #scrate::__private::codec::Encode, + #scrate::__private::codec::Decode, + #scrate::__private::codec::MaxEncodedLen, + #scrate::sp_runtime::RuntimeDebug, + #scrate::__private::scale_info::TypeInfo + )] + #vis enum #params_key_ident { + #( + #(#attributes)* + #param_names(<#param_types as #scrate::traits::dynamic_params::AggregatedKeyValue>::Key), + )* + } + + #[doc(hidden)] + #[derive( + Clone, + PartialEq, + Eq, + #scrate::__private::codec::Encode, + #scrate::__private::codec::Decode, + #scrate::__private::codec::MaxEncodedLen, + #scrate::sp_runtime::RuntimeDebug, + #scrate::__private::scale_info::TypeInfo + )] + #vis enum #params_value_ident { + #( + #(#attributes)* + #param_names(<#param_types as #scrate::traits::dynamic_params::AggregatedKeyValue>::Value), + )* + } + + impl #scrate::traits::dynamic_params::AggregatedKeyValue for #name { + type Key = #params_key_ident; + type Value = #params_value_ident; + + fn into_parts(self) -> (Self::Key, Option) { + match self { + #( + #name::#param_names(parameter) => { + let (key, value) = parameter.into_parts(); + (#params_key_ident::#param_names(key), value.map(#params_value_ident::#param_names)) + }, + )* + } + } + } + + #( + impl ::core::convert::From<<#param_types as #scrate::traits::dynamic_params::AggregatedKeyValue>::Key> for #params_key_ident { + fn from(key: <#param_types as #scrate::traits::dynamic_params::AggregatedKeyValue>::Key) -> Self { + #params_key_ident::#param_names(key) + } + } + + impl ::core::convert::TryFrom<#params_value_ident> for <#param_types as #scrate::traits::dynamic_params::AggregatedKeyValue>::Value { + type Error = (); + + fn try_from(value: #params_value_ident) -> Result { + match value { + #params_value_ident::#param_names(value) => Ok(value), + _ => Err(()), + } + } + } + )* + }); + } +} + +/// Get access to the current crate and convert the error to a compile error. +fn crate_access() -> core::result::Result { + generate_access_from_frame_or_crate("frame-support").map_err(|e| e.to_compile_error()) +} diff --git a/support/procedural-fork/src/key_prefix.rs b/support/procedural-fork/src/key_prefix.rs new file mode 100644 index 000000000..7f1ab6866 --- /dev/null +++ b/support/procedural-fork/src/key_prefix.rs @@ -0,0 +1,104 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use frame_support_procedural_tools::generate_access_from_frame_or_crate; +use proc_macro2::{Span, TokenStream}; +use quote::{format_ident, quote, ToTokens}; +use syn::{Ident, Result}; + +const MAX_IDENTS: usize = 18; + +pub fn impl_key_prefix_for_tuples(input: proc_macro::TokenStream) -> Result { + if !input.is_empty() { + return Err(syn::Error::new(Span::call_site(), "No arguments expected")) + } + + let mut all_trait_impls = TokenStream::new(); + let frame_support = generate_access_from_frame_or_crate("frame-support")?; + + for i in 2..=MAX_IDENTS { + let current_tuple = (0..i) + .map(|n| Ident::new(&format!("Tuple{}", n), Span::call_site())) + .collect::>(); + + for prefix_count in 1..i { + let (prefixes, suffixes) = current_tuple.split_at(prefix_count); + + let hashers = current_tuple + .iter() + .map(|ident| format_ident!("Hasher{}", ident)) + .collect::>(); + let kargs = + prefixes.iter().map(|ident| format_ident!("KArg{}", ident)).collect::>(); + let partial_keygen = generate_keygen(prefixes); + let suffix_keygen = generate_keygen(suffixes); + let suffix_tuple = generate_tuple(suffixes); + + let trait_impls = quote! { + impl< + #(#current_tuple: FullCodec + StaticTypeInfo,)* + #(#hashers: StorageHasher,)* + #(#kargs: EncodeLike<#prefixes>),* + > HasKeyPrefix<( #( #kargs, )* )> for ( #( Key<#hashers, #current_tuple>, )* ) { + type Suffix = #suffix_tuple; + + fn partial_key(prefix: ( #( #kargs, )* )) -> Vec { + <#partial_keygen>::final_key(prefix) + } + } + + impl< + #(#current_tuple: FullCodec + StaticTypeInfo,)* + #(#hashers: ReversibleStorageHasher,)* + #(#kargs: EncodeLike<#prefixes>),* + > HasReversibleKeyPrefix<( #( #kargs, )* )> for ( #( Key<#hashers, #current_tuple>, )* ) { + fn decode_partial_key(key_material: &[u8]) -> Result< + Self::Suffix, + #frame_support::__private::codec::Error, + > { + <#suffix_keygen>::decode_final_key(key_material).map(|k| k.0) + } + } + }; + + all_trait_impls.extend(trait_impls); + } + } + + Ok(all_trait_impls) +} + +fn generate_tuple(idents: &[Ident]) -> TokenStream { + if idents.len() == 1 { + idents[0].to_token_stream() + } else { + quote!((#(#idents),*)) + } +} + +fn generate_keygen(idents: &[Ident]) -> TokenStream { + if idents.len() == 1 { + let key = &idents[0]; + let hasher = format_ident!("Hasher{}", key); + + quote!(Key<#hasher, #key>) + } else { + let hashers = idents.iter().map(|ident| format_ident!("Hasher{}", ident)); + + quote!((#(Key<#hashers, #idents>),*)) + } +} diff --git a/support/procedural-fork/src/lib.rs b/support/procedural-fork/src/lib.rs index 8b1378917..08ce0a73c 100644 --- a/support/procedural-fork/src/lib.rs +++ b/support/procedural-fork/src/lib.rs @@ -1 +1,67 @@ +#![recursion_limit = "512"] +#![deny(rustdoc::broken_intra_doc_links)] +extern crate proc_macro; + +mod benchmark; +mod construct_runtime; +mod crate_version; +mod derive_impl; +mod dummy_part_checker; +mod dynamic_params; +mod key_prefix; +mod match_and_insert; +mod no_bound; +mod pallet; +mod pallet_error; +mod runtime; +mod storage_alias; +mod transactional; +mod tt_macro; + +use frame_support_procedural_tools::generate_access_from_frame_or_crate; +use macro_magic::{import_tokens_attr, import_tokens_attr_verbatim}; +use proc_macro::TokenStream; +use quote::{quote, ToTokens}; +use std::{cell::RefCell, str::FromStr}; +use syn::{parse_macro_input, Error, ItemImpl, ItemMod, TraitItemType}; + +pub(crate) const INHERENT_INSTANCE_NAME: &str = "__InherentHiddenInstance"; + +/// The number of module instances supported by the runtime, starting at index 1, +/// and up to `NUMBER_OF_INSTANCE`. +pub(crate) const NUMBER_OF_INSTANCE: u8 = 16; + +thread_local! { + /// A global counter, can be used to generate a relatively unique identifier. + static COUNTER: RefCell = RefCell::new(Counter(0)); +} + +/// Counter to generate a relatively unique identifier for macros. This is necessary because +/// declarative macros gets hoisted to the crate root, which shares the namespace with other pallets +/// containing the very same macros. +struct Counter(u64); + +impl Counter { + fn inc(&mut self) -> u64 { + let ret = self.0; + self.0 += 1; + ret + } +} + +/// Get the value from the given environment variable set by cargo. +/// +/// The value is parsed into the requested destination type. +fn get_cargo_env_var(version_env: &str) -> std::result::Result { + let version = std::env::var(version_env) + .unwrap_or_else(|_| panic!("`{}` is always set by cargo; qed", version_env)); + + T::from_str(&version).map_err(drop) +} + +/// Generate the counter_prefix related to the storage. +/// counter_prefix is used by counted storage map. +fn counter_prefix(prefix: &str) -> String { + format!("CounterFor{}", prefix) +} diff --git a/support/procedural-fork/src/match_and_insert.rs b/support/procedural-fork/src/match_and_insert.rs new file mode 100644 index 000000000..aa9cc56d7 --- /dev/null +++ b/support/procedural-fork/src/match_and_insert.rs @@ -0,0 +1,159 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Implementation of the `match_and_insert` macro. + +use proc_macro2::{Group, Span, TokenStream, TokenTree}; +use std::iter::once; +use syn::spanned::Spanned; + +mod keyword { + syn::custom_keyword!(target); + syn::custom_keyword!(pattern); + syn::custom_keyword!(tokens); +} + +pub fn match_and_insert(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let MatchAndInsertDef { pattern, tokens, target } = + syn::parse_macro_input!(input as MatchAndInsertDef); + + match expand_in_stream(&pattern, &mut Some(tokens), target) { + Ok(stream) => stream.into(), + Err(err) => err.to_compile_error().into(), + } +} + +struct MatchAndInsertDef { + // Token stream to search and insert tokens into. + target: TokenStream, + // Pattern to match against, this is ensured to have no TokenTree::Group nor TokenTree::Literal + // (i.e. contains only Punct or Ident), and not being empty. + pattern: Vec, + // Token stream to insert after the match pattern. + tokens: TokenStream, +} + +impl syn::parse::Parse for MatchAndInsertDef { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut target; + let _ = input.parse::()?; + let _ = input.parse::()?; + let _replace_with_bracket: syn::token::Bracket = syn::bracketed!(target in input); + let _replace_with_brace: syn::token::Brace = syn::braced!(target in target); + let target = target.parse()?; + + let mut pattern; + let _ = input.parse::()?; + let _ = input.parse::()?; + let _replace_with_bracket: syn::token::Bracket = syn::bracketed!(pattern in input); + let _replace_with_brace: syn::token::Brace = syn::braced!(pattern in pattern); + let pattern = pattern.parse::()?.into_iter().collect::>(); + + if let Some(t) = pattern.iter().find(|t| matches!(t, TokenTree::Group(_))) { + return Err(syn::Error::new(t.span(), "Unexpected group token tree")) + } + if let Some(t) = pattern.iter().find(|t| matches!(t, TokenTree::Literal(_))) { + return Err(syn::Error::new(t.span(), "Unexpected literal token tree")) + } + + if pattern.is_empty() { + return Err(syn::Error::new(Span::call_site(), "empty match pattern is invalid")) + } + + let mut tokens; + let _ = input.parse::()?; + let _ = input.parse::()?; + let _replace_with_bracket: syn::token::Bracket = syn::bracketed!(tokens in input); + let _replace_with_brace: syn::token::Brace = syn::braced!(tokens in tokens); + let tokens = tokens.parse()?; + + Ok(Self { tokens, pattern, target }) + } +} + +// Insert `tokens` after the first matching `pattern`. +// `tokens` must be some (Option is used for internal simplification). +// `pattern` must not be empty and should only contain Ident or Punct. +fn expand_in_stream( + pattern: &[TokenTree], + tokens: &mut Option, + stream: TokenStream, +) -> syn::Result { + assert!( + tokens.is_some(), + "`tokens` must be some, Option is used because `tokens` is used only once" + ); + assert!( + !pattern.is_empty(), + "`pattern` must not be empty, otherwise there is nothing to match against" + ); + + let stream_span = stream.span(); + let mut stream = stream.into_iter(); + let mut extended = TokenStream::new(); + let mut match_cursor = 0; + + while let Some(token) = stream.next() { + match token { + TokenTree::Group(group) => { + match_cursor = 0; + let group_stream = group.stream(); + match expand_in_stream(pattern, tokens, group_stream) { + Ok(s) => { + extended.extend(once(TokenTree::Group(Group::new(group.delimiter(), s)))); + extended.extend(stream); + return Ok(extended) + }, + Err(_) => { + extended.extend(once(TokenTree::Group(group))); + }, + } + }, + other => { + advance_match_cursor(&other, pattern, &mut match_cursor); + + extended.extend(once(other)); + + if match_cursor == pattern.len() { + extended + .extend(once(tokens.take().expect("tokens is used to replace only once"))); + extended.extend(stream); + return Ok(extended) + } + }, + } + } + // if we reach this point, it means the stream is empty and we haven't found a matching pattern + let msg = format!("Cannot find pattern `{:?}` in given token stream", pattern); + Err(syn::Error::new(stream_span, msg)) +} + +fn advance_match_cursor(other: &TokenTree, pattern: &[TokenTree], match_cursor: &mut usize) { + use TokenTree::{Ident, Punct}; + + let does_match_other_pattern = match (other, &pattern[*match_cursor]) { + (Ident(i1), Ident(i2)) => i1 == i2, + (Punct(p1), Punct(p2)) => p1.as_char() == p2.as_char(), + _ => false, + }; + + if does_match_other_pattern { + *match_cursor += 1; + } else { + *match_cursor = 0; + } +} diff --git a/support/procedural-fork/src/no_bound/clone.rs b/support/procedural-fork/src/no_bound/clone.rs new file mode 100644 index 000000000..346bf450f --- /dev/null +++ b/support/procedural-fork/src/no_bound/clone.rs @@ -0,0 +1,107 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use syn::spanned::Spanned; + +/// Derive Clone but do not bound any generic. +pub fn derive_clone_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = syn::parse_macro_input!(input as syn::DeriveInput); + + let name = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + + let impl_ = match input.data { + syn::Data::Struct(struct_) => match struct_.fields { + syn::Fields::Named(named) => { + let fields = named.named.iter().map(|i| &i.ident).map(|i| { + quote::quote_spanned!(i.span() => + #i: ::core::clone::Clone::clone(&self.#i) + ) + }); + + quote::quote!( Self { #( #fields, )* } ) + }, + syn::Fields::Unnamed(unnamed) => { + let fields = + unnamed.unnamed.iter().enumerate().map(|(i, _)| syn::Index::from(i)).map(|i| { + quote::quote_spanned!(i.span() => + ::core::clone::Clone::clone(&self.#i) + ) + }); + + quote::quote!( Self ( #( #fields, )* ) ) + }, + syn::Fields::Unit => { + quote::quote!(Self) + }, + }, + syn::Data::Enum(enum_) => { + let variants = enum_.variants.iter().map(|variant| { + let ident = &variant.ident; + match &variant.fields { + syn::Fields::Named(named) => { + let captured = named.named.iter().map(|i| &i.ident); + let cloned = captured.clone().map(|i| { + ::quote::quote_spanned!(i.span() => + #i: ::core::clone::Clone::clone(#i) + ) + }); + quote::quote!( + Self::#ident { #( ref #captured, )* } => Self::#ident { #( #cloned, )*} + ) + }, + syn::Fields::Unnamed(unnamed) => { + let captured = unnamed + .unnamed + .iter() + .enumerate() + .map(|(i, f)| syn::Ident::new(&format!("_{}", i), f.span())); + let cloned = captured.clone().map(|i| { + quote::quote_spanned!(i.span() => + ::core::clone::Clone::clone(#i) + ) + }); + quote::quote!( + Self::#ident ( #( ref #captured, )* ) => Self::#ident ( #( #cloned, )*) + ) + }, + syn::Fields::Unit => quote::quote!( Self::#ident => Self::#ident ), + } + }); + + quote::quote!(match self { + #( #variants, )* + }) + }, + syn::Data::Union(_) => { + let msg = "Union type not supported by `derive(CloneNoBound)`"; + return syn::Error::new(input.span(), msg).to_compile_error().into() + }, + }; + + quote::quote!( + const _: () = { + #[automatically_derived] + impl #impl_generics ::core::clone::Clone for #name #ty_generics #where_clause { + fn clone(&self) -> Self { + #impl_ + } + } + }; + ) + .into() +} diff --git a/support/procedural-fork/src/no_bound/debug.rs b/support/procedural-fork/src/no_bound/debug.rs new file mode 100644 index 000000000..a1b3f4f0d --- /dev/null +++ b/support/procedural-fork/src/no_bound/debug.rs @@ -0,0 +1,121 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use syn::spanned::Spanned; + +/// Derive Debug but do not bound any generics. +pub fn derive_debug_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = syn::parse_macro_input!(input as syn::DeriveInput); + + let input_ident = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + + let impl_ = match input.data { + syn::Data::Struct(struct_) => match struct_.fields { + syn::Fields::Named(named) => { + let fields = + named.named.iter().map(|i| &i.ident).map( + |i| quote::quote_spanned!(i.span() => .field(stringify!(#i), &self.#i) ), + ); + + quote::quote!( + fmt.debug_struct(stringify!(#input_ident)) + #( #fields )* + .finish() + ) + }, + syn::Fields::Unnamed(unnamed) => { + let fields = unnamed + .unnamed + .iter() + .enumerate() + .map(|(i, _)| syn::Index::from(i)) + .map(|i| quote::quote_spanned!(i.span() => .field(&self.#i) )); + + quote::quote!( + fmt.debug_tuple(stringify!(#input_ident)) + #( #fields )* + .finish() + ) + }, + syn::Fields::Unit => quote::quote!(fmt.write_str(stringify!(#input_ident))), + }, + syn::Data::Enum(enum_) => { + let variants = enum_.variants.iter().map(|variant| { + let ident = &variant.ident; + let full_variant_str = format!("{}::{}", input_ident, ident); + match &variant.fields { + syn::Fields::Named(named) => { + let captured = named.named.iter().map(|i| &i.ident); + let debugged = captured.clone().map(|i| { + quote::quote_spanned!(i.span() => + .field(stringify!(#i), &#i) + ) + }); + quote::quote!( + Self::#ident { #( ref #captured, )* } => { + fmt.debug_struct(#full_variant_str) + #( #debugged )* + .finish() + } + ) + }, + syn::Fields::Unnamed(unnamed) => { + let captured = unnamed + .unnamed + .iter() + .enumerate() + .map(|(i, f)| syn::Ident::new(&format!("_{}", i), f.span())); + let debugged = captured + .clone() + .map(|i| quote::quote_spanned!(i.span() => .field(&#i))); + quote::quote!( + Self::#ident ( #( ref #captured, )* ) => { + fmt.debug_tuple(#full_variant_str) + #( #debugged )* + .finish() + } + ) + }, + syn::Fields::Unit => quote::quote!( + Self::#ident => fmt.write_str(#full_variant_str) + ), + } + }); + + quote::quote!(match *self { + #( #variants, )* + }) + }, + syn::Data::Union(_) => { + let msg = "Union type not supported by `derive(DebugNoBound)`"; + return syn::Error::new(input.span(), msg).to_compile_error().into() + }, + }; + + quote::quote!( + const _: () = { + #[automatically_derived] + impl #impl_generics ::core::fmt::Debug for #input_ident #ty_generics #where_clause { + fn fmt(&self, fmt: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + #impl_ + } + } + }; + ) + .into() +} diff --git a/support/procedural-fork/src/no_bound/default.rs b/support/procedural-fork/src/no_bound/default.rs new file mode 100644 index 000000000..0524247d2 --- /dev/null +++ b/support/procedural-fork/src/no_bound/default.rs @@ -0,0 +1,161 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use proc_macro2::Span; +use quote::{quote, quote_spanned}; +use syn::{spanned::Spanned, Data, DeriveInput, Fields}; + +/// Derive Default but do not bound any generic. +pub fn derive_default_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = syn::parse_macro_input!(input as DeriveInput); + + let name = &input.ident; + + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + + let impl_ = match input.data { + Data::Struct(struct_) => match struct_.fields { + Fields::Named(named) => { + let fields = named.named.iter().map(|field| &field.ident).map(|ident| { + quote_spanned! {ident.span() => + #ident: ::core::default::Default::default() + } + }); + + quote!(Self { #( #fields, )* }) + }, + Fields::Unnamed(unnamed) => { + let fields = unnamed.unnamed.iter().map(|field| { + quote_spanned! {field.span()=> + ::core::default::Default::default() + } + }); + + quote!(Self( #( #fields, )* )) + }, + Fields::Unit => { + quote!(Self) + }, + }, + Data::Enum(enum_) => { + if enum_.variants.is_empty() { + return syn::Error::new_spanned(name, "cannot derive Default for an empty enum") + .to_compile_error() + .into() + } + + // all #[default] attrs with the variant they're on; i.e. a var + let default_variants = enum_ + .variants + .into_iter() + .filter(|variant| variant.attrs.iter().any(|attr| attr.path().is_ident("default"))) + .collect::>(); + + match &*default_variants { + [] => return syn::Error::new( + name.clone().span(), + "no default declared, make a variant default by placing `#[default]` above it", + ) + .into_compile_error() + .into(), + // only one variant with the #[default] attribute set + [default_variant] => { + let variant_attrs = default_variant + .attrs + .iter() + .filter(|a| a.path().is_ident("default")) + .collect::>(); + + // check that there is only one #[default] attribute on the variant + if let [first_attr, second_attr, additional_attrs @ ..] = &*variant_attrs { + let mut err = + syn::Error::new(Span::call_site(), "multiple `#[default]` attributes"); + + err.combine(syn::Error::new_spanned(first_attr, "`#[default]` used here")); + + err.extend([second_attr].into_iter().chain(additional_attrs).map( + |variant| { + syn::Error::new_spanned(variant, "`#[default]` used again here") + }, + )); + + return err.into_compile_error().into() + } + + let variant_ident = &default_variant.ident; + + let fully_qualified_variant_path = quote!(Self::#variant_ident); + + match &default_variant.fields { + Fields::Named(named) => { + let fields = + named.named.iter().map(|field| &field.ident).map(|ident| { + quote_spanned! {ident.span()=> + #ident: ::core::default::Default::default() + } + }); + + quote!(#fully_qualified_variant_path { #( #fields, )* }) + }, + Fields::Unnamed(unnamed) => { + let fields = unnamed.unnamed.iter().map(|field| { + quote_spanned! {field.span()=> + ::core::default::Default::default() + } + }); + + quote!(#fully_qualified_variant_path( #( #fields, )* )) + }, + Fields::Unit => fully_qualified_variant_path, + } + }, + [first, additional @ ..] => { + let mut err = syn::Error::new(Span::call_site(), "multiple declared defaults"); + + err.combine(syn::Error::new_spanned(first, "first default")); + + err.extend( + additional + .into_iter() + .map(|variant| syn::Error::new_spanned(variant, "additional default")), + ); + + return err.into_compile_error().into() + }, + } + }, + Data::Union(union_) => + return syn::Error::new_spanned( + union_.union_token, + "Union type not supported by `derive(DefaultNoBound)`", + ) + .to_compile_error() + .into(), + }; + + quote!( + const _: () = { + #[automatically_derived] + impl #impl_generics ::core::default::Default for #name #ty_generics #where_clause { + fn default() -> Self { + #impl_ + } + } + }; + ) + .into() +} diff --git a/support/procedural-fork/src/no_bound/mod.rs b/support/procedural-fork/src/no_bound/mod.rs new file mode 100644 index 000000000..9e0377dda --- /dev/null +++ b/support/procedural-fork/src/no_bound/mod.rs @@ -0,0 +1,25 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Derive macros to derive traits without bounding generic parameters. + +pub mod clone; +pub mod debug; +pub mod default; +pub mod ord; +pub mod partial_eq; +pub mod partial_ord; diff --git a/support/procedural-fork/src/no_bound/ord.rs b/support/procedural-fork/src/no_bound/ord.rs new file mode 100644 index 000000000..b24d27c04 --- /dev/null +++ b/support/procedural-fork/src/no_bound/ord.rs @@ -0,0 +1,75 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use syn::spanned::Spanned; + +/// Derive Ord but do not bound any generic. +pub fn derive_ord_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input: syn::DeriveInput = match syn::parse(input) { + Ok(input) => input, + Err(e) => return e.to_compile_error().into(), + }; + + let name = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + + let impl_ = match input.data { + syn::Data::Struct(struct_) => match struct_.fields { + syn::Fields::Named(named) => { + let fields = named + .named + .iter() + .map(|i| &i.ident) + .map(|i| quote::quote_spanned!(i.span() => self.#i.cmp(&other.#i) )); + + quote::quote!( core::cmp::Ordering::Equal #( .then_with(|| #fields) )* ) + }, + syn::Fields::Unnamed(unnamed) => { + let fields = unnamed + .unnamed + .iter() + .enumerate() + .map(|(i, _)| syn::Index::from(i)) + .map(|i| quote::quote_spanned!(i.span() => self.#i.cmp(&other.#i) )); + + quote::quote!( core::cmp::Ordering::Equal #( .then_with(|| #fields) )* ) + }, + syn::Fields::Unit => { + quote::quote!(core::cmp::Ordering::Equal) + }, + }, + syn::Data::Enum(_) => { + let msg = "Enum type not supported by `derive(OrdNoBound)`"; + return syn::Error::new(input.span(), msg).to_compile_error().into() + }, + syn::Data::Union(_) => { + let msg = "Union type not supported by `derive(OrdNoBound)`"; + return syn::Error::new(input.span(), msg).to_compile_error().into() + }, + }; + + quote::quote!( + const _: () = { + impl #impl_generics core::cmp::Ord for #name #ty_generics #where_clause { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + #impl_ + } + } + }; + ) + .into() +} diff --git a/support/procedural-fork/src/no_bound/partial_eq.rs b/support/procedural-fork/src/no_bound/partial_eq.rs new file mode 100644 index 000000000..a1be71a96 --- /dev/null +++ b/support/procedural-fork/src/no_bound/partial_eq.rs @@ -0,0 +1,137 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use syn::spanned::Spanned; + +/// Derive PartialEq but do not bound any generic. +pub fn derive_partial_eq_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = syn::parse_macro_input!(input as syn::DeriveInput); + + let name = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + + let impl_ = match input.data { + syn::Data::Struct(struct_) => match struct_.fields { + syn::Fields::Named(named) => { + let fields = named + .named + .iter() + .map(|i| &i.ident) + .map(|i| quote::quote_spanned!(i.span() => self.#i == other.#i )); + + quote::quote!( true #( && #fields )* ) + }, + syn::Fields::Unnamed(unnamed) => { + let fields = unnamed + .unnamed + .iter() + .enumerate() + .map(|(i, _)| syn::Index::from(i)) + .map(|i| quote::quote_spanned!(i.span() => self.#i == other.#i )); + + quote::quote!( true #( && #fields )* ) + }, + syn::Fields::Unit => { + quote::quote!(true) + }, + }, + syn::Data::Enum(enum_) => { + let variants = + enum_.variants.iter().map(|variant| { + let ident = &variant.ident; + match &variant.fields { + syn::Fields::Named(named) => { + let names = named.named.iter().map(|i| &i.ident); + let other_names = names.clone().enumerate().map(|(n, ident)| { + syn::Ident::new(&format!("_{}", n), ident.span()) + }); + + let capture = names.clone(); + let other_capture = names + .clone() + .zip(other_names.clone()) + .map(|(i, other_i)| quote::quote!(#i: #other_i)); + let eq = names.zip(other_names).map( + |(i, other_i)| quote::quote_spanned!(i.span() => #i == #other_i), + ); + quote::quote!( + ( + Self::#ident { #( #capture, )* }, + Self::#ident { #( #other_capture, )* }, + ) => true #( && #eq )* + ) + }, + syn::Fields::Unnamed(unnamed) => { + let names = unnamed + .unnamed + .iter() + .enumerate() + .map(|(i, f)| syn::Ident::new(&format!("_{}", i), f.span())); + let other_names = + unnamed.unnamed.iter().enumerate().map(|(i, f)| { + syn::Ident::new(&format!("_{}_other", i), f.span()) + }); + let eq = names.clone().zip(other_names.clone()).map( + |(i, other_i)| quote::quote_spanned!(i.span() => #i == #other_i), + ); + quote::quote!( + ( + Self::#ident ( #( #names, )* ), + Self::#ident ( #( #other_names, )* ), + ) => true #( && #eq )* + ) + }, + syn::Fields::Unit => quote::quote!( (Self::#ident, Self::#ident) => true ), + } + }); + + let mut different_variants = vec![]; + for (i, i_variant) in enum_.variants.iter().enumerate() { + for (j, j_variant) in enum_.variants.iter().enumerate() { + if i != j { + let i_ident = &i_variant.ident; + let j_ident = &j_variant.ident; + different_variants.push(quote::quote!( + (Self::#i_ident { .. }, Self::#j_ident { .. }) => false + )) + } + } + } + + quote::quote!( match (self, other) { + #( #variants, )* + #( #different_variants, )* + }) + }, + syn::Data::Union(_) => { + let msg = "Union type not supported by `derive(PartialEqNoBound)`"; + return syn::Error::new(input.span(), msg).to_compile_error().into() + }, + }; + + quote::quote!( + const _: () = { + #[automatically_derived] + impl #impl_generics ::core::cmp::PartialEq for #name #ty_generics #where_clause { + fn eq(&self, other: &Self) -> bool { + #impl_ + } + } + }; + ) + .into() +} diff --git a/support/procedural-fork/src/no_bound/partial_ord.rs b/support/procedural-fork/src/no_bound/partial_ord.rs new file mode 100644 index 000000000..86aa42be9 --- /dev/null +++ b/support/procedural-fork/src/no_bound/partial_ord.rs @@ -0,0 +1,89 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use syn::spanned::Spanned; + +/// Derive PartialOrd but do not bound any generic. +pub fn derive_partial_ord_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input: syn::DeriveInput = match syn::parse(input) { + Ok(input) => input, + Err(e) => return e.to_compile_error().into(), + }; + + let name = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + + let impl_ = match input.data { + syn::Data::Struct(struct_) => + match struct_.fields { + syn::Fields::Named(named) => { + let fields = + named.named.iter().map(|i| &i.ident).map( + |i| quote::quote_spanned!(i.span() => self.#i.partial_cmp(&other.#i)), + ); + + quote::quote!( + Some(core::cmp::Ordering::Equal) + #( + .and_then(|order| { + let next_order = #fields?; + Some(order.then(next_order)) + }) + )* + ) + }, + syn::Fields::Unnamed(unnamed) => { + let fields = + unnamed.unnamed.iter().enumerate().map(|(i, _)| syn::Index::from(i)).map( + |i| quote::quote_spanned!(i.span() => self.#i.partial_cmp(&other.#i)), + ); + + quote::quote!( + Some(core::cmp::Ordering::Equal) + #( + .and_then(|order| { + let next_order = #fields?; + Some(order.then(next_order)) + }) + )* + ) + }, + syn::Fields::Unit => { + quote::quote!(Some(core::cmp::Ordering::Equal)) + }, + }, + syn::Data::Enum(_) => { + let msg = "Enum type not supported by `derive(PartialOrdNoBound)`"; + return syn::Error::new(input.span(), msg).to_compile_error().into() + }, + syn::Data::Union(_) => { + let msg = "Union type not supported by `derive(PartialOrdNoBound)`"; + return syn::Error::new(input.span(), msg).to_compile_error().into() + }, + }; + + quote::quote!( + const _: () = { + impl #impl_generics core::cmp::PartialOrd for #name #ty_generics #where_clause { + fn partial_cmp(&self, other: &Self) -> Option { + #impl_ + } + } + }; + ) + .into() +} diff --git a/support/procedural-fork/src/pallet/expand/call.rs b/support/procedural-fork/src/pallet/expand/call.rs new file mode 100644 index 000000000..f395872c8 --- /dev/null +++ b/support/procedural-fork/src/pallet/expand/call.rs @@ -0,0 +1,452 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{ + pallet::{ + expand::warnings::{weight_constant_warning, weight_witness_warning}, + parse::call::CallWeightDef, + Def, + }, + COUNTER, +}; +use proc_macro2::TokenStream as TokenStream2; +use proc_macro_warning::Warning; +use quote::{quote, ToTokens}; +use syn::spanned::Spanned; + +/// +/// * Generate enum call and implement various trait on it. +/// * Implement Callable and call_function on `Pallet` +pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream { + let (span, where_clause, methods, docs) = match def.call.as_ref() { + Some(call) => { + let span = call.attr_span; + let where_clause = call.where_clause.clone(); + let methods = call.methods.clone(); + let docs = call.docs.clone(); + + (span, where_clause, methods, docs) + }, + None => (def.item.span(), def.config.where_clause.clone(), Vec::new(), Vec::new()), + }; + let frame_support = &def.frame_support; + let frame_system = &def.frame_system; + let type_impl_gen = &def.type_impl_generics(span); + let type_decl_bounded_gen = &def.type_decl_bounded_generics(span); + let type_use_gen = &def.type_use_generics(span); + let call_ident = syn::Ident::new("Call", span); + let pallet_ident = &def.pallet_struct.pallet; + + let fn_name = methods.iter().map(|method| &method.name).collect::>(); + let call_index = methods.iter().map(|method| method.call_index).collect::>(); + let new_call_variant_fn_name = fn_name + .iter() + .map(|fn_name| quote::format_ident!("new_call_variant_{}", fn_name)) + .collect::>(); + + let new_call_variant_doc = fn_name + .iter() + .map(|fn_name| format!("Create a call with the variant `{}`.", fn_name)) + .collect::>(); + + let mut call_index_warnings = Vec::new(); + // Emit a warning for each call that is missing `call_index` when not in dev-mode. + for method in &methods { + if method.explicit_call_index || def.dev_mode { + continue + } + + let warning = Warning::new_deprecated("ImplicitCallIndex") + .index(call_index_warnings.len()) + .old("use implicit call indices") + .new("ensure that all calls have a `pallet::call_index` attribute or put the pallet into `dev` mode") + .help_links(&[ + "https://github.com/paritytech/substrate/pull/12891", + "https://github.com/paritytech/substrate/pull/11381" + ]) + .span(method.name.span()) + .build_or_panic(); + call_index_warnings.push(warning); + } + + let mut fn_weight = Vec::::new(); + let mut weight_warnings = Vec::new(); + for method in &methods { + match &method.weight { + CallWeightDef::DevModeDefault => fn_weight.push(syn::parse_quote!(0)), + CallWeightDef::Immediate(e) => { + weight_constant_warning(e, def.dev_mode, &mut weight_warnings); + weight_witness_warning(method, def.dev_mode, &mut weight_warnings); + + fn_weight.push(e.into_token_stream()); + }, + CallWeightDef::Inherited => { + let pallet_weight = def + .call + .as_ref() + .expect("we have methods; we have calls; qed") + .inherited_call_weight + .as_ref() + .expect("the parser prevents this"); + + // Expand `<::WeightInfo>::call_name()`. + let t = &pallet_weight.typename; + let n = &method.name; + fn_weight.push(quote!({ < #t > :: #n () })); + }, + } + } + debug_assert_eq!(fn_weight.len(), methods.len()); + + let fn_doc = methods.iter().map(|method| &method.docs).collect::>(); + + let args_name = methods + .iter() + .map(|method| method.args.iter().map(|(_, name, _)| name.clone()).collect::>()) + .collect::>(); + + let args_name_stripped = methods + .iter() + .map(|method| { + method + .args + .iter() + .map(|(_, name, _)| { + syn::Ident::new(name.to_string().trim_start_matches('_'), name.span()) + }) + .collect::>() + }) + .collect::>(); + + let make_args_name_pattern = |ref_tok| { + args_name + .iter() + .zip(args_name_stripped.iter()) + .map(|(args_name, args_name_stripped)| { + args_name + .iter() + .zip(args_name_stripped) + .map(|(args_name, args_name_stripped)| { + if args_name == args_name_stripped { + quote::quote!( #ref_tok #args_name ) + } else { + quote::quote!( #args_name_stripped: #ref_tok #args_name ) + } + }) + .collect::>() + }) + .collect::>() + }; + + let args_name_pattern = make_args_name_pattern(None); + let args_name_pattern_ref = make_args_name_pattern(Some(quote::quote!(ref))); + + let args_type = methods + .iter() + .map(|method| method.args.iter().map(|(_, _, type_)| type_.clone()).collect::>()) + .collect::>(); + + let args_compact_attr = methods.iter().map(|method| { + method + .args + .iter() + .map(|(is_compact, _, type_)| { + if *is_compact { + quote::quote_spanned!(type_.span() => #[codec(compact)] ) + } else { + quote::quote!() + } + }) + .collect::>() + }); + + let default_docs = + [syn::parse_quote!(r"Contains a variant per dispatchable extrinsic that this pallet has.")]; + let docs = if docs.is_empty() { &default_docs[..] } else { &docs[..] }; + + let maybe_compile_error = if def.call.is_none() { + quote::quote! { + compile_error!(concat!( + "`", + stringify!($pallet_name), + "` does not have #[pallet::call] defined, perhaps you should remove `Call` from \ + construct_runtime?", + )); + } + } else { + proc_macro2::TokenStream::new() + }; + + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + let macro_ident = syn::Ident::new(&format!("__is_call_part_defined_{}", count), span); + + let capture_docs = if cfg!(feature = "no-metadata-docs") { "never" } else { "always" }; + + // Wrap all calls inside of storage layers + if let Some(syn::Item::Impl(item_impl)) = def + .call + .as_ref() + .map(|c| &mut def.item.content.as_mut().expect("Checked by def parser").1[c.index]) + { + item_impl.items.iter_mut().for_each(|i| { + if let syn::ImplItem::Fn(method) = i { + let block = &method.block; + method.block = syn::parse_quote! {{ + // We execute all dispatchable in a new storage layer, allowing them + // to return an error at any point, and undoing any storage changes. + #frame_support::storage::with_storage_layer(|| #block) + }}; + } + }); + } + + // Extracts #[allow] attributes, necessary so that we don't run into compiler warnings + let maybe_allow_attrs = methods + .iter() + .map(|method| { + method + .attrs + .iter() + .find(|attr| attr.path().is_ident("allow")) + .map_or(proc_macro2::TokenStream::new(), |attr| attr.to_token_stream()) + }) + .collect::>(); + + let cfg_attrs = methods + .iter() + .map(|method| { + let attrs = + method.cfg_attrs.iter().map(|attr| attr.to_token_stream()).collect::>(); + quote::quote!( #( #attrs )* ) + }) + .collect::>(); + + let feeless_check = methods.iter().map(|method| &method.feeless_check).collect::>(); + let feeless_check_result = + feeless_check.iter().zip(args_name.iter()).map(|(feeless_check, arg_name)| { + if let Some(feeless_check) = feeless_check { + quote::quote!(#feeless_check(origin, #( #arg_name, )*)) + } else { + quote::quote!(false) + } + }); + + quote::quote_spanned!(span => + #[doc(hidden)] + mod warnings { + #( + #call_index_warnings + )* + #( + #weight_warnings + )* + } + + #[allow(unused_imports)] + #[doc(hidden)] + pub mod __substrate_call_check { + #[macro_export] + #[doc(hidden)] + macro_rules! #macro_ident { + ($pallet_name:ident) => { + #maybe_compile_error + }; + } + + #[doc(hidden)] + pub use #macro_ident as is_call_part_defined; + } + + #( #[doc = #docs] )* + #[derive( + #frame_support::RuntimeDebugNoBound, + #frame_support::CloneNoBound, + #frame_support::EqNoBound, + #frame_support::PartialEqNoBound, + #frame_support::__private::codec::Encode, + #frame_support::__private::codec::Decode, + #frame_support::__private::scale_info::TypeInfo, + )] + #[codec(encode_bound())] + #[codec(decode_bound())] + #[scale_info(skip_type_params(#type_use_gen), capture_docs = #capture_docs)] + #[allow(non_camel_case_types)] + pub enum #call_ident<#type_decl_bounded_gen> #where_clause { + #[doc(hidden)] + #[codec(skip)] + __Ignore( + ::core::marker::PhantomData<(#type_use_gen,)>, + #frame_support::Never, + ), + #( + #cfg_attrs + #( #[doc = #fn_doc] )* + #[codec(index = #call_index)] + #fn_name { + #( + #[allow(missing_docs)] + #args_compact_attr #args_name_stripped: #args_type + ),* + }, + )* + } + + impl<#type_impl_gen> #call_ident<#type_use_gen> #where_clause { + #( + #cfg_attrs + #[doc = #new_call_variant_doc] + pub fn #new_call_variant_fn_name( + #( #args_name_stripped: #args_type ),* + ) -> Self { + Self::#fn_name { + #( #args_name_stripped ),* + } + } + )* + } + + impl<#type_impl_gen> #frame_support::dispatch::GetDispatchInfo + for #call_ident<#type_use_gen> + #where_clause + { + fn get_dispatch_info(&self) -> #frame_support::dispatch::DispatchInfo { + match *self { + #( + #cfg_attrs + Self::#fn_name { #( #args_name_pattern_ref, )* } => { + let __pallet_base_weight = #fn_weight; + + let __pallet_weight = < + dyn #frame_support::dispatch::WeighData<( #( & #args_type, )* )> + >::weigh_data(&__pallet_base_weight, ( #( #args_name, )* )); + + let __pallet_class = < + dyn #frame_support::dispatch::ClassifyDispatch< + ( #( & #args_type, )* ) + > + >::classify_dispatch(&__pallet_base_weight, ( #( #args_name, )* )); + + let __pallet_pays_fee = < + dyn #frame_support::dispatch::PaysFee<( #( & #args_type, )* )> + >::pays_fee(&__pallet_base_weight, ( #( #args_name, )* )); + + #frame_support::dispatch::DispatchInfo { + weight: __pallet_weight, + class: __pallet_class, + pays_fee: __pallet_pays_fee, + } + }, + )* + Self::__Ignore(_, _) => unreachable!("__Ignore cannot be used"), + } + } + } + + impl<#type_impl_gen> #frame_support::dispatch::CheckIfFeeless for #call_ident<#type_use_gen> + #where_clause + { + type Origin = #frame_system::pallet_prelude::OriginFor; + #[allow(unused_variables)] + fn is_feeless(&self, origin: &Self::Origin) -> bool { + match *self { + #( + #cfg_attrs + Self::#fn_name { #( #args_name_pattern_ref, )* } => { + #feeless_check_result + }, + )* + Self::__Ignore(_, _) => unreachable!("__Ignore cannot be used"), + } + } + } + + impl<#type_impl_gen> #frame_support::traits::GetCallName for #call_ident<#type_use_gen> + #where_clause + { + fn get_call_name(&self) -> &'static str { + match *self { + #( #cfg_attrs Self::#fn_name { .. } => stringify!(#fn_name), )* + Self::__Ignore(_, _) => unreachable!("__PhantomItem cannot be used."), + } + } + + fn get_call_names() -> &'static [&'static str] { + &[ #( #cfg_attrs stringify!(#fn_name), )* ] + } + } + + impl<#type_impl_gen> #frame_support::traits::GetCallIndex for #call_ident<#type_use_gen> + #where_clause + { + fn get_call_index(&self) -> u8 { + match *self { + #( #cfg_attrs Self::#fn_name { .. } => #call_index, )* + Self::__Ignore(_, _) => unreachable!("__PhantomItem cannot be used."), + } + } + + fn get_call_indices() -> &'static [u8] { + &[ #( #cfg_attrs #call_index, )* ] + } + } + + impl<#type_impl_gen> #frame_support::traits::UnfilteredDispatchable + for #call_ident<#type_use_gen> + #where_clause + { + type RuntimeOrigin = #frame_system::pallet_prelude::OriginFor; + fn dispatch_bypass_filter( + self, + origin: Self::RuntimeOrigin + ) -> #frame_support::dispatch::DispatchResultWithPostInfo { + #frame_support::dispatch_context::run_in_context(|| { + match self { + #( + #cfg_attrs + Self::#fn_name { #( #args_name_pattern, )* } => { + #frame_support::__private::sp_tracing::enter_span!( + #frame_support::__private::sp_tracing::trace_span!(stringify!(#fn_name)) + ); + #maybe_allow_attrs + <#pallet_ident<#type_use_gen>>::#fn_name(origin, #( #args_name, )* ) + .map(Into::into).map_err(Into::into) + }, + )* + Self::__Ignore(_, _) => { + let _ = origin; // Use origin for empty Call enum + unreachable!("__PhantomItem cannot be used."); + }, + } + }) + } + } + + impl<#type_impl_gen> #frame_support::dispatch::Callable for #pallet_ident<#type_use_gen> + #where_clause + { + type RuntimeCall = #call_ident<#type_use_gen>; + } + + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #where_clause { + #[allow(dead_code)] + #[doc(hidden)] + pub fn call_functions() -> #frame_support::__private::metadata_ir::PalletCallMetadataIR { + #frame_support::__private::scale_info::meta_type::<#call_ident<#type_use_gen>>().into() + } + } + ) +} diff --git a/support/procedural-fork/src/pallet/expand/composite.rs b/support/procedural-fork/src/pallet/expand/composite.rs new file mode 100644 index 000000000..d449afe8f --- /dev/null +++ b/support/procedural-fork/src/pallet/expand/composite.rs @@ -0,0 +1,40 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::pallet::Def; +use proc_macro2::TokenStream; + +/// Expands `composite_enum` and adds the `VariantCount` implementation for it. +pub fn expand_composites(def: &mut Def) -> TokenStream { + let mut expand = quote::quote!(); + let frame_support = &def.frame_support; + + for composite in &def.composites { + let name = &composite.ident; + let (impl_generics, ty_generics, where_clause) = composite.generics.split_for_impl(); + let variants_count = composite.variant_count; + + // add `VariantCount` implementation for `composite_enum` + expand.extend(quote::quote_spanned!(composite.attr_span => + impl #impl_generics #frame_support::traits::VariantCount for #name #ty_generics #where_clause { + const VARIANT_COUNT: u32 = #variants_count; + } + )); + } + + expand +} diff --git a/support/procedural-fork/src/pallet/expand/config.rs b/support/procedural-fork/src/pallet/expand/config.rs new file mode 100644 index 000000000..5cf4035a8 --- /dev/null +++ b/support/procedural-fork/src/pallet/expand/config.rs @@ -0,0 +1,97 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::pallet::Def; +use proc_macro2::TokenStream; +use quote::quote; +use syn::{parse_quote, Item}; + +/// +/// * Generate default rust doc +pub fn expand_config(def: &mut Def) -> TokenStream { + let config = &def.config; + let config_item = { + let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[config.index]; + if let Item::Trait(item) = item { + item + } else { + unreachable!("Checked by config parser") + } + }; + + config_item.attrs.insert( + 0, + parse_quote!( + #[doc = r" +Configuration trait of this pallet. + +The main purpose of this trait is to act as an interface between this pallet and the runtime in +which it is embedded in. A type, function, or constant in this trait is essentially left to be +configured by the runtime that includes this pallet. + +Consequently, a runtime that wants to include this pallet must implement this trait." + ] + ), + ); + + // we only emit `DefaultConfig` if there are trait items, so an empty `DefaultConfig` is + // impossible consequently. + match &config.default_sub_trait { + Some(default_sub_trait) if default_sub_trait.items.len() > 0 => { + let trait_items = &default_sub_trait + .items + .iter() + .map(|item| { + if item.1 { + if let syn::TraitItem::Type(item) = item.0.clone() { + let mut item = item.clone(); + item.bounds.clear(); + syn::TraitItem::Type(item) + } else { + item.0.clone() + } + } else { + item.0.clone() + } + }) + .collect::>(); + + let type_param_bounds = if default_sub_trait.has_system { + let system = &def.frame_system; + quote::quote!(: #system::DefaultConfig) + } else { + quote::quote!() + }; + + quote!( + /// Based on [`Config`]. Auto-generated by + /// [`#[pallet::config(with_default)]`](`frame_support::pallet_macros::config`). + /// Can be used in tandem with + /// [`#[register_default_config]`](`frame_support::register_default_config`) and + /// [`#[derive_impl]`](`frame_support::derive_impl`) to derive test config traits + /// based on existing pallet config traits in a safe and developer-friendly way. + /// + /// See [here](`frame_support::pallet_macros::config`) for more information and caveats about + /// the auto-generated `DefaultConfig` trait and how it is generated. + pub trait DefaultConfig #type_param_bounds { + #(#trait_items)* + } + ) + }, + _ => Default::default(), + } +} diff --git a/support/procedural-fork/src/pallet/expand/constants.rs b/support/procedural-fork/src/pallet/expand/constants.rs new file mode 100644 index 000000000..57fa8b7f3 --- /dev/null +++ b/support/procedural-fork/src/pallet/expand/constants.rs @@ -0,0 +1,108 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::pallet::Def; + +struct ConstDef { + /// Name of the associated type. + pub ident: syn::Ident, + /// The type in Get, e.g. `u32` in `type Foo: Get;`, but `Self` is replaced by `T` + pub type_: syn::Type, + /// The doc associated + pub doc: Vec, + /// default_byte implementation + pub default_byte_impl: proc_macro2::TokenStream, + /// Constant name for Metadata (optional) + pub metadata_name: Option, +} + +/// +/// * Impl fn module_constant_metadata for pallet. +pub fn expand_constants(def: &mut Def) -> proc_macro2::TokenStream { + let frame_support = &def.frame_support; + let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site()); + let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site()); + let pallet_ident = &def.pallet_struct.pallet; + let trait_use_gen = &def.trait_use_generics(proc_macro2::Span::call_site()); + + let mut where_clauses = vec![&def.config.where_clause]; + where_clauses.extend(def.extra_constants.iter().map(|d| &d.where_clause)); + let completed_where_clause = super::merge_where_clauses(&where_clauses); + + let config_consts = def.config.consts_metadata.iter().map(|const_| { + let ident = &const_.ident; + let const_type = &const_.type_; + + ConstDef { + ident: const_.ident.clone(), + type_: const_.type_.clone(), + doc: const_.doc.clone(), + default_byte_impl: quote::quote!( + let value = <::#ident as + #frame_support::traits::Get<#const_type>>::get(); + #frame_support::__private::codec::Encode::encode(&value) + ), + metadata_name: None, + } + }); + + let extra_consts = def.extra_constants.iter().flat_map(|d| &d.extra_constants).map(|const_| { + let ident = &const_.ident; + + ConstDef { + ident: const_.ident.clone(), + type_: const_.type_.clone(), + doc: const_.doc.clone(), + default_byte_impl: quote::quote!( + let value = >::#ident(); + #frame_support::__private::codec::Encode::encode(&value) + ), + metadata_name: const_.metadata_name.clone(), + } + }); + + let consts = config_consts.chain(extra_consts).map(|const_| { + let const_type = &const_.type_; + let ident_str = format!("{}", const_.metadata_name.unwrap_or(const_.ident)); + + let no_docs = vec![]; + let doc = if cfg!(feature = "no-metadata-docs") { &no_docs } else { &const_.doc }; + + let default_byte_impl = &const_.default_byte_impl; + + quote::quote!({ + #frame_support::__private::metadata_ir::PalletConstantMetadataIR { + name: #ident_str, + ty: #frame_support::__private::scale_info::meta_type::<#const_type>(), + value: { #default_byte_impl }, + docs: #frame_support::__private::sp_std::vec![ #( #doc ),* ], + } + }) + }); + + quote::quote!( + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause{ + + #[doc(hidden)] + pub fn pallet_constants_metadata() + -> #frame_support::__private::sp_std::vec::Vec<#frame_support::__private::metadata_ir::PalletConstantMetadataIR> + { + #frame_support::__private::sp_std::vec![ #( #consts ),* ] + } + } + ) +} diff --git a/support/procedural-fork/src/pallet/expand/doc_only.rs b/support/procedural-fork/src/pallet/expand/doc_only.rs new file mode 100644 index 000000000..621a051ac --- /dev/null +++ b/support/procedural-fork/src/pallet/expand/doc_only.rs @@ -0,0 +1,103 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use proc_macro2::Span; + +use crate::pallet::Def; + +pub fn expand_doc_only(def: &mut Def) -> proc_macro2::TokenStream { + let dispatchables = if let Some(call_def) = &def.call { + let type_impl_generics = def.type_impl_generics(Span::call_site()); + call_def + .methods + .iter() + .map(|method| { + let name = &method.name; + let args = &method + .args + .iter() + .map(|(_, arg_name, arg_type)| quote::quote!( #arg_name: #arg_type, )) + .collect::(); + let docs = &method.docs; + + let real = format!(" [`Pallet::{}`].", name); + quote::quote!( + #( #[doc = #docs] )* + /// + /// # Warning: Doc-Only + /// + /// This function is an automatically generated, and is doc-only, uncallable + /// stub. See the real version in + #[ doc = #real ] + pub fn #name<#type_impl_generics>(#args) { unreachable!(); } + ) + }) + .collect::() + } else { + quote::quote!() + }; + + let storage_types = def + .storages + .iter() + .map(|storage| { + let storage_name = &storage.ident; + let storage_type_docs = &storage.docs; + let real = format!("[`pallet::{}`].", storage_name); + quote::quote!( + #( #[doc = #storage_type_docs] )* + /// + /// # Warning: Doc-Only + /// + /// This type is automatically generated, and is doc-only. See the real version in + #[ doc = #real ] + pub struct #storage_name(); + ) + }) + .collect::(); + + quote::quote!( + /// Auto-generated docs-only module listing all (public and private) defined storage types + /// for this pallet. + /// + /// # Warning: Doc-Only + /// + /// Members of this module cannot be used directly and are only provided for documentation + /// purposes. + /// + /// To see the actual storage type, find a struct with the same name at the root of the + /// pallet, in the list of [*Type Definitions*](../index.html#types). + #[cfg(doc)] + pub mod storage_types { + use super::*; + #storage_types + } + + /// Auto-generated docs-only module listing all defined dispatchables for this pallet. + /// + /// # Warning: Doc-Only + /// + /// Members of this module cannot be used directly and are only provided for documentation + /// purposes. To see the real version of each dispatchable, look for them in [`Pallet`] or + /// [`Call`]. + #[cfg(doc)] + pub mod dispatchables { + use super::*; + #dispatchables + } + ) +} diff --git a/support/procedural-fork/src/pallet/expand/documentation.rs b/support/procedural-fork/src/pallet/expand/documentation.rs new file mode 100644 index 000000000..ec19f889a --- /dev/null +++ b/support/procedural-fork/src/pallet/expand/documentation.rs @@ -0,0 +1,172 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::pallet::Def; +use proc_macro2::TokenStream; +use quote::ToTokens; +use syn::{spanned::Spanned, Attribute, Lit, LitStr}; + +const DOC: &'static str = "doc"; +const PALLET_DOC: &'static str = "pallet_doc"; + +/// Get the documentation file path from the `pallet_doc` attribute. +/// +/// Supported format: +/// `#[pallet_doc(PATH)]`: The path of the file from which the documentation is loaded +fn parse_pallet_doc_value(attr: &Attribute) -> syn::Result { + let lit: syn::LitStr = attr.parse_args().map_err(|_| { + let msg = "The `pallet_doc` received an unsupported argument. Supported format: `pallet_doc(\"PATH\")`"; + syn::Error::new(attr.span(), msg) + })?; + + Ok(DocMetaValue::Path(lit)) +} + +/// Get the value from the `doc` comment attribute: +/// +/// Supported formats: +/// - `#[doc = "A doc string"]`: Documentation as a string literal +/// - `#[doc = include_str!(PATH)]`: Documentation obtained from a path +fn parse_doc_value(attr: &Attribute) -> syn::Result> { + if !attr.path().is_ident(DOC) { + return Ok(None) + } + + let meta = attr.meta.require_name_value()?; + + match &meta.value { + syn::Expr::Lit(lit) => Ok(Some(DocMetaValue::Lit(lit.lit.clone()))), + syn::Expr::Macro(mac) if mac.mac.path.is_ident("include_str") => + Ok(Some(DocMetaValue::Path(mac.mac.parse_body()?))), + _ => + Err(syn::Error::new(attr.span(), "Expected `= \"docs\"` or `= include_str!(\"PATH\")`")), + } +} + +/// Supported documentation tokens. +#[derive(Debug)] +enum DocMetaValue { + /// Documentation with string literals. + /// + /// `#[doc = "Lit"]` + Lit(Lit), + /// Documentation with `include_str!` macro. + /// + /// The string literal represents the file `PATH`. + /// + /// `#[doc = include_str!(PATH)]` + Path(LitStr), +} + +impl ToTokens for DocMetaValue { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + DocMetaValue::Lit(lit) => lit.to_tokens(tokens), + DocMetaValue::Path(path_lit) => { + let decl = quote::quote!(include_str!(#path_lit)); + tokens.extend(decl) + }, + } + } +} + +/// Extract the documentation from the given pallet definition +/// to include in the runtime metadata. +/// +/// Implement a `pallet_documentation_metadata` function to fetch the +/// documentation that is included in the metadata. +/// +/// The documentation is placed on the pallet similar to: +/// +/// ```ignore +/// #[pallet] +/// /// Documentation for pallet +/// #[doc = "Documentation for pallet"] +/// #[doc = include_str!("../README.md")] +/// #[pallet_doc("../documentation1.md")] +/// #[pallet_doc("../documentation2.md")] +/// pub mod pallet {} +/// ``` +/// +/// # pallet_doc +/// +/// The `pallet_doc` attribute can only be provided with one argument, +/// which is the file path that holds the documentation to be added to the metadata. +/// +/// Unlike the `doc` attribute, the documentation provided to the `proc_macro` attribute is +/// not added to the pallet. +pub fn expand_documentation(def: &mut Def) -> proc_macro2::TokenStream { + let frame_support = &def.frame_support; + let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site()); + let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site()); + let pallet_ident = &def.pallet_struct.pallet; + let where_clauses = &def.config.where_clause; + + // TODO: Use [drain_filter](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.drain_filter) when it is stable. + + // The `pallet_doc` attributes are excluded from the generation of the pallet, + // but they are included in the runtime metadata. + let mut pallet_docs = Vec::with_capacity(def.item.attrs.len()); + let mut index = 0; + while index < def.item.attrs.len() { + let attr = &def.item.attrs[index]; + if attr.path().get_ident().map_or(false, |i| *i == PALLET_DOC) { + pallet_docs.push(def.item.attrs.remove(index)); + // Do not increment the index, we have just removed the + // element from the attributes. + continue + } + + index += 1; + } + + // Capture the `#[doc = include_str!("../README.md")]` and `#[doc = "Documentation"]`. + let docs = match def + .item + .attrs + .iter() + .filter_map(|v| parse_doc_value(v).transpose()) + .collect::>>() + { + Ok(r) => r, + Err(err) => return err.into_compile_error(), + }; + + // Capture the `#[pallet_doc("../README.md")]`. + let pallet_docs = match pallet_docs + .into_iter() + .map(|attr| parse_pallet_doc_value(&attr)) + .collect::>>() + { + Ok(docs) => docs, + Err(err) => return err.into_compile_error(), + }; + + let docs = docs.iter().chain(pallet_docs.iter()); + + quote::quote!( + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #where_clauses{ + + #[doc(hidden)] + pub fn pallet_documentation_metadata() + -> #frame_support::__private::sp_std::vec::Vec<&'static str> + { + #frame_support::__private::sp_std::vec![ #( #docs ),* ] + } + } + ) +} diff --git a/support/procedural-fork/src/pallet/expand/error.rs b/support/procedural-fork/src/pallet/expand/error.rs new file mode 100644 index 000000000..72fb6e923 --- /dev/null +++ b/support/procedural-fork/src/pallet/expand/error.rs @@ -0,0 +1,191 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{ + pallet::{ + parse::error::{VariantDef, VariantField}, + Def, + }, + COUNTER, +}; +use frame_support_procedural_tools::get_doc_literals; +use quote::ToTokens; +use syn::spanned::Spanned; + +/// +/// * impl various trait on Error +pub fn expand_error(def: &mut Def) -> proc_macro2::TokenStream { + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + let error_token_unique_id = + syn::Ident::new(&format!("__tt_error_token_{}", count), def.item.span()); + + let frame_support = &def.frame_support; + let frame_system = &def.frame_system; + let config_where_clause = &def.config.where_clause; + + let error = if let Some(error) = &def.error { + error + } else { + return quote::quote! { + #[macro_export] + #[doc(hidden)] + macro_rules! #error_token_unique_id { + { + $caller:tt + your_tt_return = [{ $my_tt_return:path }] + } => { + $my_tt_return! { + $caller + } + }; + } + + pub use #error_token_unique_id as tt_error_token; + } + }; + + let error_ident = &error.error; + let type_impl_gen = &def.type_impl_generics(error.attr_span); + let type_use_gen = &def.type_use_generics(error.attr_span); + + let phantom_variant: syn::Variant = syn::parse_quote!( + #[doc(hidden)] + #[codec(skip)] + __Ignore( + #frame_support::__private::sp_std::marker::PhantomData<(#type_use_gen)>, + #frame_support::Never, + ) + ); + + let as_str_matches = error.variants.iter().map( + |VariantDef { ident: variant, field: field_ty, docs: _, cfg_attrs }| { + let variant_str = variant.to_string(); + let cfg_attrs = cfg_attrs.iter().map(|attr| attr.to_token_stream()); + match field_ty { + Some(VariantField { is_named: true }) => { + quote::quote_spanned!(error.attr_span => #( #cfg_attrs )* Self::#variant { .. } => #variant_str,) + }, + Some(VariantField { is_named: false }) => { + quote::quote_spanned!(error.attr_span => #( #cfg_attrs )* Self::#variant(..) => #variant_str,) + }, + None => { + quote::quote_spanned!(error.attr_span => #( #cfg_attrs )* Self::#variant => #variant_str,) + }, + } + }, + ); + + let error_item = { + let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[error.index]; + if let syn::Item::Enum(item) = item { + item + } else { + unreachable!("Checked by error parser") + } + }; + + error_item.variants.insert(0, phantom_variant); + + let capture_docs = if cfg!(feature = "no-metadata-docs") { "never" } else { "always" }; + + // derive TypeInfo for error metadata + error_item.attrs.push(syn::parse_quote! { + #[derive( + #frame_support::__private::codec::Encode, + #frame_support::__private::codec::Decode, + #frame_support::__private::scale_info::TypeInfo, + #frame_support::PalletError, + )] + }); + error_item.attrs.push(syn::parse_quote!( + #[scale_info(skip_type_params(#type_use_gen), capture_docs = #capture_docs)] + )); + + if get_doc_literals(&error_item.attrs).is_empty() { + error_item.attrs.push(syn::parse_quote!( + #[doc = "The `Error` enum of this pallet."] + )); + } + + quote::quote_spanned!(error.attr_span => + impl<#type_impl_gen> #frame_support::__private::sp_std::fmt::Debug for #error_ident<#type_use_gen> + #config_where_clause + { + fn fmt(&self, f: &mut #frame_support::__private::sp_std::fmt::Formatter<'_>) + -> #frame_support::__private::sp_std::fmt::Result + { + f.write_str(self.as_str()) + } + } + + impl<#type_impl_gen> #error_ident<#type_use_gen> #config_where_clause { + #[doc(hidden)] + pub fn as_str(&self) -> &'static str { + match &self { + Self::__Ignore(_, _) => unreachable!("`__Ignore` can never be constructed"), + #( #as_str_matches )* + } + } + } + + impl<#type_impl_gen> From<#error_ident<#type_use_gen>> for &'static str + #config_where_clause + { + fn from(err: #error_ident<#type_use_gen>) -> &'static str { + err.as_str() + } + } + + impl<#type_impl_gen> From<#error_ident<#type_use_gen>> + for #frame_support::sp_runtime::DispatchError + #config_where_clause + { + fn from(err: #error_ident<#type_use_gen>) -> Self { + use #frame_support::__private::codec::Encode; + let index = < + ::PalletInfo + as #frame_support::traits::PalletInfo + >::index::>() + .expect("Every active module has an index in the runtime; qed") as u8; + let mut encoded = err.encode(); + encoded.resize(#frame_support::MAX_MODULE_ERROR_ENCODED_SIZE, 0); + + #frame_support::sp_runtime::DispatchError::Module(#frame_support::sp_runtime::ModuleError { + index, + error: TryInto::try_into(encoded).expect("encoded error is resized to be equal to the maximum encoded error size; qed"), + message: Some(err.as_str()), + }) + } + } + + #[macro_export] + #[doc(hidden)] + macro_rules! #error_token_unique_id { + { + $caller:tt + your_tt_return = [{ $my_tt_return:path }] + } => { + $my_tt_return! { + $caller + error = [{ #error_ident }] + } + }; + } + + pub use #error_token_unique_id as tt_error_token; + ) +} diff --git a/support/procedural-fork/src/pallet/expand/event.rs b/support/procedural-fork/src/pallet/expand/event.rs new file mode 100644 index 000000000..655fc5507 --- /dev/null +++ b/support/procedural-fork/src/pallet/expand/event.rs @@ -0,0 +1,174 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{ + pallet::{parse::event::PalletEventDepositAttr, Def}, + COUNTER, +}; +use frame_support_procedural_tools::get_doc_literals; +use syn::{spanned::Spanned, Ident}; + +/// +/// * Add __Ignore variant on Event +/// * Impl various trait on Event including metadata +/// * if deposit_event is defined, implement deposit_event on module. +pub fn expand_event(def: &mut Def) -> proc_macro2::TokenStream { + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + + let (event, macro_ident) = if let Some(event) = &def.event { + let ident = Ident::new(&format!("__is_event_part_defined_{}", count), event.attr_span); + (event, ident) + } else { + let macro_ident = + Ident::new(&format!("__is_event_part_defined_{}", count), def.item.span()); + + return quote::quote! { + #[doc(hidden)] + pub mod __substrate_event_check { + #[macro_export] + #[doc(hidden)] + macro_rules! #macro_ident { + ($pallet_name:ident) => { + compile_error!(concat!( + "`", + stringify!($pallet_name), + "` does not have #[pallet::event] defined, perhaps you should \ + remove `Event` from construct_runtime?", + )); + } + } + + #[doc(hidden)] + pub use #macro_ident as is_event_part_defined; + } + } + }; + + let event_where_clause = &event.where_clause; + + // NOTE: actually event where clause must be a subset of config where clause because of + // `type RuntimeEvent: From>`. But we merge either way for potential better error + // message + let completed_where_clause = + super::merge_where_clauses(&[&event.where_clause, &def.config.where_clause]); + + let event_ident = &event.event; + let frame_system = &def.frame_system; + let frame_support = &def.frame_support; + let event_use_gen = &event.gen_kind.type_use_gen(event.attr_span); + let event_impl_gen = &event.gen_kind.type_impl_gen(event.attr_span); + + let event_item = { + let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[event.index]; + if let syn::Item::Enum(item) = item { + item + } else { + unreachable!("Checked by event parser") + } + }; + + // Phantom data is added for generic event. + if event.gen_kind.is_generic() { + let variant = syn::parse_quote!( + #[doc(hidden)] + #[codec(skip)] + __Ignore( + ::core::marker::PhantomData<(#event_use_gen)>, + #frame_support::Never, + ) + ); + + // Push ignore variant at the end. + event_item.variants.push(variant); + } + + if get_doc_literals(&event_item.attrs).is_empty() { + event_item + .attrs + .push(syn::parse_quote!(#[doc = "The `Event` enum of this pallet"])); + } + + // derive some traits because system event require Clone, FullCodec, Eq, PartialEq and Debug + event_item.attrs.push(syn::parse_quote!( + #[derive( + #frame_support::CloneNoBound, + #frame_support::EqNoBound, + #frame_support::PartialEqNoBound, + #frame_support::RuntimeDebugNoBound, + #frame_support::__private::codec::Encode, + #frame_support::__private::codec::Decode, + #frame_support::__private::scale_info::TypeInfo, + )] + )); + + let capture_docs = if cfg!(feature = "no-metadata-docs") { "never" } else { "always" }; + + // skip requirement for type params to implement `TypeInfo`, and set docs capture + event_item.attrs.push(syn::parse_quote!( + #[scale_info(skip_type_params(#event_use_gen), capture_docs = #capture_docs)] + )); + + let deposit_event = if let Some(deposit_event) = &event.deposit_event { + let event_use_gen = &event.gen_kind.type_use_gen(event.attr_span); + let trait_use_gen = &def.trait_use_generics(event.attr_span); + let type_impl_gen = &def.type_impl_generics(event.attr_span); + let type_use_gen = &def.type_use_generics(event.attr_span); + let pallet_ident = &def.pallet_struct.pallet; + + let PalletEventDepositAttr { fn_vis, fn_span, .. } = deposit_event; + + quote::quote_spanned!(*fn_span => + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { + #fn_vis fn deposit_event(event: Event<#event_use_gen>) { + let event = < + ::RuntimeEvent as + From> + >::from(event); + + let event = < + ::RuntimeEvent as + Into<::RuntimeEvent> + >::into(event); + + <#frame_system::Pallet>::deposit_event(event) + } + } + ) + } else { + Default::default() + }; + + quote::quote_spanned!(event.attr_span => + #[doc(hidden)] + pub mod __substrate_event_check { + #[macro_export] + #[doc(hidden)] + macro_rules! #macro_ident { + ($pallet_name:ident) => {}; + } + + #[doc(hidden)] + pub use #macro_ident as is_event_part_defined; + } + + #deposit_event + + impl<#event_impl_gen> From<#event_ident<#event_use_gen>> for () #event_where_clause { + fn from(_: #event_ident<#event_use_gen>) {} + } + ) +} diff --git a/support/procedural-fork/src/pallet/expand/genesis_build.rs b/support/procedural-fork/src/pallet/expand/genesis_build.rs new file mode 100644 index 000000000..248e83469 --- /dev/null +++ b/support/procedural-fork/src/pallet/expand/genesis_build.rs @@ -0,0 +1,49 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::pallet::Def; + +/// +/// * implement the trait `sp_runtime::BuildStorage` +pub fn expand_genesis_build(def: &mut Def) -> proc_macro2::TokenStream { + let genesis_config = if let Some(genesis_config) = &def.genesis_config { + genesis_config + } else { + return Default::default() + }; + let genesis_build = def.genesis_build.as_ref().expect("Checked by def parser"); + + let frame_support = &def.frame_support; + let type_impl_gen = &genesis_config.gen_kind.type_impl_gen(genesis_build.attr_span); + let gen_cfg_ident = &genesis_config.genesis_config; + let gen_cfg_use_gen = &genesis_config.gen_kind.type_use_gen(genesis_build.attr_span); + + let where_clause = &genesis_build.where_clause; + + quote::quote_spanned!(genesis_build.attr_span => + #[cfg(feature = "std")] + impl<#type_impl_gen> #frame_support::sp_runtime::BuildStorage for #gen_cfg_ident<#gen_cfg_use_gen> #where_clause + { + fn assimilate_storage(&self, storage: &mut #frame_support::sp_runtime::Storage) -> std::result::Result<(), std::string::String> { + #frame_support::__private::BasicExternalities::execute_with_storage(storage, || { + self.build(); + Ok(()) + }) + } + } + ) +} diff --git a/support/procedural-fork/src/pallet/expand/genesis_config.rs b/support/procedural-fork/src/pallet/expand/genesis_config.rs new file mode 100644 index 000000000..31d519ef2 --- /dev/null +++ b/support/procedural-fork/src/pallet/expand/genesis_config.rs @@ -0,0 +1,147 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{pallet::Def, COUNTER}; +use frame_support_procedural_tools::get_doc_literals; +use quote::ToTokens; +use syn::{spanned::Spanned, Ident}; + +/// +/// * add various derive trait on GenesisConfig struct. +pub fn expand_genesis_config(def: &mut Def) -> proc_macro2::TokenStream { + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + + let (genesis_config, def_macro_ident, std_macro_ident) = + if let Some(genesis_config) = &def.genesis_config { + let def_macro_ident = Ident::new( + &format!("__is_genesis_config_defined_{}", count), + genesis_config.genesis_config.span(), + ); + + let std_macro_ident = Ident::new( + &format!("__is_std_macro_defined_for_genesis_{}", count), + genesis_config.genesis_config.span(), + ); + + (genesis_config, def_macro_ident, std_macro_ident) + } else { + let def_macro_ident = + Ident::new(&format!("__is_genesis_config_defined_{}", count), def.item.span()); + + let std_macro_ident = + Ident::new(&format!("__is_std_enabled_for_genesis_{}", count), def.item.span()); + + return quote::quote! { + #[doc(hidden)] + pub mod __substrate_genesis_config_check { + #[macro_export] + #[doc(hidden)] + macro_rules! #def_macro_ident { + ($pallet_name:ident) => { + compile_error!(concat!( + "`", + stringify!($pallet_name), + "` does not have #[pallet::genesis_config] defined, perhaps you should \ + remove `Config` from construct_runtime?", + )); + } + } + + #[macro_export] + #[doc(hidden)] + macro_rules! #std_macro_ident { + ($pallet_name:ident, $pallet_path:expr) => {}; + } + + #[doc(hidden)] + pub use #def_macro_ident as is_genesis_config_defined; + #[doc(hidden)] + pub use #std_macro_ident as is_std_enabled_for_genesis; + } + } + }; + + let frame_support = &def.frame_support; + + let genesis_config_item = + &mut def.item.content.as_mut().expect("Checked by def parser").1[genesis_config.index]; + + let serde_crate = format!("{}::__private::serde", frame_support.to_token_stream()); + + match genesis_config_item { + syn::Item::Enum(syn::ItemEnum { attrs, .. }) | + syn::Item::Struct(syn::ItemStruct { attrs, .. }) | + syn::Item::Type(syn::ItemType { attrs, .. }) => { + if get_doc_literals(attrs).is_empty() { + attrs.push(syn::parse_quote!( + #[doc = r" + Can be used to configure the + [genesis state](https://docs.substrate.io/build/genesis-configuration/) + of this pallet. + "] + )); + } + attrs.push(syn::parse_quote!( + #[derive(#frame_support::Serialize, #frame_support::Deserialize)] + )); + attrs.push(syn::parse_quote!( #[serde(rename_all = "camelCase")] )); + attrs.push(syn::parse_quote!( #[serde(deny_unknown_fields)] )); + attrs.push(syn::parse_quote!( #[serde(bound(serialize = ""))] )); + attrs.push(syn::parse_quote!( #[serde(bound(deserialize = ""))] )); + attrs.push(syn::parse_quote!( #[serde(crate = #serde_crate)] )); + }, + _ => unreachable!("Checked by genesis_config parser"), + } + + quote::quote! { + #[doc(hidden)] + pub mod __substrate_genesis_config_check { + #[macro_export] + #[doc(hidden)] + macro_rules! #def_macro_ident { + ($pallet_name:ident) => {}; + } + + #[cfg(not(feature = "std"))] + #[macro_export] + #[doc(hidden)] + macro_rules! #std_macro_ident { + ($pallet_name:ident, $pallet_path:expr) => { + compile_error!(concat!( + "`", + stringify!($pallet_name), + "` does not have the std feature enabled, this will cause the `", + $pallet_path, + "::GenesisConfig` type to not implement serde traits." + )); + }; + } + + #[cfg(feature = "std")] + #[macro_export] + #[doc(hidden)] + macro_rules! #std_macro_ident { + ($pallet_name:ident, $pallet_path:expr) => {}; + } + + #[doc(hidden)] + pub use #def_macro_ident as is_genesis_config_defined; + #[doc(hidden)] + pub use #std_macro_ident as is_std_enabled_for_genesis; + } + } +} diff --git a/support/procedural-fork/src/pallet/expand/hooks.rs b/support/procedural-fork/src/pallet/expand/hooks.rs new file mode 100644 index 000000000..3623b5952 --- /dev/null +++ b/support/procedural-fork/src/pallet/expand/hooks.rs @@ -0,0 +1,340 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::pallet::Def; + +/// * implement the individual traits using the Hooks trait +pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { + let (where_clause, span, has_runtime_upgrade) = match def.hooks.as_ref() { + Some(hooks) => { + let where_clause = hooks.where_clause.clone(); + let span = hooks.attr_span; + let has_runtime_upgrade = hooks.has_runtime_upgrade; + (where_clause, span, has_runtime_upgrade) + }, + None => (def.config.where_clause.clone(), def.pallet_struct.attr_span, false), + }; + + let frame_support = &def.frame_support; + let type_impl_gen = &def.type_impl_generics(span); + let type_use_gen = &def.type_use_generics(span); + let pallet_ident = &def.pallet_struct.pallet; + let frame_system = &def.frame_system; + let pallet_name = quote::quote! { + < + ::PalletInfo + as + #frame_support::traits::PalletInfo + >::name::().unwrap_or("") + }; + + let initialize_on_chain_storage_version = if let Some(in_code_version) = + &def.pallet_struct.storage_version + { + quote::quote! { + #frame_support::__private::log::info!( + target: #frame_support::LOG_TARGET, + "🐥 New pallet {:?} detected in the runtime. Initializing the on-chain storage version to match the storage version defined in the pallet: {:?}", + #pallet_name, + #in_code_version + ); + #in_code_version.put::(); + } + } else { + quote::quote! { + let default_version = #frame_support::traits::StorageVersion::new(0); + #frame_support::__private::log::info!( + target: #frame_support::LOG_TARGET, + "🐥 New pallet {:?} detected in the runtime. The pallet has no defined storage version, so the on-chain version is being initialized to {:?}.", + #pallet_name, + default_version + ); + default_version.put::(); + } + }; + + let log_runtime_upgrade = if has_runtime_upgrade { + // a migration is defined here. + quote::quote! { + #frame_support::__private::log::info!( + target: #frame_support::LOG_TARGET, + "⚠️ {} declares internal migrations (which *might* execute). \ + On-chain `{:?}` vs in-code storage version `{:?}`", + #pallet_name, + ::on_chain_storage_version(), + ::in_code_storage_version(), + ); + } + } else { + // default. + quote::quote! { + #frame_support::__private::log::debug!( + target: #frame_support::LOG_TARGET, + "✅ no migration for {}", + #pallet_name, + ); + } + }; + + let hooks_impl = if def.hooks.is_none() { + let frame_system = &def.frame_system; + quote::quote! { + impl<#type_impl_gen> + #frame_support::traits::Hooks<#frame_system::pallet_prelude::BlockNumberFor::> + for #pallet_ident<#type_use_gen> #where_clause {} + } + } else { + proc_macro2::TokenStream::new() + }; + + // If a storage version is set, we should ensure that the storage version on chain matches the + // in-code storage version. This assumes that `Executive` is running custom migrations before + // the pallets are called. + let post_storage_version_check = if def.pallet_struct.storage_version.is_some() { + quote::quote! { + let on_chain_version = ::on_chain_storage_version(); + let in_code_version = ::in_code_storage_version(); + + if on_chain_version != in_code_version { + #frame_support::__private::log::error!( + target: #frame_support::LOG_TARGET, + "{}: On chain storage version {:?} doesn't match in-code storage version {:?}.", + #pallet_name, + on_chain_version, + in_code_version, + ); + + return Err("On chain and in-code storage version do not match. Missing runtime upgrade?".into()); + } + } + } else { + quote::quote! { + let on_chain_version = ::on_chain_storage_version(); + + if on_chain_version != #frame_support::traits::StorageVersion::new(0) { + #frame_support::__private::log::error!( + target: #frame_support::LOG_TARGET, + "{}: On chain storage version {:?} is set to non zero, \ + while the pallet is missing the `#[pallet::storage_version(VERSION)]` attribute.", + #pallet_name, + on_chain_version, + ); + + return Err("On chain storage version set, while the pallet doesn't \ + have the `#[pallet::storage_version(VERSION)]` attribute.".into()); + } + } + }; + + quote::quote_spanned!(span => + #hooks_impl + + impl<#type_impl_gen> + #frame_support::traits::OnFinalize<#frame_system::pallet_prelude::BlockNumberFor::> + for #pallet_ident<#type_use_gen> #where_clause + { + fn on_finalize(n: #frame_system::pallet_prelude::BlockNumberFor::) { + #frame_support::__private::sp_tracing::enter_span!( + #frame_support::__private::sp_tracing::trace_span!("on_finalize") + ); + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::on_finalize(n) + } + } + + impl<#type_impl_gen> + #frame_support::traits::OnIdle<#frame_system::pallet_prelude::BlockNumberFor::> + for #pallet_ident<#type_use_gen> #where_clause + { + fn on_idle( + n: #frame_system::pallet_prelude::BlockNumberFor::, + remaining_weight: #frame_support::weights::Weight + ) -> #frame_support::weights::Weight { + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::on_idle(n, remaining_weight) + } + } + + impl<#type_impl_gen> + #frame_support::traits::OnPoll<#frame_system::pallet_prelude::BlockNumberFor::> + for #pallet_ident<#type_use_gen> #where_clause + { + fn on_poll( + n: #frame_system::pallet_prelude::BlockNumberFor::, + weight: &mut #frame_support::weights::WeightMeter + ) { + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::on_poll(n, weight); + } + } + + impl<#type_impl_gen> + #frame_support::traits::OnInitialize<#frame_system::pallet_prelude::BlockNumberFor::> + for #pallet_ident<#type_use_gen> #where_clause + { + fn on_initialize( + n: #frame_system::pallet_prelude::BlockNumberFor:: + ) -> #frame_support::weights::Weight { + #frame_support::__private::sp_tracing::enter_span!( + #frame_support::__private::sp_tracing::trace_span!("on_initialize") + ); + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::on_initialize(n) + } + } + + impl<#type_impl_gen> + #frame_support::traits::BeforeAllRuntimeMigrations + for #pallet_ident<#type_use_gen> #where_clause + { + fn before_all_runtime_migrations() -> #frame_support::weights::Weight { + use #frame_support::traits::{Get, PalletInfoAccess}; + use #frame_support::__private::hashing::twox_128; + use #frame_support::storage::unhashed::contains_prefixed_key; + #frame_support::__private::sp_tracing::enter_span!( + #frame_support::__private::sp_tracing::trace_span!("before_all") + ); + + // Check if the pallet has any keys set, including the storage version. If there are + // no keys set, the pallet was just added to the runtime and needs to have its + // version initialized. + let pallet_hashed_prefix = ::name_hash(); + let exists = contains_prefixed_key(&pallet_hashed_prefix); + if !exists { + #initialize_on_chain_storage_version + ::DbWeight::get().reads_writes(1, 1) + } else { + ::DbWeight::get().reads(1) + } + } + } + + impl<#type_impl_gen> + #frame_support::traits::OnRuntimeUpgrade + for #pallet_ident<#type_use_gen> #where_clause + { + fn on_runtime_upgrade() -> #frame_support::weights::Weight { + #frame_support::__private::sp_tracing::enter_span!( + #frame_support::__private::sp_tracing::trace_span!("on_runtime_update") + ); + + // log info about the upgrade. + #log_runtime_upgrade + + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::on_runtime_upgrade() + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result<#frame_support::__private::sp_std::vec::Vec, #frame_support::sp_runtime::TryRuntimeError> { + < + Self + as + #frame_support::traits::Hooks<#frame_system::pallet_prelude::BlockNumberFor::> + >::pre_upgrade() + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: #frame_support::__private::sp_std::vec::Vec) -> Result<(), #frame_support::sp_runtime::TryRuntimeError> { + #post_storage_version_check + + < + Self + as + #frame_support::traits::Hooks<#frame_system::pallet_prelude::BlockNumberFor::> + >::post_upgrade(state) + } + } + + impl<#type_impl_gen> + #frame_support::traits::OffchainWorker<#frame_system::pallet_prelude::BlockNumberFor::> + for #pallet_ident<#type_use_gen> #where_clause + { + fn offchain_worker(n: #frame_system::pallet_prelude::BlockNumberFor::) { + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::offchain_worker(n) + } + } + + // Integrity tests are only required for when `std` is enabled. + #frame_support::std_enabled! { + impl<#type_impl_gen> + #frame_support::traits::IntegrityTest + for #pallet_ident<#type_use_gen> #where_clause + { + fn integrity_test() { + #frame_support::__private::sp_io::TestExternalities::default().execute_with(|| { + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::integrity_test() + }); + } + } + } + + #[cfg(feature = "try-runtime")] + impl<#type_impl_gen> + #frame_support::traits::TryState<#frame_system::pallet_prelude::BlockNumberFor::> + for #pallet_ident<#type_use_gen> #where_clause + { + fn try_state( + n: #frame_system::pallet_prelude::BlockNumberFor::, + _s: #frame_support::traits::TryStateSelect + ) -> Result<(), #frame_support::sp_runtime::TryRuntimeError> { + #frame_support::__private::log::info!( + target: #frame_support::LOG_TARGET, + "🩺 Running {:?} try-state checks", + #pallet_name, + ); + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::try_state(n).map_err(|err| { + #frame_support::__private::log::error!( + target: #frame_support::LOG_TARGET, + "❌ {:?} try_state checks failed: {:?}", + #pallet_name, + err + ); + + err + }) + } + } + ) +} diff --git a/support/procedural-fork/src/pallet/expand/inherent.rs b/support/procedural-fork/src/pallet/expand/inherent.rs new file mode 100644 index 000000000..182d79f5b --- /dev/null +++ b/support/procedural-fork/src/pallet/expand/inherent.rs @@ -0,0 +1,55 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{pallet::Def, COUNTER}; +use proc_macro2::TokenStream; +use quote::quote; +use syn::{spanned::Spanned, Ident}; + +pub fn expand_inherents(def: &mut Def) -> TokenStream { + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + let macro_ident = Ident::new(&format!("__is_inherent_part_defined_{}", count), def.item.span()); + + let maybe_compile_error = if def.inherent.is_none() { + quote! { + compile_error!(concat!( + "`", + stringify!($pallet_name), + "` does not have #[pallet::inherent] defined, perhaps you should \ + remove `Inherent` from construct_runtime?", + )); + } + } else { + TokenStream::new() + }; + + quote! { + #[doc(hidden)] + pub mod __substrate_inherent_check { + #[macro_export] + #[doc(hidden)] + macro_rules! #macro_ident { + ($pallet_name:ident) => { + #maybe_compile_error + } + } + + #[doc(hidden)] + pub use #macro_ident as is_inherent_part_defined; + } + } +} diff --git a/support/procedural-fork/src/pallet/expand/instances.rs b/support/procedural-fork/src/pallet/expand/instances.rs new file mode 100644 index 000000000..b6dfa7e6d --- /dev/null +++ b/support/procedural-fork/src/pallet/expand/instances.rs @@ -0,0 +1,43 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{pallet::Def, NUMBER_OF_INSTANCE}; +use proc_macro2::Span; + +/// +/// * Provide inherent instance to be used by construct_runtime +/// * Provide Instance1 ..= Instance16 for instantiable pallet +pub fn expand_instances(def: &mut Def) -> proc_macro2::TokenStream { + let frame_support = &def.frame_support; + let inherent_ident = syn::Ident::new(crate::INHERENT_INSTANCE_NAME, Span::call_site()); + let instances = if def.config.has_instance { + (1..=NUMBER_OF_INSTANCE) + .map(|i| syn::Ident::new(&format!("Instance{}", i), Span::call_site())) + .collect() + } else { + vec![] + }; + + quote::quote!( + /// Hidden instance generated to be internally used when module is used without + /// instance. + #[doc(hidden)] + pub type #inherent_ident = (); + + #( pub use #frame_support::instances::#instances; )* + ) +} diff --git a/support/procedural-fork/src/pallet/expand/mod.rs b/support/procedural-fork/src/pallet/expand/mod.rs new file mode 100644 index 000000000..067839c28 --- /dev/null +++ b/support/procedural-fork/src/pallet/expand/mod.rs @@ -0,0 +1,130 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +mod call; +mod composite; +mod config; +mod constants; +mod doc_only; +mod documentation; +mod error; +mod event; +mod genesis_build; +mod genesis_config; +mod hooks; +mod inherent; +mod instances; +mod origin; +mod pallet_struct; +mod storage; +mod tasks; +mod tt_default_parts; +mod type_value; +mod validate_unsigned; +mod warnings; + +use crate::pallet::Def; +use quote::ToTokens; + +/// Merge where clause together, `where` token span is taken from the first not none one. +pub fn merge_where_clauses(clauses: &[&Option]) -> Option { + let mut clauses = clauses.iter().filter_map(|f| f.as_ref()); + let mut res = clauses.next()?.clone(); + for other in clauses { + res.predicates.extend(other.predicates.iter().cloned()) + } + Some(res) +} + +/// Expand definition, in particular: +/// * add some bounds and variants to type defined, +/// * create some new types, +/// * impl stuff on them. +pub fn expand(mut def: Def) -> proc_macro2::TokenStream { + // Remove the `pallet_doc` attribute first. + let metadata_docs = documentation::expand_documentation(&mut def); + let constants = constants::expand_constants(&mut def); + let pallet_struct = pallet_struct::expand_pallet_struct(&mut def); + let config = config::expand_config(&mut def); + let call = call::expand_call(&mut def); + let tasks = tasks::expand_tasks(&mut def); + let error = error::expand_error(&mut def); + let event = event::expand_event(&mut def); + let storages = storage::expand_storages(&mut def); + let inherents = inherent::expand_inherents(&mut def); + let instances = instances::expand_instances(&mut def); + let hooks = hooks::expand_hooks(&mut def); + let genesis_build = genesis_build::expand_genesis_build(&mut def); + let genesis_config = genesis_config::expand_genesis_config(&mut def); + let type_values = type_value::expand_type_values(&mut def); + let origins = origin::expand_origins(&mut def); + let validate_unsigned = validate_unsigned::expand_validate_unsigned(&mut def); + let tt_default_parts = tt_default_parts::expand_tt_default_parts(&mut def); + let doc_only = doc_only::expand_doc_only(&mut def); + let composites = composite::expand_composites(&mut def); + + def.item.attrs.insert( + 0, + syn::parse_quote!( + #[doc = r"The `pallet` module in each FRAME pallet hosts the most important items needed +to construct this pallet. + +The main components of this pallet are: +- [`Pallet`], which implements all of the dispatchable extrinsics of the pallet, among +other public functions. + - The subset of the functions that are dispatchable can be identified either in the + [`dispatchables`] module or in the [`Call`] enum. +- [`storage_types`], which contains the list of all types that are representing a +storage item. Otherwise, all storage items are listed among [*Type Definitions*](#types). +- [`Config`], which contains the configuration trait of this pallet. +- [`Event`] and [`Error`], which are listed among the [*Enums*](#enums). + "] + ), + ); + + let new_items = quote::quote!( + #metadata_docs + #constants + #pallet_struct + #config + #call + #tasks + #error + #event + #storages + #inherents + #instances + #hooks + #genesis_build + #genesis_config + #type_values + #origins + #validate_unsigned + #tt_default_parts + #doc_only + #composites + ); + + def.item + .content + .as_mut() + .expect("This is checked by parsing") + .1 + .push(syn::Item::Verbatim(new_items)); + + def.item.into_token_stream() +} diff --git a/support/procedural-fork/src/pallet/expand/origin.rs b/support/procedural-fork/src/pallet/expand/origin.rs new file mode 100644 index 000000000..55865b424 --- /dev/null +++ b/support/procedural-fork/src/pallet/expand/origin.rs @@ -0,0 +1,55 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{pallet::Def, COUNTER}; +use proc_macro2::TokenStream; +use quote::quote; +use syn::{spanned::Spanned, Ident}; + +pub fn expand_origins(def: &mut Def) -> TokenStream { + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + let macro_ident = Ident::new(&format!("__is_origin_part_defined_{}", count), def.item.span()); + + let maybe_compile_error = if def.origin.is_none() { + quote! { + compile_error!(concat!( + "`", + stringify!($pallet_name), + "` does not have #[pallet::origin] defined, perhaps you should \ + remove `Origin` from construct_runtime?", + )); + } + } else { + TokenStream::new() + }; + + quote! { + #[doc(hidden)] + pub mod __substrate_origin_check { + #[macro_export] + #[doc(hidden)] + macro_rules! #macro_ident { + ($pallet_name:ident) => { + #maybe_compile_error + } + } + + #[doc(hidden)] + pub use #macro_ident as is_origin_part_defined; + } + } +} diff --git a/support/procedural-fork/src/pallet/expand/pallet_struct.rs b/support/procedural-fork/src/pallet/expand/pallet_struct.rs new file mode 100644 index 000000000..7cdf6bde9 --- /dev/null +++ b/support/procedural-fork/src/pallet/expand/pallet_struct.rs @@ -0,0 +1,290 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::pallet::{expand::merge_where_clauses, Def}; +use frame_support_procedural_tools::get_doc_literals; + +/// +/// * Add derive trait on Pallet +/// * Implement GetStorageVersion on Pallet +/// * Implement OnGenesis on Pallet +/// * Implement `fn error_metadata` on Pallet +/// * declare Module type alias for construct_runtime +/// * replace the first field type of `struct Pallet` with `PhantomData` if it is `_` +/// * implementation of `PalletInfoAccess` information +/// * implementation of `StorageInfoTrait` on Pallet +pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream { + let frame_support = &def.frame_support; + let frame_system = &def.frame_system; + let type_impl_gen = &def.type_impl_generics(def.pallet_struct.attr_span); + let type_use_gen = &def.type_use_generics(def.pallet_struct.attr_span); + let type_decl_gen = &def.type_decl_generics(def.pallet_struct.attr_span); + let pallet_ident = &def.pallet_struct.pallet; + let config_where_clause = &def.config.where_clause; + + let mut storages_where_clauses = vec![&def.config.where_clause]; + storages_where_clauses.extend(def.storages.iter().map(|storage| &storage.where_clause)); + let storages_where_clauses = merge_where_clauses(&storages_where_clauses); + + let pallet_item = { + let pallet_module_items = &mut def.item.content.as_mut().expect("Checked by def").1; + let item = &mut pallet_module_items[def.pallet_struct.index]; + if let syn::Item::Struct(item) = item { + item + } else { + unreachable!("Checked by pallet struct parser") + } + }; + + // If the first field type is `_` then we replace with `PhantomData` + if let Some(field) = pallet_item.fields.iter_mut().next() { + if field.ty == syn::parse_quote!(_) { + field.ty = syn::parse_quote!( + #frame_support::__private::sp_std::marker::PhantomData<(#type_use_gen)> + ); + } + } + + if get_doc_literals(&pallet_item.attrs).is_empty() { + pallet_item.attrs.push(syn::parse_quote!( + #[doc = r" + The `Pallet` struct, the main type that implements traits and standalone + functions within the pallet. + "] + )); + } + + pallet_item.attrs.push(syn::parse_quote!( + #[derive( + #frame_support::CloneNoBound, + #frame_support::EqNoBound, + #frame_support::PartialEqNoBound, + #frame_support::RuntimeDebugNoBound, + )] + )); + + let pallet_error_metadata = if let Some(error_def) = &def.error { + let error_ident = &error_def.error; + quote::quote_spanned!(def.pallet_struct.attr_span => + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #config_where_clause { + #[doc(hidden)] + pub fn error_metadata() -> Option<#frame_support::__private::metadata_ir::PalletErrorMetadataIR> { + Some(#frame_support::__private::metadata_ir::PalletErrorMetadataIR { + ty: #frame_support::__private::scale_info::meta_type::<#error_ident<#type_use_gen>>() + }) + } + } + ) + } else { + quote::quote_spanned!(def.pallet_struct.attr_span => + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #config_where_clause { + #[doc(hidden)] + pub fn error_metadata() -> Option<#frame_support::__private::metadata_ir::PalletErrorMetadataIR> { + None + } + } + ) + }; + + let storage_info_span = + def.pallet_struct.without_storage_info.unwrap_or(def.pallet_struct.attr_span); + + let storage_names = &def.storages.iter().map(|storage| &storage.ident).collect::>(); + let storage_cfg_attrs = + &def.storages.iter().map(|storage| &storage.cfg_attrs).collect::>(); + + // Depending on the flag `without_storage_info` and the storage attribute `unbounded`, we use + // partial or full storage info from storage. + let storage_info_traits = &def + .storages + .iter() + .map(|storage| { + if storage.unbounded || def.pallet_struct.without_storage_info.is_some() { + quote::quote_spanned!(storage_info_span => PartialStorageInfoTrait) + } else { + quote::quote_spanned!(storage_info_span => StorageInfoTrait) + } + }) + .collect::>(); + + let storage_info_methods = &def + .storages + .iter() + .map(|storage| { + if storage.unbounded || def.pallet_struct.without_storage_info.is_some() { + quote::quote_spanned!(storage_info_span => partial_storage_info) + } else { + quote::quote_spanned!(storage_info_span => storage_info) + } + }) + .collect::>(); + + let storage_info = quote::quote_spanned!(storage_info_span => + impl<#type_impl_gen> #frame_support::traits::StorageInfoTrait + for #pallet_ident<#type_use_gen> + #storages_where_clauses + { + fn storage_info() + -> #frame_support::__private::sp_std::vec::Vec<#frame_support::traits::StorageInfo> + { + #[allow(unused_mut)] + let mut res = #frame_support::__private::sp_std::vec![]; + + #( + #(#storage_cfg_attrs)* + { + let mut storage_info = < + #storage_names<#type_use_gen> + as #frame_support::traits::#storage_info_traits + >::#storage_info_methods(); + res.append(&mut storage_info); + } + )* + + res + } + } + ); + + let (storage_version, in_code_storage_version_ty) = + if let Some(v) = def.pallet_struct.storage_version.as_ref() { + (quote::quote! { #v }, quote::quote! { #frame_support::traits::StorageVersion }) + } else { + ( + quote::quote! { core::default::Default::default() }, + quote::quote! { #frame_support::traits::NoStorageVersionSet }, + ) + }; + + let whitelisted_storage_idents: Vec = def + .storages + .iter() + .filter_map(|s| s.whitelisted.then_some(s.ident.clone())) + .collect(); + + let whitelisted_storage_keys_impl = quote::quote![ + use #frame_support::traits::{StorageInfoTrait, TrackedStorageKey, WhitelistedStorageKeys}; + impl<#type_impl_gen> WhitelistedStorageKeys for #pallet_ident<#type_use_gen> #storages_where_clauses { + fn whitelisted_storage_keys() -> #frame_support::__private::sp_std::vec::Vec { + use #frame_support::__private::sp_std::vec; + vec![#( + TrackedStorageKey::new(#whitelisted_storage_idents::<#type_use_gen>::hashed_key().to_vec()) + ),*] + } + } + ]; + + quote::quote_spanned!(def.pallet_struct.attr_span => + #pallet_error_metadata + + /// Type alias to `Pallet`, to be used by `construct_runtime`. + /// + /// Generated by `pallet` attribute macro. + #[deprecated(note = "use `Pallet` instead")] + #[allow(dead_code)] + pub type Module<#type_decl_gen> = #pallet_ident<#type_use_gen>; + + // Implement `GetStorageVersion` for `Pallet` + impl<#type_impl_gen> #frame_support::traits::GetStorageVersion + for #pallet_ident<#type_use_gen> + #config_where_clause + { + type InCodeStorageVersion = #in_code_storage_version_ty; + + fn in_code_storage_version() -> Self::InCodeStorageVersion { + #storage_version + } + + fn on_chain_storage_version() -> #frame_support::traits::StorageVersion { + #frame_support::traits::StorageVersion::get::() + } + } + + // Implement `OnGenesis` for `Pallet` + impl<#type_impl_gen> #frame_support::traits::OnGenesis + for #pallet_ident<#type_use_gen> + #config_where_clause + { + fn on_genesis() { + let storage_version: #frame_support::traits::StorageVersion = #storage_version; + storage_version.put::(); + } + } + + // Implement `PalletInfoAccess` for `Pallet` + impl<#type_impl_gen> #frame_support::traits::PalletInfoAccess + for #pallet_ident<#type_use_gen> + #config_where_clause + { + fn index() -> usize { + < + ::PalletInfo as #frame_support::traits::PalletInfo + >::index::() + .expect("Pallet is part of the runtime because pallet `Config` trait is \ + implemented by the runtime") + } + + fn name() -> &'static str { + < + ::PalletInfo as #frame_support::traits::PalletInfo + >::name::() + .expect("Pallet is part of the runtime because pallet `Config` trait is \ + implemented by the runtime") + } + + fn name_hash() -> [u8; 16] { + < + ::PalletInfo as #frame_support::traits::PalletInfo + >::name_hash::() + .expect("Pallet is part of the runtime because pallet `Config` trait is \ + implemented by the runtime") + } + + fn module_name() -> &'static str { + < + ::PalletInfo as #frame_support::traits::PalletInfo + >::module_name::() + .expect("Pallet is part of the runtime because pallet `Config` trait is \ + implemented by the runtime") + } + + fn crate_version() -> #frame_support::traits::CrateVersion { + #frame_support::crate_to_crate_version!() + } + } + + impl<#type_impl_gen> #frame_support::traits::PalletsInfoAccess + for #pallet_ident<#type_use_gen> + #config_where_clause + { + fn count() -> usize { 1 } + fn infos() -> #frame_support::__private::sp_std::vec::Vec<#frame_support::traits::PalletInfoData> { + use #frame_support::traits::PalletInfoAccess; + let item = #frame_support::traits::PalletInfoData { + index: Self::index(), + name: Self::name(), + module_name: Self::module_name(), + crate_version: Self::crate_version(), + }; + #frame_support::__private::sp_std::vec![item] + } + } + + #storage_info + #whitelisted_storage_keys_impl + ) +} diff --git a/support/procedural-fork/src/pallet/expand/storage.rs b/support/procedural-fork/src/pallet/expand/storage.rs new file mode 100644 index 000000000..937b068cf --- /dev/null +++ b/support/procedural-fork/src/pallet/expand/storage.rs @@ -0,0 +1,919 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{ + counter_prefix, + pallet::{ + parse::{ + helper::two128_str, + storage::{Metadata, QueryKind, StorageDef, StorageGenerics}, + }, + Def, + }, +}; +use quote::ToTokens; +use std::{collections::HashMap, ops::IndexMut}; +use syn::spanned::Spanned; + +/// Generate the prefix_ident related to the storage. +/// prefix_ident is used for the prefix struct to be given to storage as first generic param. +fn prefix_ident(storage: &StorageDef) -> syn::Ident { + let storage_ident = &storage.ident; + syn::Ident::new(&format!("_GeneratedPrefixForStorage{}", storage_ident), storage_ident.span()) +} + +/// Generate the counter_prefix_ident related to the storage. +/// counter_prefix_ident is used for the prefix struct to be given to counted storage map. +fn counter_prefix_ident(storage_ident: &syn::Ident) -> syn::Ident { + syn::Ident::new( + &format!("_GeneratedCounterPrefixForStorage{}", storage_ident), + storage_ident.span(), + ) +} + +/// Check for duplicated storage prefixes. This step is necessary since users can specify an +/// alternative storage prefix using the #[pallet::storage_prefix] syntax, and we need to ensure +/// that the prefix specified by the user is not a duplicate of an existing one. +fn check_prefix_duplicates( + storage_def: &StorageDef, + // A hashmap of all already used prefix and their associated error if duplication + used_prefixes: &mut HashMap, +) -> syn::Result<()> { + let prefix = storage_def.prefix(); + let dup_err = syn::Error::new( + storage_def.prefix_span(), + format!("Duplicate storage prefixes found for `{}`", prefix), + ); + + if let Some(other_dup_err) = used_prefixes.insert(prefix.clone(), dup_err.clone()) { + let mut err = dup_err; + err.combine(other_dup_err); + return Err(err) + } + + if let Metadata::CountedMap { .. } = storage_def.metadata { + let counter_prefix = counter_prefix(&prefix); + let counter_dup_err = syn::Error::new( + storage_def.prefix_span(), + format!( + "Duplicate storage prefixes found for `{}`, used for counter associated to \ + counted storage map", + counter_prefix, + ), + ); + + if let Some(other_dup_err) = used_prefixes.insert(counter_prefix, counter_dup_err.clone()) { + let mut err = counter_dup_err; + err.combine(other_dup_err); + return Err(err) + } + } + + Ok(()) +} + +pub struct ResultOnEmptyStructMetadata { + /// The Rust ident that is going to be used as the name of the OnEmpty struct. + pub name: syn::Ident, + /// The path to the error type being returned by the ResultQuery. + pub error_path: syn::Path, + /// The visibility of the OnEmpty struct. + pub visibility: syn::Visibility, + /// The type of the storage item. + pub value_ty: syn::Type, + /// The name of the pallet error enum variant that is going to be returned. + pub variant_name: syn::Ident, + /// The span used to report compilation errors about the OnEmpty struct. + pub span: proc_macro2::Span, +} + +/// +/// * if generics are unnamed: replace the first generic `_` by the generated prefix structure +/// * if generics are named: reorder the generic, remove their name, and add the missing ones. +/// * Add `#[allow(type_alias_bounds)]` +pub fn process_generics(def: &mut Def) -> syn::Result> { + let frame_support = &def.frame_support; + let mut on_empty_struct_metadata = Vec::new(); + + for storage_def in def.storages.iter_mut() { + let item = &mut def.item.content.as_mut().expect("Checked by def").1[storage_def.index]; + + let typ_item = match item { + syn::Item::Type(t) => t, + _ => unreachable!("Checked by def"), + }; + + typ_item.attrs.push(syn::parse_quote!(#[allow(type_alias_bounds)])); + + let typ_path = match &mut *typ_item.ty { + syn::Type::Path(p) => p, + _ => unreachable!("Checked by def"), + }; + + let args = match &mut typ_path.path.segments[0].arguments { + syn::PathArguments::AngleBracketed(args) => args, + _ => unreachable!("Checked by def"), + }; + + let prefix_ident = prefix_ident(storage_def); + let type_use_gen = if def.config.has_instance { + quote::quote_spanned!(storage_def.attr_span => T, I) + } else { + quote::quote_spanned!(storage_def.attr_span => T) + }; + + let default_query_kind: syn::Type = + syn::parse_quote!(#frame_support::storage::types::OptionQuery); + let mut default_on_empty = |value_ty: syn::Type| -> syn::Type { + if let Some(QueryKind::ResultQuery(error_path, variant_name)) = + storage_def.query_kind.as_ref() + { + let on_empty_ident = + quote::format_ident!("__Frame_Internal_Get{}Result", storage_def.ident); + on_empty_struct_metadata.push(ResultOnEmptyStructMetadata { + name: on_empty_ident.clone(), + visibility: storage_def.vis.clone(), + value_ty, + error_path: error_path.clone(), + variant_name: variant_name.clone(), + span: storage_def.attr_span, + }); + return syn::parse_quote!(#on_empty_ident) + } + syn::parse_quote!(#frame_support::traits::GetDefault) + }; + let default_max_values: syn::Type = syn::parse_quote!(#frame_support::traits::GetDefault); + + let set_result_query_type_parameter = |query_type: &mut syn::Type| -> syn::Result<()> { + if let Some(QueryKind::ResultQuery(error_path, _)) = storage_def.query_kind.as_ref() { + if let syn::Type::Path(syn::TypePath { path: syn::Path { segments, .. }, .. }) = + query_type + { + if let Some(seg) = segments.last_mut() { + if let syn::PathArguments::AngleBracketed( + syn::AngleBracketedGenericArguments { args, .. }, + ) = &mut seg.arguments + { + args.clear(); + args.push(syn::GenericArgument::Type(syn::parse_quote!(#error_path))); + } + } + } else { + let msg = format!( + "Invalid pallet::storage, unexpected type for query, expected ResultQuery \ + with 1 type parameter, found `{}`", + query_type.to_token_stream().to_string() + ); + return Err(syn::Error::new(query_type.span(), msg)) + } + } + Ok(()) + }; + + if let Some(named_generics) = storage_def.named_generics.clone() { + args.args.clear(); + args.args.push(syn::parse_quote!( #prefix_ident<#type_use_gen> )); + match named_generics { + StorageGenerics::Value { value, query_kind, on_empty } => { + args.args.push(syn::GenericArgument::Type(value.clone())); + let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone()); + set_result_query_type_parameter(&mut query_kind)?; + args.args.push(syn::GenericArgument::Type(query_kind)); + let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value)); + args.args.push(syn::GenericArgument::Type(on_empty)); + }, + StorageGenerics::Map { hasher, key, value, query_kind, on_empty, max_values } | + StorageGenerics::CountedMap { + hasher, + key, + value, + query_kind, + on_empty, + max_values, + } => { + args.args.push(syn::GenericArgument::Type(hasher)); + args.args.push(syn::GenericArgument::Type(key)); + args.args.push(syn::GenericArgument::Type(value.clone())); + let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone()); + set_result_query_type_parameter(&mut query_kind)?; + args.args.push(syn::GenericArgument::Type(query_kind)); + let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value)); + args.args.push(syn::GenericArgument::Type(on_empty)); + let max_values = max_values.unwrap_or_else(|| default_max_values.clone()); + args.args.push(syn::GenericArgument::Type(max_values)); + }, + StorageGenerics::DoubleMap { + hasher1, + key1, + hasher2, + key2, + value, + query_kind, + on_empty, + max_values, + } => { + args.args.push(syn::GenericArgument::Type(hasher1)); + args.args.push(syn::GenericArgument::Type(key1)); + args.args.push(syn::GenericArgument::Type(hasher2)); + args.args.push(syn::GenericArgument::Type(key2)); + args.args.push(syn::GenericArgument::Type(value.clone())); + let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone()); + set_result_query_type_parameter(&mut query_kind)?; + args.args.push(syn::GenericArgument::Type(query_kind)); + let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value)); + args.args.push(syn::GenericArgument::Type(on_empty)); + let max_values = max_values.unwrap_or_else(|| default_max_values.clone()); + args.args.push(syn::GenericArgument::Type(max_values)); + }, + StorageGenerics::NMap { keygen, value, query_kind, on_empty, max_values } | + StorageGenerics::CountedNMap { + keygen, + value, + query_kind, + on_empty, + max_values, + } => { + args.args.push(syn::GenericArgument::Type(keygen)); + args.args.push(syn::GenericArgument::Type(value.clone())); + let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone()); + set_result_query_type_parameter(&mut query_kind)?; + args.args.push(syn::GenericArgument::Type(query_kind)); + let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value)); + args.args.push(syn::GenericArgument::Type(on_empty)); + let max_values = max_values.unwrap_or_else(|| default_max_values.clone()); + args.args.push(syn::GenericArgument::Type(max_values)); + }, + } + } else { + args.args[0] = syn::parse_quote!( #prefix_ident<#type_use_gen> ); + + let (value_idx, query_idx, on_empty_idx) = match storage_def.metadata { + Metadata::Value { .. } => (1, 2, 3), + Metadata::NMap { .. } | Metadata::CountedNMap { .. } => (2, 3, 4), + Metadata::Map { .. } | Metadata::CountedMap { .. } => (3, 4, 5), + Metadata::DoubleMap { .. } => (5, 6, 7), + }; + + if storage_def.use_default_hasher { + let hasher_indices: Vec = match storage_def.metadata { + Metadata::Map { .. } | Metadata::CountedMap { .. } => vec![1], + Metadata::DoubleMap { .. } => vec![1, 3], + _ => vec![], + }; + for hasher_idx in hasher_indices { + args.args[hasher_idx] = syn::GenericArgument::Type( + syn::parse_quote!(#frame_support::Blake2_128Concat), + ); + } + } + + if query_idx < args.args.len() { + if let syn::GenericArgument::Type(query_kind) = args.args.index_mut(query_idx) { + set_result_query_type_parameter(query_kind)?; + } + } else if let Some(QueryKind::ResultQuery(error_path, _)) = + storage_def.query_kind.as_ref() + { + args.args.push(syn::GenericArgument::Type(syn::parse_quote!(#error_path))) + } + + // Here, we only need to check if OnEmpty is *not* specified, and if so, then we have to + // generate a default OnEmpty struct for it. + if on_empty_idx >= args.args.len() && + matches!(storage_def.query_kind.as_ref(), Some(QueryKind::ResultQuery(_, _))) + { + let value_ty = match args.args[value_idx].clone() { + syn::GenericArgument::Type(ty) => ty, + _ => unreachable!(), + }; + let on_empty = default_on_empty(value_ty); + args.args.push(syn::GenericArgument::Type(on_empty)); + } + } + } + + Ok(on_empty_struct_metadata) +} + +fn augment_final_docs(def: &mut Def) { + // expand the docs with a new line showing the storage type (value, map, double map, etc), and + // the key/value type(s). + let mut push_string_literal = |doc_line: &str, storage: &mut StorageDef| { + let item = &mut def.item.content.as_mut().expect("Checked by def").1[storage.index]; + let typ_item = match item { + syn::Item::Type(t) => t, + _ => unreachable!("Checked by def"), + }; + typ_item.attrs.push(syn::parse_quote!(#[doc = ""])); + typ_item.attrs.push(syn::parse_quote!(#[doc = #doc_line])); + }; + def.storages.iter_mut().for_each(|storage| match &storage.metadata { + Metadata::Value { value } => { + let doc_line = format!( + "Storage type is [`StorageValue`] with value type `{}`.", + value.to_token_stream() + ); + push_string_literal(&doc_line, storage); + }, + Metadata::Map { key, value } => { + let doc_line = format!( + "Storage type is [`StorageMap`] with key type `{}` and value type `{}`.", + key.to_token_stream(), + value.to_token_stream() + ); + push_string_literal(&doc_line, storage); + }, + Metadata::DoubleMap { key1, key2, value } => { + let doc_line = format!( + "Storage type is [`StorageDoubleMap`] with key1 type {}, key2 type {} and value type {}.", + key1.to_token_stream(), + key2.to_token_stream(), + value.to_token_stream() + ); + push_string_literal(&doc_line, storage); + }, + Metadata::NMap { keys, value, .. } => { + let doc_line = format!( + "Storage type is [`StorageNMap`] with keys type ({}) and value type {}.", + keys.iter() + .map(|k| k.to_token_stream().to_string()) + .collect::>() + .join(", "), + value.to_token_stream() + ); + push_string_literal(&doc_line, storage); + }, + Metadata::CountedNMap { keys, value, .. } => { + let doc_line = format!( + "Storage type is [`CountedStorageNMap`] with keys type ({}) and value type {}.", + keys.iter() + .map(|k| k.to_token_stream().to_string()) + .collect::>() + .join(", "), + value.to_token_stream() + ); + push_string_literal(&doc_line, storage); + }, + Metadata::CountedMap { key, value } => { + let doc_line = format!( + "Storage type is [`CountedStorageMap`] with key type {} and value type {}.", + key.to_token_stream(), + value.to_token_stream() + ); + push_string_literal(&doc_line, storage); + }, + }); +} + +/// +/// * generate StoragePrefix structs (e.g. for a storage `MyStorage` a struct with the name +/// `_GeneratedPrefixForStorage$NameOfStorage` is generated) and implements StorageInstance trait. +/// * if generics are unnamed: replace the first generic `_` by the generated prefix structure +/// * if generics are named: reorder the generic, remove their name, and add the missing ones. +/// * Add `#[allow(type_alias_bounds)]` on storages type alias +/// * generate metadatas +pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { + let on_empty_struct_metadata = match process_generics(def) { + Ok(idents) => idents, + Err(e) => return e.into_compile_error(), + }; + + augment_final_docs(def); + + // Check for duplicate prefixes + let mut prefix_set = HashMap::new(); + let mut errors = def + .storages + .iter() + .filter_map(|storage_def| check_prefix_duplicates(storage_def, &mut prefix_set).err()); + if let Some(mut final_error) = errors.next() { + errors.for_each(|error| final_error.combine(error)); + return final_error.into_compile_error() + } + + let frame_support = &def.frame_support; + let frame_system = &def.frame_system; + let pallet_ident = &def.pallet_struct.pallet; + + let entries_builder = def.storages.iter().map(|storage| { + let no_docs = vec![]; + let docs = if cfg!(feature = "no-metadata-docs") { &no_docs } else { &storage.docs }; + + let ident = &storage.ident; + let gen = &def.type_use_generics(storage.attr_span); + let full_ident = quote::quote_spanned!(storage.attr_span => #ident<#gen> ); + + let cfg_attrs = &storage.cfg_attrs; + + quote::quote_spanned!(storage.attr_span => + #(#cfg_attrs)* + { + <#full_ident as #frame_support::storage::StorageEntryMetadataBuilder>::build_metadata( + #frame_support::__private::sp_std::vec![ + #( #docs, )* + ], + &mut entries, + ); + } + ) + }); + + let getters = def.storages.iter().map(|storage| { + if let Some(getter) = &storage.getter { + let completed_where_clause = + super::merge_where_clauses(&[&storage.where_clause, &def.config.where_clause]); + + let ident = &storage.ident; + let gen = &def.type_use_generics(storage.attr_span); + let type_impl_gen = &def.type_impl_generics(storage.attr_span); + let type_use_gen = &def.type_use_generics(storage.attr_span); + let full_ident = quote::quote_spanned!(storage.attr_span => #ident<#gen> ); + + let cfg_attrs = &storage.cfg_attrs; + + // If the storage item is public, link it and otherwise just mention it. + // + // We can not just copy the docs from a non-public type as it may links to internal + // types which makes the compiler very unhappy :( + let getter_doc_line = if matches!(storage.vis, syn::Visibility::Public(_)) { + format!("An auto-generated getter for [`{}`].", storage.ident) + } else { + format!("An auto-generated getter for `{}`.", storage.ident) + }; + + match &storage.metadata { + Metadata::Value { value } => { + let query = match storage.query_kind.as_ref().expect("Checked by def") { + QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => + Option<#value> + ), + QueryKind::ResultQuery(error_path, _) => { + quote::quote_spanned!(storage.attr_span => + Result<#value, #error_path> + ) + }, + QueryKind::ValueQuery => quote::quote!(#value), + }; + quote::quote_spanned!(storage.attr_span => + #(#cfg_attrs)* + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { + #[doc = #getter_doc_line] + pub fn #getter() -> #query { + < + #full_ident as #frame_support::storage::StorageValue<#value> + >::get() + } + } + ) + }, + Metadata::Map { key, value } => { + let query = match storage.query_kind.as_ref().expect("Checked by def") { + QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => + Option<#value> + ), + QueryKind::ResultQuery(error_path, _) => { + quote::quote_spanned!(storage.attr_span => + Result<#value, #error_path> + ) + }, + QueryKind::ValueQuery => quote::quote!(#value), + }; + quote::quote_spanned!(storage.attr_span => + #(#cfg_attrs)* + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { + #[doc = #getter_doc_line] + pub fn #getter(k: KArg) -> #query where + KArg: #frame_support::__private::codec::EncodeLike<#key>, + { + < + #full_ident as #frame_support::storage::StorageMap<#key, #value> + >::get(k) + } + } + ) + }, + Metadata::CountedMap { key, value } => { + let query = match storage.query_kind.as_ref().expect("Checked by def") { + QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => + Option<#value> + ), + QueryKind::ResultQuery(error_path, _) => { + quote::quote_spanned!(storage.attr_span => + Result<#value, #error_path> + ) + }, + QueryKind::ValueQuery => quote::quote!(#value), + }; + quote::quote_spanned!(storage.attr_span => + #(#cfg_attrs)* + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { + #[doc = #getter_doc_line] + pub fn #getter(k: KArg) -> #query where + KArg: #frame_support::__private::codec::EncodeLike<#key>, + { + // NOTE: we can't use any trait here because CountedStorageMap + // doesn't implement any. + <#full_ident>::get(k) + } + } + ) + }, + Metadata::DoubleMap { key1, key2, value } => { + let query = match storage.query_kind.as_ref().expect("Checked by def") { + QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => + Option<#value> + ), + QueryKind::ResultQuery(error_path, _) => { + quote::quote_spanned!(storage.attr_span => + Result<#value, #error_path> + ) + }, + QueryKind::ValueQuery => quote::quote!(#value), + }; + quote::quote_spanned!(storage.attr_span => + #(#cfg_attrs)* + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { + #[doc = #getter_doc_line] + pub fn #getter(k1: KArg1, k2: KArg2) -> #query where + KArg1: #frame_support::__private::codec::EncodeLike<#key1>, + KArg2: #frame_support::__private::codec::EncodeLike<#key2>, + { + < + #full_ident as + #frame_support::storage::StorageDoubleMap<#key1, #key2, #value> + >::get(k1, k2) + } + } + ) + }, + Metadata::NMap { keygen, value, .. } => { + let query = match storage.query_kind.as_ref().expect("Checked by def") { + QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => + Option<#value> + ), + QueryKind::ResultQuery(error_path, _) => { + quote::quote_spanned!(storage.attr_span => + Result<#value, #error_path> + ) + }, + QueryKind::ValueQuery => quote::quote!(#value), + }; + quote::quote_spanned!(storage.attr_span => + #(#cfg_attrs)* + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { + #[doc = #getter_doc_line] + pub fn #getter(key: KArg) -> #query + where + KArg: #frame_support::storage::types::EncodeLikeTuple< + <#keygen as #frame_support::storage::types::KeyGenerator>::KArg + > + + #frame_support::storage::types::TupleToEncodedIter, + { + < + #full_ident as + #frame_support::storage::StorageNMap<#keygen, #value> + >::get(key) + } + } + ) + }, + Metadata::CountedNMap { keygen, value, .. } => { + let query = match storage.query_kind.as_ref().expect("Checked by def") { + QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => + Option<#value> + ), + QueryKind::ResultQuery(error_path, _) => { + quote::quote_spanned!(storage.attr_span => + Result<#value, #error_path> + ) + }, + QueryKind::ValueQuery => quote::quote!(#value), + }; + quote::quote_spanned!(storage.attr_span => + #(#cfg_attrs)* + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { + #[doc = #getter_doc_line] + pub fn #getter(key: KArg) -> #query + where + KArg: #frame_support::storage::types::EncodeLikeTuple< + <#keygen as #frame_support::storage::types::KeyGenerator>::KArg + > + + #frame_support::storage::types::TupleToEncodedIter, + { + // NOTE: we can't use any trait here because CountedStorageNMap + // doesn't implement any. + <#full_ident>::get(key) + } + } + ) + }, + } + } else { + Default::default() + } + }); + + let prefix_structs = def.storages.iter().map(|storage_def| { + let type_impl_gen = &def.type_impl_generics(storage_def.attr_span); + let type_use_gen = &def.type_use_generics(storage_def.attr_span); + let prefix_struct_ident = prefix_ident(storage_def); + let prefix_struct_vis = &storage_def.vis; + let prefix_struct_const = storage_def.prefix(); + let config_where_clause = &def.config.where_clause; + + let cfg_attrs = &storage_def.cfg_attrs; + + let maybe_counter = match storage_def.metadata { + Metadata::CountedMap { .. } => { + let counter_prefix_struct_ident = counter_prefix_ident(&storage_def.ident); + let counter_prefix_struct_const = counter_prefix(&prefix_struct_const); + let storage_prefix_hash = two128_str(&counter_prefix_struct_const); + quote::quote_spanned!(storage_def.attr_span => + #(#cfg_attrs)* + #[doc(hidden)] + #prefix_struct_vis struct #counter_prefix_struct_ident<#type_use_gen>( + core::marker::PhantomData<(#type_use_gen,)> + ); + #(#cfg_attrs)* + impl<#type_impl_gen> #frame_support::traits::StorageInstance + for #counter_prefix_struct_ident<#type_use_gen> + #config_where_clause + { + fn pallet_prefix() -> &'static str { + < + ::PalletInfo + as #frame_support::traits::PalletInfo + >::name::>() + .expect("No name found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`.") + } + + fn pallet_prefix_hash() -> [u8; 16] { + < + ::PalletInfo + as #frame_support::traits::PalletInfo + >::name_hash::>() + .expect("No name_hash found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`.") + } + + const STORAGE_PREFIX: &'static str = #counter_prefix_struct_const; + fn storage_prefix_hash() -> [u8; 16] { + #storage_prefix_hash + } + } + #(#cfg_attrs)* + impl<#type_impl_gen> #frame_support::storage::types::CountedStorageMapInstance + for #prefix_struct_ident<#type_use_gen> + #config_where_clause + { + type CounterPrefix = #counter_prefix_struct_ident<#type_use_gen>; + } + ) + }, + Metadata::CountedNMap { .. } => { + let counter_prefix_struct_ident = counter_prefix_ident(&storage_def.ident); + let counter_prefix_struct_const = counter_prefix(&prefix_struct_const); + let storage_prefix_hash = two128_str(&counter_prefix_struct_const); + quote::quote_spanned!(storage_def.attr_span => + #(#cfg_attrs)* + #[doc(hidden)] + #prefix_struct_vis struct #counter_prefix_struct_ident<#type_use_gen>( + core::marker::PhantomData<(#type_use_gen,)> + ); + #(#cfg_attrs)* + impl<#type_impl_gen> #frame_support::traits::StorageInstance + for #counter_prefix_struct_ident<#type_use_gen> + #config_where_clause + { + fn pallet_prefix() -> &'static str { + < + ::PalletInfo + as #frame_support::traits::PalletInfo + >::name::>() + .expect("No name found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`.") + } + fn pallet_prefix_hash() -> [u8; 16] { + < + ::PalletInfo + as #frame_support::traits::PalletInfo + >::name_hash::>() + .expect("No name_hash found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`.") + } + const STORAGE_PREFIX: &'static str = #counter_prefix_struct_const; + fn storage_prefix_hash() -> [u8; 16] { + #storage_prefix_hash + } + } + #(#cfg_attrs)* + impl<#type_impl_gen> #frame_support::storage::types::CountedStorageNMapInstance + for #prefix_struct_ident<#type_use_gen> + #config_where_clause + { + type CounterPrefix = #counter_prefix_struct_ident<#type_use_gen>; + } + ) + }, + _ => proc_macro2::TokenStream::default(), + }; + + let storage_prefix_hash = two128_str(&prefix_struct_const); + quote::quote_spanned!(storage_def.attr_span => + #maybe_counter + + #(#cfg_attrs)* + #[doc(hidden)] + #prefix_struct_vis struct #prefix_struct_ident<#type_use_gen>( + core::marker::PhantomData<(#type_use_gen,)> + ); + #(#cfg_attrs)* + impl<#type_impl_gen> #frame_support::traits::StorageInstance + for #prefix_struct_ident<#type_use_gen> + #config_where_clause + { + fn pallet_prefix() -> &'static str { + < + ::PalletInfo + as #frame_support::traits::PalletInfo + >::name::>() + .expect("No name found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`.") + } + + fn pallet_prefix_hash() -> [u8; 16] { + < + ::PalletInfo + as #frame_support::traits::PalletInfo + >::name_hash::>() + .expect("No name_hash found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`.") + } + + const STORAGE_PREFIX: &'static str = #prefix_struct_const; + fn storage_prefix_hash() -> [u8; 16] { + #storage_prefix_hash + } + } + ) + }); + + let on_empty_structs = on_empty_struct_metadata.into_iter().map(|metadata| { + use crate::pallet::parse::GenericKind; + use syn::{GenericArgument, Path, PathArguments, PathSegment, Type, TypePath}; + + let ResultOnEmptyStructMetadata { + name, + visibility, + value_ty, + error_path, + variant_name, + span, + } = metadata; + + let generic_kind = match error_path.segments.last() { + Some(PathSegment { arguments: PathArguments::AngleBracketed(args), .. }) => { + let (has_config, has_instance) = + args.args.iter().fold((false, false), |(has_config, has_instance), arg| { + match arg { + GenericArgument::Type(Type::Path(TypePath { + path: Path { segments, .. }, + .. + })) => { + let maybe_config = + segments.first().map_or(false, |seg| seg.ident == "T"); + let maybe_instance = + segments.first().map_or(false, |seg| seg.ident == "I"); + + (has_config || maybe_config, has_instance || maybe_instance) + }, + _ => (has_config, has_instance), + } + }); + GenericKind::from_gens(has_config, has_instance).unwrap_or(GenericKind::None) + }, + _ => GenericKind::None, + }; + let type_impl_gen = generic_kind.type_impl_gen(proc_macro2::Span::call_site()); + let config_where_clause = &def.config.where_clause; + + quote::quote_spanned!(span => + #[doc(hidden)] + #[allow(non_camel_case_types)] + #visibility struct #name; + + impl<#type_impl_gen> #frame_support::traits::Get> + for #name + #config_where_clause + { + fn get() -> Result<#value_ty, #error_path> { + Err(<#error_path>::#variant_name) + } + } + ) + }); + + // aggregated where clause of all storage types and the whole pallet. + let mut where_clauses = vec![&def.config.where_clause]; + where_clauses.extend(def.storages.iter().map(|storage| &storage.where_clause)); + let completed_where_clause = super::merge_where_clauses(&where_clauses); + let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site()); + let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site()); + + let try_decode_entire_state = { + let mut storage_names = def + .storages + .iter() + .filter_map(|storage| { + // A little hacky; don't generate for cfg gated storages to not get compile errors + // when building "frame-feature-testing" gated storages in the "frame-support-test" + // crate. + if storage.try_decode && storage.cfg_attrs.is_empty() { + let ident = &storage.ident; + let gen = &def.type_use_generics(storage.attr_span); + Some(quote::quote_spanned!(storage.attr_span => #ident<#gen> )) + } else { + None + } + }) + .collect::>(); + storage_names.sort_by_cached_key(|ident| ident.to_string()); + + quote::quote!( + #[cfg(feature = "try-runtime")] + impl<#type_impl_gen> #frame_support::traits::TryDecodeEntireStorage + for #pallet_ident<#type_use_gen> #completed_where_clause + { + fn try_decode_entire_state() -> Result> { + let pallet_name = <::PalletInfo as frame_support::traits::PalletInfo> + ::name::<#pallet_ident<#type_use_gen>>() + .expect("Every active pallet has a name in the runtime; qed"); + + #frame_support::__private::log::debug!(target: "runtime::try-decode-state", "trying to decode pallet: {pallet_name}"); + + // NOTE: for now, we have to exclude storage items that are feature gated. + let mut errors = #frame_support::__private::sp_std::vec::Vec::new(); + let mut decoded = 0usize; + + #( + #frame_support::__private::log::debug!(target: "runtime::try-decode-state", "trying to decode storage: \ + {pallet_name}::{}", stringify!(#storage_names)); + + match <#storage_names as #frame_support::traits::TryDecodeEntireStorage>::try_decode_entire_state() { + Ok(count) => { + decoded += count; + }, + Err(err) => { + errors.extend(err); + }, + } + )* + + if errors.is_empty() { + Ok(decoded) + } else { + Err(errors) + } + } + } + ) + }; + + quote::quote!( + impl<#type_impl_gen> #pallet_ident<#type_use_gen> + #completed_where_clause + { + #[doc(hidden)] + pub fn storage_metadata() -> #frame_support::__private::metadata_ir::PalletStorageMetadataIR { + #frame_support::__private::metadata_ir::PalletStorageMetadataIR { + prefix: < + ::PalletInfo as + #frame_support::traits::PalletInfo + >::name::<#pallet_ident<#type_use_gen>>() + .expect("No name found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`."), + entries: { + #[allow(unused_mut)] + let mut entries = #frame_support::__private::sp_std::vec![]; + #( #entries_builder )* + entries + }, + } + } + } + + #( #getters )* + #( #prefix_structs )* + #( #on_empty_structs )* + + #try_decode_entire_state + ) +} diff --git a/support/procedural-fork/src/pallet/expand/tasks.rs b/support/procedural-fork/src/pallet/expand/tasks.rs new file mode 100644 index 000000000..6697e5c82 --- /dev/null +++ b/support/procedural-fork/src/pallet/expand/tasks.rs @@ -0,0 +1,267 @@ +//! Contains logic for expanding task-related items. + +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Home of the expansion code for the Tasks API + +use crate::pallet::{parse::tasks::*, Def}; +use derive_syn_parse::Parse; +use inflector::Inflector; +use proc_macro2::TokenStream as TokenStream2; +use quote::{format_ident, quote, ToTokens}; +use syn::{parse_quote, spanned::Spanned, ItemEnum, ItemImpl}; + +impl TaskEnumDef { + /// Since we optionally allow users to manually specify a `#[pallet::task_enum]`, in the + /// event they _don't_ specify one (which is actually the most common behavior) we have to + /// generate one based on the existing [`TasksDef`]. This method performs that generation. + pub fn generate( + tasks: &TasksDef, + type_decl_bounded_generics: TokenStream2, + type_use_generics: TokenStream2, + ) -> Self { + let variants = if tasks.tasks_attr.is_some() { + tasks + .tasks + .iter() + .map(|task| { + let ident = &task.item.sig.ident; + let ident = + format_ident!("{}", ident.to_string().to_class_case(), span = ident.span()); + + let args = task.item.sig.inputs.iter().collect::>(); + + if args.is_empty() { + quote!(#ident) + } else { + quote!(#ident { + #(#args),* + }) + } + }) + .collect::>() + } else { + Vec::new() + }; + let mut task_enum_def: TaskEnumDef = parse_quote! { + /// Auto-generated enum that encapsulates all tasks defined by this pallet. + /// + /// Conceptually similar to the [`Call`] enum, but for tasks. This is only + /// generated if there are tasks present in this pallet. + #[pallet::task_enum] + pub enum Task<#type_decl_bounded_generics> { + #( + #variants, + )* + } + }; + task_enum_def.type_use_generics = type_use_generics; + task_enum_def + } +} + +impl ToTokens for TaskEnumDef { + fn to_tokens(&self, tokens: &mut TokenStream2) { + let item_enum = &self.item_enum; + let ident = &item_enum.ident; + let vis = &item_enum.vis; + let attrs = &item_enum.attrs; + let generics = &item_enum.generics; + let variants = &item_enum.variants; + let scrate = &self.scrate; + let type_use_generics = &self.type_use_generics; + if self.attr.is_some() { + // `item_enum` is short-hand / generated enum + tokens.extend(quote! { + #(#attrs)* + #[derive( + #scrate::CloneNoBound, + #scrate::EqNoBound, + #scrate::PartialEqNoBound, + #scrate::pallet_prelude::Encode, + #scrate::pallet_prelude::Decode, + #scrate::pallet_prelude::TypeInfo, + )] + #[codec(encode_bound())] + #[codec(decode_bound())] + #[scale_info(skip_type_params(#type_use_generics))] + #vis enum #ident #generics { + #variants + #[doc(hidden)] + #[codec(skip)] + __Ignore(core::marker::PhantomData, #scrate::Never), + } + + impl core::fmt::Debug for #ident<#type_use_generics> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct(stringify!(#ident)).field("value", self).finish() + } + } + }); + } else { + // `item_enum` is a manually specified enum (no attribute) + tokens.extend(item_enum.to_token_stream()); + } + } +} + +/// Represents an already-expanded [`TasksDef`]. +#[derive(Parse)] +pub struct ExpandedTasksDef { + pub task_item_impl: ItemImpl, + pub task_trait_impl: ItemImpl, +} + +impl ToTokens for TasksDef { + fn to_tokens(&self, tokens: &mut TokenStream2) { + let scrate = &self.scrate; + let enum_ident = syn::Ident::new("Task", self.enum_ident.span()); + let enum_arguments = &self.enum_arguments; + let enum_use = quote!(#enum_ident #enum_arguments); + + let task_fn_idents = self + .tasks + .iter() + .map(|task| { + format_ident!( + "{}", + &task.item.sig.ident.to_string().to_class_case(), + span = task.item.sig.ident.span() + ) + }) + .collect::>(); + let task_indices = self.tasks.iter().map(|task| &task.index_attr.meta.index); + let task_conditions = self.tasks.iter().map(|task| &task.condition_attr.meta.expr); + let task_weights = self.tasks.iter().map(|task| &task.weight_attr.meta.expr); + let task_iters = self.tasks.iter().map(|task| &task.list_attr.meta.expr); + + let task_fn_impls = self.tasks.iter().map(|task| { + let mut task_fn_impl = task.item.clone(); + task_fn_impl.attrs = vec![]; + task_fn_impl + }); + + let task_fn_names = self.tasks.iter().map(|task| &task.item.sig.ident); + let task_arg_names = self.tasks.iter().map(|task| &task.arg_names).collect::>(); + + let sp_std = quote!(#scrate::__private::sp_std); + let impl_generics = &self.item_impl.generics; + tokens.extend(quote! { + impl #impl_generics #enum_use + { + #(#task_fn_impls)* + } + + impl #impl_generics #scrate::traits::Task for #enum_use + { + type Enumeration = #sp_std::vec::IntoIter<#enum_use>; + + fn iter() -> Self::Enumeration { + let mut all_tasks = #sp_std::vec![]; + #(all_tasks + .extend(#task_iters.map(|(#(#task_arg_names),*)| #enum_ident::#task_fn_idents { #(#task_arg_names: #task_arg_names.clone()),* }) + .collect::<#sp_std::vec::Vec<_>>()); + )* + all_tasks.into_iter() + } + + fn task_index(&self) -> u32 { + match self.clone() { + #(#enum_ident::#task_fn_idents { .. } => #task_indices,)* + Task::__Ignore(_, _) => unreachable!(), + } + } + + fn is_valid(&self) -> bool { + match self.clone() { + #(#enum_ident::#task_fn_idents { #(#task_arg_names),* } => (#task_conditions)(#(#task_arg_names),* ),)* + Task::__Ignore(_, _) => unreachable!(), + } + } + + fn run(&self) -> Result<(), #scrate::pallet_prelude::DispatchError> { + match self.clone() { + #(#enum_ident::#task_fn_idents { #(#task_arg_names),* } => { + <#enum_use>::#task_fn_names(#( #task_arg_names, )* ) + },)* + Task::__Ignore(_, _) => unreachable!(), + } + } + + #[allow(unused_variables)] + fn weight(&self) -> #scrate::pallet_prelude::Weight { + match self.clone() { + #(#enum_ident::#task_fn_idents { #(#task_arg_names),* } => #task_weights,)* + Task::__Ignore(_, _) => unreachable!(), + } + } + } + }); + } +} + +/// Expands the [`TasksDef`] in the enclosing [`Def`], if present, and returns its tokens. +/// +/// This modifies the underlying [`Def`] in addition to returning any tokens that were added. +pub fn expand_tasks_impl(def: &mut Def) -> TokenStream2 { + let Some(tasks) = &mut def.tasks else { return quote!() }; + let ExpandedTasksDef { task_item_impl, task_trait_impl } = parse_quote!(#tasks); + quote! { + #task_item_impl + #task_trait_impl + } +} + +/// Represents a fully-expanded [`TaskEnumDef`]. +#[derive(Parse)] +pub struct ExpandedTaskEnum { + pub item_enum: ItemEnum, + pub debug_impl: ItemImpl, +} + +/// Modifies a [`Def`] to expand the underlying [`TaskEnumDef`] if present, and also returns +/// its tokens. A blank [`TokenStream2`] is returned if no [`TaskEnumDef`] has been generated +/// or defined. +pub fn expand_task_enum(def: &mut Def) -> TokenStream2 { + let Some(task_enum) = &mut def.task_enum else { return quote!() }; + let ExpandedTaskEnum { item_enum, debug_impl } = parse_quote!(#task_enum); + quote! { + #item_enum + #debug_impl + } +} + +/// Modifies a [`Def`] to expand the underlying [`TasksDef`] and also generate a +/// [`TaskEnumDef`] if applicable. The tokens for these items are returned if they are created. +pub fn expand_tasks(def: &mut Def) -> TokenStream2 { + if let Some(tasks_def) = &def.tasks { + if def.task_enum.is_none() { + def.task_enum = Some(TaskEnumDef::generate( + &tasks_def, + def.type_decl_bounded_generics(tasks_def.item_impl.span()), + def.type_use_generics(tasks_def.item_impl.span()), + )); + } + } + let tasks_extra_output = expand_tasks_impl(def); + let task_enum_extra_output = expand_task_enum(def); + quote! { + #tasks_extra_output + #task_enum_extra_output + } +} diff --git a/support/procedural-fork/src/pallet/expand/tt_default_parts.rs b/support/procedural-fork/src/pallet/expand/tt_default_parts.rs new file mode 100644 index 000000000..99364aaa9 --- /dev/null +++ b/support/procedural-fork/src/pallet/expand/tt_default_parts.rs @@ -0,0 +1,216 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{ + pallet::{CompositeKeyword, Def}, + COUNTER, +}; +use syn::spanned::Spanned; + +/// Generate the `tt_default_parts` macro. +pub fn expand_tt_default_parts(def: &mut Def) -> proc_macro2::TokenStream { + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + let default_parts_unique_id = + syn::Ident::new(&format!("__tt_default_parts_{}", count), def.item.span()); + let extra_parts_unique_id = + syn::Ident::new(&format!("__tt_extra_parts_{}", count), def.item.span()); + let default_parts_unique_id_v2 = + syn::Ident::new(&format!("__tt_default_parts_v2_{}", count), def.item.span()); + + let call_part = def.call.as_ref().map(|_| quote::quote!(Call,)); + + let task_part = def.task_enum.as_ref().map(|_| quote::quote!(Task,)); + + let storage_part = (!def.storages.is_empty()).then(|| quote::quote!(Storage,)); + + let event_part = def.event.as_ref().map(|event| { + let gen = event.gen_kind.is_generic().then(|| quote::quote!( )); + quote::quote!( Event #gen , ) + }); + + let error_part = def.error.as_ref().map(|_| quote::quote!(Error,)); + + let origin_part = def.origin.as_ref().map(|origin| { + let gen = origin.is_generic.then(|| quote::quote!( )); + quote::quote!( Origin #gen , ) + }); + + let config_part = def.genesis_config.as_ref().map(|genesis_config| { + let gen = genesis_config.gen_kind.is_generic().then(|| quote::quote!( )); + quote::quote!( Config #gen , ) + }); + + let inherent_part = def.inherent.as_ref().map(|_| quote::quote!(Inherent,)); + + let validate_unsigned_part = + def.validate_unsigned.as_ref().map(|_| quote::quote!(ValidateUnsigned,)); + + let freeze_reason_part = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::FreezeReason(_))) + .then_some(quote::quote!(FreezeReason,)); + + let hold_reason_part = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::HoldReason(_))) + .then_some(quote::quote!(HoldReason,)); + + let lock_id_part = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::LockId(_))) + .then_some(quote::quote!(LockId,)); + + let slash_reason_part = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::SlashReason(_))) + .then_some(quote::quote!(SlashReason,)); + + let call_part_v2 = def.call.as_ref().map(|_| quote::quote!(+ Call)); + + let task_part_v2 = def.task_enum.as_ref().map(|_| quote::quote!(+ Task)); + + let storage_part_v2 = (!def.storages.is_empty()).then(|| quote::quote!(+ Storage)); + + let event_part_v2 = def.event.as_ref().map(|event| { + let gen = event.gen_kind.is_generic().then(|| quote::quote!()); + quote::quote!(+ Event #gen) + }); + + let error_part_v2 = def.error.as_ref().map(|_| quote::quote!(+ Error)); + + let origin_part_v2 = def.origin.as_ref().map(|origin| { + let gen = origin.is_generic.then(|| quote::quote!()); + quote::quote!(+ Origin #gen) + }); + + let config_part_v2 = def.genesis_config.as_ref().map(|genesis_config| { + let gen = genesis_config.gen_kind.is_generic().then(|| quote::quote!()); + quote::quote!(+ Config #gen) + }); + + let inherent_part_v2 = def.inherent.as_ref().map(|_| quote::quote!(+ Inherent)); + + let validate_unsigned_part_v2 = + def.validate_unsigned.as_ref().map(|_| quote::quote!(+ ValidateUnsigned)); + + let freeze_reason_part_v2 = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::FreezeReason(_))) + .then_some(quote::quote!(+ FreezeReason)); + + let hold_reason_part_v2 = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::HoldReason(_))) + .then_some(quote::quote!(+ HoldReason)); + + let lock_id_part_v2 = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::LockId(_))) + .then_some(quote::quote!(+ LockId)); + + let slash_reason_part_v2 = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::SlashReason(_))) + .then_some(quote::quote!(+ SlashReason)); + + quote::quote!( + // This macro follows the conventions as laid out by the `tt-call` crate. It does not + // accept any arguments and simply returns the pallet parts, separated by commas, then + // wrapped inside of braces and finally prepended with double colons, to the caller inside + // of a key named `tokens`. + // + // We need to accept a path argument here, because this macro gets expanded on the + // crate that called the `construct_runtime!` macro, and the actual path is unknown. + #[macro_export] + #[doc(hidden)] + macro_rules! #default_parts_unique_id { + { + $caller:tt + your_tt_return = [{ $my_tt_return:path }] + } => { + $my_tt_return! { + $caller + tokens = [{ + expanded::{ + Pallet, #call_part #storage_part #event_part #error_part #origin_part #config_part + #inherent_part #validate_unsigned_part #freeze_reason_part #task_part + #hold_reason_part #lock_id_part #slash_reason_part + } + }] + } + }; + } + + pub use #default_parts_unique_id as tt_default_parts; + + + // This macro is similar to the `tt_default_parts!`. It expands the pallets that are declared + // explicitly (`System: frame_system::{Pallet, Call}`) with extra parts. + // + // For example, after expansion an explicit pallet would look like: + // `System: expanded::{Error} ::{Pallet, Call}`. + // + // The `expanded` keyword is a marker of the final state of the `construct_runtime!`. + #[macro_export] + #[doc(hidden)] + macro_rules! #extra_parts_unique_id { + { + $caller:tt + your_tt_return = [{ $my_tt_return:path }] + } => { + $my_tt_return! { + $caller + tokens = [{ + expanded::{ + #error_part + } + }] + } + }; + } + + pub use #extra_parts_unique_id as tt_extra_parts; + + #[macro_export] + #[doc(hidden)] + macro_rules! #default_parts_unique_id_v2 { + { + $caller:tt + frame_support = [{ $($frame_support:ident)::* }] + } => { + $($frame_support)*::__private::tt_return! { + $caller + tokens = [{ + + Pallet #call_part_v2 #storage_part_v2 #event_part_v2 #error_part_v2 #origin_part_v2 #config_part_v2 + #inherent_part_v2 #validate_unsigned_part_v2 #freeze_reason_part_v2 #task_part_v2 + #hold_reason_part_v2 #lock_id_part_v2 #slash_reason_part_v2 + }] + } + }; + } + + pub use #default_parts_unique_id_v2 as tt_default_parts_v2; + ) +} diff --git a/support/procedural-fork/src/pallet/expand/type_value.rs b/support/procedural-fork/src/pallet/expand/type_value.rs new file mode 100644 index 000000000..5dc6309c0 --- /dev/null +++ b/support/procedural-fork/src/pallet/expand/type_value.rs @@ -0,0 +1,77 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::pallet::Def; + +/// +/// * Generate the struct +/// * implement the `Get<..>` on it +/// * Rename the name of the function to internal name +pub fn expand_type_values(def: &mut Def) -> proc_macro2::TokenStream { + let mut expand = quote::quote!(); + let frame_support = &def.frame_support; + + for type_value in &def.type_values { + let fn_name_str = &type_value.ident.to_string(); + let fn_name_snakecase = inflector::cases::snakecase::to_snake_case(fn_name_str); + let fn_ident_renamed = syn::Ident::new( + &format!("__type_value_for_{}", fn_name_snakecase), + type_value.ident.span(), + ); + + let type_value_item = { + let item = &mut def.item.content.as_mut().expect("Checked by def").1[type_value.index]; + if let syn::Item::Fn(item) = item { + item + } else { + unreachable!("Checked by error parser") + } + }; + + // Rename the type_value function name + type_value_item.sig.ident = fn_ident_renamed.clone(); + + let vis = &type_value.vis; + let ident = &type_value.ident; + let type_ = &type_value.type_; + let where_clause = &type_value.where_clause; + + let (struct_impl_gen, struct_use_gen) = if type_value.is_generic { + ( + def.type_impl_generics(type_value.attr_span), + def.type_use_generics(type_value.attr_span), + ) + } else { + (Default::default(), Default::default()) + }; + + let docs = &type_value.docs; + + expand.extend(quote::quote_spanned!(type_value.attr_span => + #( #[doc = #docs] )* + #vis struct #ident<#struct_use_gen>(core::marker::PhantomData<((), #struct_use_gen)>); + impl<#struct_impl_gen> #frame_support::traits::Get<#type_> for #ident<#struct_use_gen> + #where_clause + { + fn get() -> #type_ { + #fn_ident_renamed::<#struct_use_gen>() + } + } + )); + } + expand +} diff --git a/support/procedural-fork/src/pallet/expand/validate_unsigned.rs b/support/procedural-fork/src/pallet/expand/validate_unsigned.rs new file mode 100644 index 000000000..876995585 --- /dev/null +++ b/support/procedural-fork/src/pallet/expand/validate_unsigned.rs @@ -0,0 +1,56 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{pallet::Def, COUNTER}; +use proc_macro2::TokenStream; +use quote::quote; +use syn::{spanned::Spanned, Ident}; + +pub fn expand_validate_unsigned(def: &mut Def) -> TokenStream { + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + let macro_ident = + Ident::new(&format!("__is_validate_unsigned_part_defined_{}", count), def.item.span()); + + let maybe_compile_error = if def.validate_unsigned.is_none() { + quote! { + compile_error!(concat!( + "`", + stringify!($pallet_name), + "` does not have #[pallet::validate_unsigned] defined, perhaps you should \ + remove `ValidateUnsigned` from construct_runtime?", + )); + } + } else { + TokenStream::new() + }; + + quote! { + #[doc(hidden)] + pub mod __substrate_validate_unsigned_check { + #[macro_export] + #[doc(hidden)] + macro_rules! #macro_ident { + ($pallet_name:ident) => { + #maybe_compile_error + } + } + + #[doc(hidden)] + pub use #macro_ident as is_validate_unsigned_part_defined; + } + } +} diff --git a/support/procedural-fork/src/pallet/expand/warnings.rs b/support/procedural-fork/src/pallet/expand/warnings.rs new file mode 100644 index 000000000..030e3ddaf --- /dev/null +++ b/support/procedural-fork/src/pallet/expand/warnings.rs @@ -0,0 +1,98 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Generates warnings for undesirable pallet code. + +use crate::pallet::parse::call::{CallVariantDef, CallWeightDef}; +use proc_macro_warning::Warning; +use syn::{ + spanned::Spanned, + visit::{self, Visit}, +}; + +/// Warn if any of the call arguments starts with a underscore and is used in a weight formula. +pub(crate) fn weight_witness_warning( + method: &CallVariantDef, + dev_mode: bool, + warnings: &mut Vec, +) { + if dev_mode { + return + } + let CallWeightDef::Immediate(w) = &method.weight else { return }; + + let partial_warning = Warning::new_deprecated("UncheckedWeightWitness") + .old("not check weight witness data") + .new("ensure that all witness data for weight calculation is checked before usage") + .help_link("https://github.com/paritytech/polkadot-sdk/pull/1818"); + + for (_, arg_ident, _) in method.args.iter() { + if !arg_ident.to_string().starts_with('_') || !contains_ident(w.clone(), &arg_ident) { + continue + } + + let warning = partial_warning + .clone() + .index(warnings.len()) + .span(arg_ident.span()) + .build_or_panic(); + + warnings.push(warning); + } +} + +/// Warn if the weight is a constant and the pallet not in `dev_mode`. +pub(crate) fn weight_constant_warning( + weight: &syn::Expr, + dev_mode: bool, + warnings: &mut Vec, +) { + if dev_mode { + return + } + let syn::Expr::Lit(lit) = weight else { return }; + + let warning = Warning::new_deprecated("ConstantWeight") + .index(warnings.len()) + .old("use hard-coded constant as call weight") + .new("benchmark all calls or put the pallet into `dev` mode") + .help_link("https://github.com/paritytech/substrate/pull/13798") + .span(lit.span()) + .build_or_panic(); + + warnings.push(warning); +} + +/// Returns whether `expr` contains `ident`. +fn contains_ident(mut expr: syn::Expr, ident: &syn::Ident) -> bool { + struct ContainsIdent { + ident: syn::Ident, + found: bool, + } + + impl<'a> Visit<'a> for ContainsIdent { + fn visit_ident(&mut self, i: &syn::Ident) { + if *i == self.ident { + self.found = true; + } + } + } + + let mut visitor = ContainsIdent { ident: ident.clone(), found: false }; + visit::visit_expr(&mut visitor, &mut expr); + visitor.found +} diff --git a/support/procedural-fork/src/pallet/mod.rs b/support/procedural-fork/src/pallet/mod.rs new file mode 100644 index 000000000..42d8272fb --- /dev/null +++ b/support/procedural-fork/src/pallet/mod.rs @@ -0,0 +1,61 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Implementation for pallet attribute macro. +//! +//! General workflow: +//! 1 - parse all pallet attributes: +//! This step remove all attributes `#[pallet::*]` from the ItemMod and build the `Def` struct +//! which holds the ItemMod without `#[pallet::*]` and information given by those attributes +//! 2 - expand from the parsed information +//! This step will modify the ItemMod by adding some derive attributes or phantom data variants +//! to user defined types. And also crate new types and implement block. + +mod expand; +pub(crate) mod parse; + +pub use parse::{composite::keyword::CompositeKeyword, Def}; +use syn::spanned::Spanned; + +mod keyword { + syn::custom_keyword!(dev_mode); +} + +pub fn pallet( + attr: proc_macro::TokenStream, + item: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + let mut dev_mode = false; + if !attr.is_empty() { + if let Ok(_) = syn::parse::(attr.clone()) { + dev_mode = true; + } else { + let msg = "Invalid pallet macro call: unexpected attribute. Macro call must be \ + bare, such as `#[frame_support::pallet]` or `#[pallet]`, or must specify the \ + `dev_mode` attribute, such as `#[frame_support::pallet(dev_mode)]` or \ + #[pallet(dev_mode)]."; + let span = proc_macro2::TokenStream::from(attr).span(); + return syn::Error::new(span, msg).to_compile_error().into() + } + } + + let item = syn::parse_macro_input!(item as syn::ItemMod); + match parse::Def::try_from(item, dev_mode) { + Ok(def) => expand::expand(def).into(), + Err(e) => e.to_compile_error().into(), + } +} diff --git a/support/procedural-fork/src/pallet/parse/call.rs b/support/procedural-fork/src/pallet/parse/call.rs new file mode 100644 index 000000000..4e09b86fd --- /dev/null +++ b/support/procedural-fork/src/pallet/parse/call.rs @@ -0,0 +1,467 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::{helper, InheritedCallWeightAttr}; +use frame_support_procedural_tools::get_doc_literals; +use proc_macro2::Span; +use quote::ToTokens; +use std::collections::HashMap; +use syn::{spanned::Spanned, ExprClosure}; + +/// List of additional token to be used for parsing. +mod keyword { + syn::custom_keyword!(Call); + syn::custom_keyword!(OriginFor); + syn::custom_keyword!(RuntimeOrigin); + syn::custom_keyword!(weight); + syn::custom_keyword!(call_index); + syn::custom_keyword!(compact); + syn::custom_keyword!(T); + syn::custom_keyword!(pallet); + syn::custom_keyword!(feeless_if); +} + +/// Definition of dispatchables typically `impl Pallet { ... }` +pub struct CallDef { + /// The where_clause used. + pub where_clause: Option, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Vec, + /// The index of call item in pallet module. + pub index: usize, + /// Information on methods (used for expansion). + pub methods: Vec, + /// The span of the pallet::call attribute. + pub attr_span: proc_macro2::Span, + /// Docs, specified on the impl Block. + pub docs: Vec, + /// The optional `weight` attribute on the `pallet::call`. + pub inherited_call_weight: Option, +} + +/// The weight of a call. +#[derive(Clone)] +pub enum CallWeightDef { + /// Explicitly set on the call itself with `#[pallet::weight(…)]`. This value is used. + Immediate(syn::Expr), + + /// The default value that should be set for dev-mode pallets. Usually zero. + DevModeDefault, + + /// Inherits whatever value is configured on the pallet level. + /// + /// The concrete value is not known at this point. + Inherited, +} + +/// Definition of dispatchable typically: `#[weight...] fn foo(origin .., param1: ...) -> ..` +#[derive(Clone)] +pub struct CallVariantDef { + /// Function name. + pub name: syn::Ident, + /// Information on args: `(is_compact, name, type)` + pub args: Vec<(bool, syn::Ident, Box)>, + /// Weight for the call. + pub weight: CallWeightDef, + /// Call index of the dispatchable. + pub call_index: u8, + /// Whether an explicit call index was specified. + pub explicit_call_index: bool, + /// Docs, used for metadata. + pub docs: Vec, + /// Attributes annotated at the top of the dispatchable function. + pub attrs: Vec, + /// The `cfg` attributes. + pub cfg_attrs: Vec, + /// The optional `feeless_if` attribute on the `pallet::call`. + pub feeless_check: Option, +} + +/// Attributes for functions in call impl block. +pub enum FunctionAttr { + /// Parse for `#[pallet::call_index(expr)]` + CallIndex(u8), + /// Parse for `#[pallet::weight(expr)]` + Weight(syn::Expr), + /// Parse for `#[pallet::feeless_if(expr)]` + FeelessIf(Span, syn::ExprClosure), +} + +impl syn::parse::Parse for FunctionAttr { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + let lookahead = content.lookahead1(); + if lookahead.peek(keyword::weight) { + content.parse::()?; + let weight_content; + syn::parenthesized!(weight_content in content); + Ok(FunctionAttr::Weight(weight_content.parse::()?)) + } else if lookahead.peek(keyword::call_index) { + content.parse::()?; + let call_index_content; + syn::parenthesized!(call_index_content in content); + let index = call_index_content.parse::()?; + if !index.suffix().is_empty() { + let msg = "Number literal must not have a suffix"; + return Err(syn::Error::new(index.span(), msg)) + } + Ok(FunctionAttr::CallIndex(index.base10_parse()?)) + } else if lookahead.peek(keyword::feeless_if) { + content.parse::()?; + let closure_content; + syn::parenthesized!(closure_content in content); + Ok(FunctionAttr::FeelessIf( + closure_content.span(), + closure_content.parse::().map_err(|e| { + let msg = "Invalid feeless_if attribute: expected a closure"; + let mut err = syn::Error::new(closure_content.span(), msg); + err.combine(e); + err + })?, + )) + } else { + Err(lookahead.error()) + } + } +} + +/// Attribute for arguments in function in call impl block. +/// Parse for `#[pallet::compact]| +pub struct ArgAttrIsCompact; + +impl syn::parse::Parse for ArgAttrIsCompact { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + content.parse::()?; + Ok(ArgAttrIsCompact) + } +} + +/// Check the syntax is `OriginFor`, `&OriginFor` or `T::RuntimeOrigin`. +pub fn check_dispatchable_first_arg_type(ty: &syn::Type, is_ref: bool) -> syn::Result<()> { + pub struct CheckOriginFor(bool); + impl syn::parse::Parse for CheckOriginFor { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let is_ref = input.parse::().is_ok(); + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::]>()?; + + Ok(Self(is_ref)) + } + } + + pub struct CheckRuntimeOrigin; + impl syn::parse::Parse for CheckRuntimeOrigin { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + input.parse::()?; + input.parse::()?; + + Ok(Self) + } + } + + let result_origin_for = syn::parse2::(ty.to_token_stream()); + let result_runtime_origin = syn::parse2::(ty.to_token_stream()); + return match (result_origin_for, result_runtime_origin) { + (Ok(CheckOriginFor(has_ref)), _) if is_ref == has_ref => Ok(()), + (_, Ok(_)) => Ok(()), + (_, _) => { + let msg = if is_ref { + "Invalid type: expected `&OriginFor`" + } else { + "Invalid type: expected `OriginFor` or `T::RuntimeOrigin`" + }; + return Err(syn::Error::new(ty.span(), msg)) + }, + } +} + +impl CallDef { + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + dev_mode: bool, + inherited_call_weight: Option, + ) -> syn::Result { + let item_impl = if let syn::Item::Impl(item) = item { + item + } else { + return Err(syn::Error::new(item.span(), "Invalid pallet::call, expected item impl")) + }; + + let instances = vec![ + helper::check_impl_gen(&item_impl.generics, item_impl.impl_token.span())?, + helper::check_pallet_struct_usage(&item_impl.self_ty)?, + ]; + + if let Some((_, _, for_)) = item_impl.trait_ { + let msg = "Invalid pallet::call, expected no trait ident as in \ + `impl<..> Pallet<..> { .. }`"; + return Err(syn::Error::new(for_.span(), msg)) + } + + let mut methods = vec![]; + let mut indices = HashMap::new(); + let mut last_index: Option = None; + for item in &mut item_impl.items { + if let syn::ImplItem::Fn(method) = item { + if !matches!(method.vis, syn::Visibility::Public(_)) { + let msg = "Invalid pallet::call, dispatchable function must be public: \ + `pub fn`"; + + let span = match method.vis { + syn::Visibility::Inherited => method.sig.span(), + _ => method.vis.span(), + }; + + return Err(syn::Error::new(span, msg)) + } + + match method.sig.inputs.first() { + None => { + let msg = "Invalid pallet::call, must have at least origin arg"; + return Err(syn::Error::new(method.sig.span(), msg)) + }, + Some(syn::FnArg::Receiver(_)) => { + let msg = "Invalid pallet::call, first argument must be a typed argument, \ + e.g. `origin: OriginFor`"; + return Err(syn::Error::new(method.sig.span(), msg)) + }, + Some(syn::FnArg::Typed(arg)) => { + check_dispatchable_first_arg_type(&arg.ty, false)?; + }, + } + + if let syn::ReturnType::Type(_, type_) = &method.sig.output { + helper::check_pallet_call_return_type(type_)?; + } else { + let msg = "Invalid pallet::call, require return type \ + DispatchResultWithPostInfo"; + return Err(syn::Error::new(method.sig.span(), msg)) + } + + let cfg_attrs: Vec = helper::get_item_cfg_attrs(&method.attrs); + let mut call_idx_attrs = vec![]; + let mut weight_attrs = vec![]; + let mut feeless_attrs = vec![]; + for attr in helper::take_item_pallet_attrs(&mut method.attrs)?.into_iter() { + match attr { + FunctionAttr::CallIndex(_) => { + call_idx_attrs.push(attr); + }, + FunctionAttr::Weight(_) => { + weight_attrs.push(attr); + }, + FunctionAttr::FeelessIf(span, _) => { + feeless_attrs.push((span, attr)); + }, + } + } + + if weight_attrs.is_empty() && dev_mode { + // inject a default O(1) weight when dev mode is enabled and no weight has + // been specified on the call + let empty_weight: syn::Expr = syn::parse_quote!(0); + weight_attrs.push(FunctionAttr::Weight(empty_weight)); + } + + let weight = match weight_attrs.len() { + 0 if inherited_call_weight.is_some() => CallWeightDef::Inherited, + 0 if dev_mode => CallWeightDef::DevModeDefault, + 0 => return Err(syn::Error::new( + method.sig.span(), + "A pallet::call requires either a concrete `#[pallet::weight($expr)]` or an + inherited weight from the `#[pallet:call(weight($type))]` attribute, but + none were given.", + )), + 1 => match weight_attrs.pop().unwrap() { + FunctionAttr::Weight(w) => CallWeightDef::Immediate(w), + _ => unreachable!("checked during creation of the let binding"), + }, + _ => { + let msg = "Invalid pallet::call, too many weight attributes given"; + return Err(syn::Error::new(method.sig.span(), msg)) + }, + }; + + if call_idx_attrs.len() > 1 { + let msg = "Invalid pallet::call, too many call_index attributes given"; + return Err(syn::Error::new(method.sig.span(), msg)) + } + let call_index = call_idx_attrs.pop().map(|attr| match attr { + FunctionAttr::CallIndex(idx) => idx, + _ => unreachable!("checked during creation of the let binding"), + }); + let explicit_call_index = call_index.is_some(); + + let final_index = match call_index { + Some(i) => i, + None => + last_index.map_or(Some(0), |idx| idx.checked_add(1)).ok_or_else(|| { + let msg = "Call index doesn't fit into u8, index is 256"; + syn::Error::new(method.sig.span(), msg) + })?, + }; + last_index = Some(final_index); + + if let Some(used_fn) = indices.insert(final_index, method.sig.ident.clone()) { + let msg = format!( + "Call indices are conflicting: Both functions {} and {} are at index {}", + used_fn, method.sig.ident, final_index, + ); + let mut err = syn::Error::new(used_fn.span(), &msg); + err.combine(syn::Error::new(method.sig.ident.span(), msg)); + return Err(err) + } + + let mut args = vec![]; + for arg in method.sig.inputs.iter_mut().skip(1) { + let arg = if let syn::FnArg::Typed(arg) = arg { + arg + } else { + unreachable!("Only first argument can be receiver"); + }; + + let arg_attrs: Vec = + helper::take_item_pallet_attrs(&mut arg.attrs)?; + + if arg_attrs.len() > 1 { + let msg = "Invalid pallet::call, argument has too many attributes"; + return Err(syn::Error::new(arg.span(), msg)) + } + + let arg_ident = if let syn::Pat::Ident(pat) = &*arg.pat { + pat.ident.clone() + } else { + let msg = "Invalid pallet::call, argument must be ident"; + return Err(syn::Error::new(arg.pat.span(), msg)) + }; + + args.push((!arg_attrs.is_empty(), arg_ident, arg.ty.clone())); + } + + let docs = get_doc_literals(&method.attrs); + + if feeless_attrs.len() > 1 { + let msg = "Invalid pallet::call, there can only be one feeless_if attribute"; + return Err(syn::Error::new(feeless_attrs[1].0, msg)) + } + let feeless_check: Option = + feeless_attrs.pop().map(|(_, attr)| match attr { + FunctionAttr::FeelessIf(_, closure) => closure, + _ => unreachable!("checked during creation of the let binding"), + }); + + if let Some(ref feeless_check) = feeless_check { + if feeless_check.inputs.len() != args.len() + 1 { + let msg = "Invalid pallet::call, feeless_if closure must have same \ + number of arguments as the dispatchable function"; + return Err(syn::Error::new(feeless_check.span(), msg)) + } + + match feeless_check.inputs.first() { + None => { + let msg = "Invalid pallet::call, feeless_if closure must have at least origin arg"; + return Err(syn::Error::new(feeless_check.span(), msg)) + }, + Some(syn::Pat::Type(arg)) => { + check_dispatchable_first_arg_type(&arg.ty, true)?; + }, + _ => { + let msg = "Invalid pallet::call, feeless_if closure first argument must be a typed argument, \ + e.g. `origin: OriginFor`"; + return Err(syn::Error::new(feeless_check.span(), msg)) + }, + } + + for (feeless_arg, arg) in feeless_check.inputs.iter().skip(1).zip(args.iter()) { + let feeless_arg_type = + if let syn::Pat::Type(syn::PatType { ty, .. }) = feeless_arg.clone() { + if let syn::Type::Reference(pat) = *ty { + pat.elem.clone() + } else { + let msg = "Invalid pallet::call, feeless_if closure argument must be a reference"; + return Err(syn::Error::new(ty.span(), msg)) + } + } else { + let msg = "Invalid pallet::call, feeless_if closure argument must be a type ascription pattern"; + return Err(syn::Error::new(feeless_arg.span(), msg)) + }; + + if feeless_arg_type != arg.2 { + let msg = + "Invalid pallet::call, feeless_if closure argument must have \ + a reference to the same type as the dispatchable function argument"; + return Err(syn::Error::new(feeless_arg.span(), msg)) + } + } + + let valid_return = match &feeless_check.output { + syn::ReturnType::Type(_, type_) => match *(type_.clone()) { + syn::Type::Path(syn::TypePath { path, .. }) => path.is_ident("bool"), + _ => false, + }, + _ => false, + }; + if !valid_return { + let msg = "Invalid pallet::call, feeless_if closure must return `bool`"; + return Err(syn::Error::new(feeless_check.output.span(), msg)) + } + } + + methods.push(CallVariantDef { + name: method.sig.ident.clone(), + weight, + call_index: final_index, + explicit_call_index, + args, + docs, + attrs: method.attrs.clone(), + cfg_attrs, + feeless_check, + }); + } else { + let msg = "Invalid pallet::call, only method accepted"; + return Err(syn::Error::new(item.span(), msg)) + } + } + + Ok(Self { + index, + attr_span, + instances, + methods, + where_clause: item_impl.generics.where_clause.clone(), + docs: get_doc_literals(&item_impl.attrs), + inherited_call_weight, + }) + } +} diff --git a/support/procedural-fork/src/pallet/parse/composite.rs b/support/procedural-fork/src/pallet/parse/composite.rs new file mode 100644 index 000000000..c3ac74846 --- /dev/null +++ b/support/procedural-fork/src/pallet/parse/composite.rs @@ -0,0 +1,191 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::helper; +use quote::ToTokens; +use syn::spanned::Spanned; + +pub mod keyword { + use super::*; + + syn::custom_keyword!(FreezeReason); + syn::custom_keyword!(HoldReason); + syn::custom_keyword!(LockId); + syn::custom_keyword!(SlashReason); + syn::custom_keyword!(Task); + + pub enum CompositeKeyword { + FreezeReason(FreezeReason), + HoldReason(HoldReason), + LockId(LockId), + SlashReason(SlashReason), + Task(Task), + } + + impl ToTokens for CompositeKeyword { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + use CompositeKeyword::*; + match self { + FreezeReason(inner) => inner.to_tokens(tokens), + HoldReason(inner) => inner.to_tokens(tokens), + LockId(inner) => inner.to_tokens(tokens), + SlashReason(inner) => inner.to_tokens(tokens), + Task(inner) => inner.to_tokens(tokens), + } + } + } + + impl syn::parse::Parse for CompositeKeyword { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let lookahead = input.lookahead1(); + if lookahead.peek(FreezeReason) { + Ok(Self::FreezeReason(input.parse()?)) + } else if lookahead.peek(HoldReason) { + Ok(Self::HoldReason(input.parse()?)) + } else if lookahead.peek(LockId) { + Ok(Self::LockId(input.parse()?)) + } else if lookahead.peek(SlashReason) { + Ok(Self::SlashReason(input.parse()?)) + } else if lookahead.peek(Task) { + Ok(Self::Task(input.parse()?)) + } else { + Err(lookahead.error()) + } + } + } + + impl std::fmt::Display for CompositeKeyword { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use CompositeKeyword::*; + write!( + f, + "{}", + match self { + FreezeReason(_) => "FreezeReason", + HoldReason(_) => "HoldReason", + Task(_) => "Task", + LockId(_) => "LockId", + SlashReason(_) => "SlashReason", + } + ) + } + } +} + +pub struct CompositeDef { + /// The index of the CompositeDef item in the pallet module. + pub index: usize, + /// The composite keyword used (contains span). + pub composite_keyword: keyword::CompositeKeyword, + /// Name of the associated type. + pub ident: syn::Ident, + /// Type parameters and where clause attached to a declaration of the pallet::composite_enum. + pub generics: syn::Generics, + /// The span of the pallet::composite_enum attribute. + pub attr_span: proc_macro2::Span, + /// Variant count of the pallet::composite_enum. + pub variant_count: u32, +} + +impl CompositeDef { + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + scrate: &syn::Path, + item: &mut syn::Item, + ) -> syn::Result { + let item = if let syn::Item::Enum(item) = item { + // check variants: composite enums support only field-less enum variants. This is + // because fields can introduce too many possibilities, making it challenging to compute + // a fixed variant count. + for variant in &item.variants { + match variant.fields { + syn::Fields::Named(_) | syn::Fields::Unnamed(_) => + return Err(syn::Error::new( + variant.ident.span(), + "The composite enum does not support variants with fields!", + )), + syn::Fields::Unit => (), + } + } + item + } else { + return Err(syn::Error::new( + item.span(), + "Invalid pallet::composite_enum, expected enum item", + )) + }; + + if !matches!(item.vis, syn::Visibility::Public(_)) { + let msg = format!("Invalid pallet::composite_enum, `{}` must be public", item.ident); + return Err(syn::Error::new(item.span(), msg)) + } + + let has_instance = if item.generics.params.first().is_some() { + helper::check_config_def_gen(&item.generics, item.ident.span())?; + true + } else { + false + }; + + let has_derive_attr = item.attrs.iter().any(|attr| { + if let syn::Meta::List(syn::MetaList { path, .. }) = &attr.meta { + path.get_ident().map(|ident| ident == "derive").unwrap_or(false) + } else { + false + } + }); + + if !has_derive_attr { + let derive_attr: syn::Attribute = syn::parse_quote! { + #[derive( + Copy, Clone, Eq, PartialEq, + #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, + #scrate::__private::scale_info::TypeInfo, + #scrate::__private::RuntimeDebug, + )] + }; + item.attrs.push(derive_attr); + } + + if has_instance { + item.attrs.push(syn::parse_quote! { + #[scale_info(skip_type_params(I))] + }); + + item.variants.push(syn::parse_quote! { + #[doc(hidden)] + #[codec(skip)] + __Ignore( + ::core::marker::PhantomData, + ) + }); + } + + let composite_keyword = + syn::parse2::(item.ident.to_token_stream())?; + + Ok(CompositeDef { + index, + composite_keyword, + attr_span, + generics: item.generics.clone(), + variant_count: item.variants.len() as u32, + ident: item.ident.clone(), + }) + } +} diff --git a/support/procedural-fork/src/pallet/parse/config.rs b/support/procedural-fork/src/pallet/parse/config.rs new file mode 100644 index 000000000..fbab92db1 --- /dev/null +++ b/support/procedural-fork/src/pallet/parse/config.rs @@ -0,0 +1,590 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::helper; +use frame_support_procedural_tools::{get_doc_literals, is_using_frame_crate}; +use quote::ToTokens; +use syn::{spanned::Spanned, token, Token}; + +/// List of additional token to be used for parsing. +mod keyword { + syn::custom_keyword!(Config); + syn::custom_keyword!(From); + syn::custom_keyword!(T); + syn::custom_keyword!(I); + syn::custom_keyword!(config); + syn::custom_keyword!(pallet); + syn::custom_keyword!(IsType); + syn::custom_keyword!(RuntimeEvent); + syn::custom_keyword!(Event); + syn::custom_keyword!(frame_system); + syn::custom_keyword!(disable_frame_system_supertrait_check); + syn::custom_keyword!(no_default); + syn::custom_keyword!(no_default_bounds); + syn::custom_keyword!(constant); +} + +#[derive(Default)] +pub struct DefaultTrait { + /// A bool for each sub-trait item indicates whether the item has + /// `#[pallet::no_default_bounds]` attached to it. If true, the item will not have any bounds + /// in the generated default sub-trait. + pub items: Vec<(syn::TraitItem, bool)>, + pub has_system: bool, +} + +/// Input definition for the pallet config. +pub struct ConfigDef { + /// The index of item in pallet module. + pub index: usize, + /// Whether the trait has instance (i.e. define with `Config`) + pub has_instance: bool, + /// Const associated type. + pub consts_metadata: Vec, + /// Whether the trait has the associated type `Event`, note that those bounds are + /// checked: + /// * `IsType::RuntimeEvent` + /// * `From` or `From>` or `From>` + pub has_event_type: bool, + /// The where clause on trait definition but modified so `Self` is `T`. + pub where_clause: Option, + /// The span of the pallet::config attribute. + pub attr_span: proc_macro2::Span, + /// Whether a default sub-trait should be generated. + /// + /// Contains default sub-trait items (instantiated by `#[pallet::config(with_default)]`). + /// Vec will be empty if `#[pallet::config(with_default)]` is not specified or if there are + /// no trait items. + pub default_sub_trait: Option, +} + +/// Input definition for a constant in pallet config. +pub struct ConstMetadataDef { + /// Name of the associated type. + pub ident: syn::Ident, + /// The type in Get, e.g. `u32` in `type Foo: Get;`, but `Self` is replaced by `T` + pub type_: syn::Type, + /// The doc associated + pub doc: Vec, +} + +impl TryFrom<&syn::TraitItemType> for ConstMetadataDef { + type Error = syn::Error; + + fn try_from(trait_ty: &syn::TraitItemType) -> Result { + let err = |span, msg| { + syn::Error::new(span, format!("Invalid usage of `#[pallet::constant]`: {}", msg)) + }; + let doc = get_doc_literals(&trait_ty.attrs); + let ident = trait_ty.ident.clone(); + let bound = trait_ty + .bounds + .iter() + .find_map(|b| { + if let syn::TypeParamBound::Trait(tb) = b { + tb.path + .segments + .last() + .and_then(|s| if s.ident == "Get" { Some(s) } else { None }) + } else { + None + } + }) + .ok_or_else(|| err(trait_ty.span(), "`Get` trait bound not found"))?; + let type_arg = if let syn::PathArguments::AngleBracketed(ref ab) = bound.arguments { + if ab.args.len() == 1 { + if let syn::GenericArgument::Type(ref ty) = ab.args[0] { + Ok(ty) + } else { + Err(err(ab.args[0].span(), "Expected a type argument")) + } + } else { + Err(err(bound.span(), "Expected a single type argument")) + } + } else { + Err(err(bound.span(), "Expected trait generic args")) + }?; + let type_ = syn::parse2::(replace_self_by_t(type_arg.to_token_stream())) + .expect("Internal error: replacing `Self` by `T` should result in valid type"); + + Ok(Self { ident, type_, doc }) + } +} + +/// Parse for `#[pallet::disable_frame_system_supertrait_check]` +pub struct DisableFrameSystemSupertraitCheck; + +impl syn::parse::Parse for DisableFrameSystemSupertraitCheck { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + content.parse::()?; + Ok(Self) + } +} + +/// Parsing for the `typ` portion of `PalletAttr` +#[derive(derive_syn_parse::Parse, PartialEq, Eq)] +pub enum PalletAttrType { + #[peek(keyword::no_default, name = "no_default")] + NoDefault(keyword::no_default), + #[peek(keyword::no_default_bounds, name = "no_default_bounds")] + NoBounds(keyword::no_default_bounds), + #[peek(keyword::constant, name = "constant")] + Constant(keyword::constant), +} + +/// Parsing for `#[pallet::X]` +#[derive(derive_syn_parse::Parse)] +pub struct PalletAttr { + _pound: Token![#], + #[bracket] + _bracket: token::Bracket, + #[inside(_bracket)] + _pallet: keyword::pallet, + #[prefix(Token![::] in _bracket)] + #[inside(_bracket)] + typ: PalletAttrType, +} + +/// Parse for `IsType<::RuntimeEvent>` and retrieve `$path` +pub struct IsTypeBoundEventParse(syn::Path); + +impl syn::parse::Parse for IsTypeBoundEventParse { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + let config_path = input.parse::()?; + input.parse::]>()?; + input.parse::()?; + input.parse::()?; + input.parse::]>()?; + + Ok(Self(config_path)) + } +} + +/// Parse for `From` or `From>` or `From>` +pub struct FromEventParse { + is_generic: bool, + has_instance: bool, +} + +impl syn::parse::Parse for FromEventParse { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut is_generic = false; + let mut has_instance = false; + + input.parse::()?; + input.parse::()?; + input.parse::()?; + if input.peek(syn::Token![<]) { + is_generic = true; + input.parse::()?; + input.parse::()?; + if input.peek(syn::Token![,]) { + input.parse::()?; + input.parse::()?; + has_instance = true; + } + input.parse::]>()?; + } + input.parse::]>()?; + + Ok(Self { is_generic, has_instance }) + } +} + +/// Check if trait_item is `type RuntimeEvent`, if so checks its bounds are those expected. +/// (Event type is reserved type) +fn check_event_type( + frame_system: &syn::Path, + trait_item: &syn::TraitItem, + trait_has_instance: bool, +) -> syn::Result { + if let syn::TraitItem::Type(type_) = trait_item { + if type_.ident == "RuntimeEvent" { + // Check event has no generics + if !type_.generics.params.is_empty() || type_.generics.where_clause.is_some() { + let msg = "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must have\ + no generics nor where_clause"; + return Err(syn::Error::new(trait_item.span(), msg)) + } + + // Check bound contains IsType and From + let has_is_type_bound = type_.bounds.iter().any(|s| { + syn::parse2::(s.to_token_stream()) + .map_or(false, |b| has_expected_system_config(b.0, frame_system)) + }); + + if !has_is_type_bound { + let msg = "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must \ + bound: `IsType<::RuntimeEvent>`".to_string(); + return Err(syn::Error::new(type_.span(), msg)) + } + + let from_event_bound = type_ + .bounds + .iter() + .find_map(|s| syn::parse2::(s.to_token_stream()).ok()); + + let from_event_bound = if let Some(b) = from_event_bound { + b + } else { + let msg = "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must \ + bound: `From` or `From>` or `From>`"; + return Err(syn::Error::new(type_.span(), msg)) + }; + + if from_event_bound.is_generic && (from_event_bound.has_instance != trait_has_instance) + { + let msg = "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` bounds inconsistent \ + `From`. Config and generic Event must be both with instance or \ + without instance"; + return Err(syn::Error::new(type_.span(), msg)) + } + + Ok(true) + } else { + Ok(false) + } + } else { + Ok(false) + } +} + +/// Check that the path to `frame_system::Config` is valid, this is that the path is just +/// `frame_system::Config` or when using the `frame` crate it is `frame::xyz::frame_system::Config`. +fn has_expected_system_config(path: syn::Path, frame_system: &syn::Path) -> bool { + // Check if `frame_system` is actually 'frame_system'. + if path.segments.iter().all(|s| s.ident != "frame_system") { + return false + } + + let mut expected_system_config = + match (is_using_frame_crate(&path), is_using_frame_crate(&frame_system)) { + (true, false) => + // We can't use the path to `frame_system` from `frame` if `frame_system` is not being + // in scope through `frame`. + return false, + (false, true) => + // We know that the only valid frame_system path is one that is `frame_system`, as + // `frame` re-exports it as such. + syn::parse2::(quote::quote!(frame_system)).expect("is a valid path; qed"), + (_, _) => + // They are either both `frame_system` or both `frame::xyz::frame_system`. + frame_system.clone(), + }; + + expected_system_config + .segments + .push(syn::PathSegment::from(syn::Ident::new("Config", path.span()))); + + // the parse path might be something like `frame_system::Config<...>`, so we + // only compare the idents along the path. + expected_system_config + .segments + .into_iter() + .map(|ps| ps.ident) + .collect::>() == + path.segments.into_iter().map(|ps| ps.ident).collect::>() +} + +/// Replace ident `Self` by `T` +pub fn replace_self_by_t(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream { + input + .into_iter() + .map(|token_tree| match token_tree { + proc_macro2::TokenTree::Group(group) => + proc_macro2::Group::new(group.delimiter(), replace_self_by_t(group.stream())).into(), + proc_macro2::TokenTree::Ident(ident) if ident == "Self" => + proc_macro2::Ident::new("T", ident.span()).into(), + other => other, + }) + .collect() +} + +impl ConfigDef { + pub fn try_from( + frame_system: &syn::Path, + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + enable_default: bool, + ) -> syn::Result { + let item = if let syn::Item::Trait(item) = item { + item + } else { + let msg = "Invalid pallet::config, expected trait definition"; + return Err(syn::Error::new(item.span(), msg)) + }; + + if !matches!(item.vis, syn::Visibility::Public(_)) { + let msg = "Invalid pallet::config, trait must be public"; + return Err(syn::Error::new(item.span(), msg)) + } + + syn::parse2::(item.ident.to_token_stream())?; + + let where_clause = { + let stream = replace_self_by_t(item.generics.where_clause.to_token_stream()); + syn::parse2::>(stream).expect( + "Internal error: replacing `Self` by `T` should result in valid where + clause", + ) + }; + + if item.generics.params.len() > 1 { + let msg = "Invalid pallet::config, expected no more than one generic"; + return Err(syn::Error::new(item.generics.params[2].span(), msg)) + } + + let has_instance = if item.generics.params.first().is_some() { + helper::check_config_def_gen(&item.generics, item.ident.span())?; + true + } else { + false + }; + + let has_frame_system_supertrait = item.supertraits.iter().any(|s| { + syn::parse2::(s.to_token_stream()) + .map_or(false, |b| has_expected_system_config(b, frame_system)) + }); + + let mut has_event_type = false; + let mut consts_metadata = vec![]; + let mut default_sub_trait = if enable_default { + Some(DefaultTrait { + items: Default::default(), + has_system: has_frame_system_supertrait, + }) + } else { + None + }; + for trait_item in &mut item.items { + let is_event = check_event_type(frame_system, trait_item, has_instance)?; + has_event_type = has_event_type || is_event; + + let mut already_no_default = false; + let mut already_constant = false; + let mut already_no_default_bounds = false; + + while let Ok(Some(pallet_attr)) = + helper::take_first_item_pallet_attr::(trait_item) + { + match (pallet_attr.typ, &trait_item) { + (PalletAttrType::Constant(_), syn::TraitItem::Type(ref typ)) => { + if already_constant { + return Err(syn::Error::new( + pallet_attr._bracket.span.join(), + "Duplicate #[pallet::constant] attribute not allowed.", + )) + } + already_constant = true; + consts_metadata.push(ConstMetadataDef::try_from(typ)?); + }, + (PalletAttrType::Constant(_), _) => + return Err(syn::Error::new( + trait_item.span(), + "Invalid #[pallet::constant] in #[pallet::config], expected type item", + )), + (PalletAttrType::NoDefault(_), _) => { + if !enable_default { + return Err(syn::Error::new( + pallet_attr._bracket.span.join(), + "`#[pallet:no_default]` can only be used if `#[pallet::config(with_default)]` \ + has been specified" + )) + } + if already_no_default { + return Err(syn::Error::new( + pallet_attr._bracket.span.join(), + "Duplicate #[pallet::no_default] attribute not allowed.", + )) + } + + already_no_default = true; + }, + (PalletAttrType::NoBounds(_), _) => { + if !enable_default { + return Err(syn::Error::new( + pallet_attr._bracket.span.join(), + "`#[pallet:no_default_bounds]` can only be used if `#[pallet::config(with_default)]` \ + has been specified" + )) + } + if already_no_default_bounds { + return Err(syn::Error::new( + pallet_attr._bracket.span.join(), + "Duplicate #[pallet::no_default_bounds] attribute not allowed.", + )) + } + already_no_default_bounds = true; + }, + } + } + + if !already_no_default && enable_default { + default_sub_trait + .as_mut() + .expect("is 'Some(_)' if 'enable_default'; qed") + .items + .push((trait_item.clone(), already_no_default_bounds)); + } + } + + let attr: Option = + helper::take_first_item_pallet_attr(&mut item.attrs)?; + let disable_system_supertrait_check = attr.is_some(); + + if !has_frame_system_supertrait && !disable_system_supertrait_check { + let found = if item.supertraits.is_empty() { + "none".to_string() + } else { + let mut found = item + .supertraits + .iter() + .fold(String::new(), |acc, s| format!("{}`{}`, ", acc, quote::quote!(#s))); + found.pop(); + found.pop(); + found + }; + + let msg = format!( + "Invalid pallet::trait, expected explicit `{}::Config` as supertrait, \ + found {}. \ + (try `pub trait Config: frame_system::Config {{ ...` or \ + `pub trait Config: frame_system::Config {{ ...`). \ + To disable this check, use `#[pallet::disable_frame_system_supertrait_check]`", + frame_system.to_token_stream(), + found, + ); + return Err(syn::Error::new(item.span(), msg)) + } + + Ok(Self { + index, + has_instance, + consts_metadata, + has_event_type, + where_clause, + attr_span, + default_sub_trait, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn has_expected_system_config_works() { + let frame_system = syn::parse2::(quote::quote!(frame_system)).unwrap(); + let path = syn::parse2::(quote::quote!(frame_system::Config)).unwrap(); + assert!(has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_works_with_assoc_type() { + let frame_system = syn::parse2::(quote::quote!(frame_system)).unwrap(); + let path = + syn::parse2::(quote::quote!(frame_system::Config)) + .unwrap(); + assert!(has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_works_with_frame() { + let frame_system = + syn::parse2::(quote::quote!(frame::deps::frame_system)).unwrap(); + let path = syn::parse2::(quote::quote!(frame_system::Config)).unwrap(); + assert!(has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_works_with_frame_full_path() { + let frame_system = + syn::parse2::(quote::quote!(frame::deps::frame_system)).unwrap(); + let path = + syn::parse2::(quote::quote!(frame::deps::frame_system::Config)).unwrap(); + assert!(has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_works_with_other_frame_full_path() { + let frame_system = + syn::parse2::(quote::quote!(frame::xyz::frame_system)).unwrap(); + let path = + syn::parse2::(quote::quote!(frame::xyz::frame_system::Config)).unwrap(); + assert!(has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_does_not_works_with_mixed_frame_full_path() { + let frame_system = + syn::parse2::(quote::quote!(frame::xyz::frame_system)).unwrap(); + let path = + syn::parse2::(quote::quote!(frame::deps::frame_system::Config)).unwrap(); + assert!(!has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_does_not_works_with_other_mixed_frame_full_path() { + let frame_system = + syn::parse2::(quote::quote!(frame::deps::frame_system)).unwrap(); + let path = + syn::parse2::(quote::quote!(frame::xyz::frame_system::Config)).unwrap(); + assert!(!has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_does_not_work_with_frame_full_path_if_not_frame_crate() { + let frame_system = syn::parse2::(quote::quote!(frame_system)).unwrap(); + let path = + syn::parse2::(quote::quote!(frame::deps::frame_system::Config)).unwrap(); + assert!(!has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_unexpected_frame_system() { + let frame_system = + syn::parse2::(quote::quote!(framez::deps::frame_system)).unwrap(); + let path = syn::parse2::(quote::quote!(frame_system::Config)).unwrap(); + assert!(!has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_unexpected_path() { + let frame_system = syn::parse2::(quote::quote!(frame_system)).unwrap(); + let path = syn::parse2::(quote::quote!(frame_system::ConfigSystem)).unwrap(); + assert!(!has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_not_frame_system() { + let frame_system = syn::parse2::(quote::quote!(something)).unwrap(); + let path = syn::parse2::(quote::quote!(something::Config)).unwrap(); + assert!(!has_expected_system_config(path, &frame_system)); + } +} diff --git a/support/procedural-fork/src/pallet/parse/error.rs b/support/procedural-fork/src/pallet/parse/error.rs new file mode 100644 index 000000000..362df8d73 --- /dev/null +++ b/support/procedural-fork/src/pallet/parse/error.rs @@ -0,0 +1,115 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::helper; +use frame_support_procedural_tools::get_doc_literals; +use quote::ToTokens; +use syn::{spanned::Spanned, Fields}; + +/// List of additional token to be used for parsing. +mod keyword { + syn::custom_keyword!(Error); +} + +/// Records information about the error enum variant field. +pub struct VariantField { + /// Whether or not the field is named, i.e. whether it is a tuple variant or struct variant. + pub is_named: bool, +} + +/// Records information about the error enum variants. +pub struct VariantDef { + /// The variant ident. + pub ident: syn::Ident, + /// The variant field, if any. + pub field: Option, + /// The variant doc literals. + pub docs: Vec, + /// The `cfg` attributes. + pub cfg_attrs: Vec, +} + +/// This checks error declaration as a enum declaration with only variants without fields nor +/// discriminant. +pub struct ErrorDef { + /// The index of error item in pallet module. + pub index: usize, + /// Variant definitions. + pub variants: Vec, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Vec, + /// The keyword error used (contains span). + pub error: keyword::Error, + /// The span of the pallet::error attribute. + pub attr_span: proc_macro2::Span, +} + +impl ErrorDef { + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + ) -> syn::Result { + let item = if let syn::Item::Enum(item) = item { + item + } else { + return Err(syn::Error::new(item.span(), "Invalid pallet::error, expected item enum")) + }; + if !matches!(item.vis, syn::Visibility::Public(_)) { + let msg = "Invalid pallet::error, `Error` must be public"; + return Err(syn::Error::new(item.span(), msg)) + } + + let instances = + vec![helper::check_type_def_gen_no_bounds(&item.generics, item.ident.span())?]; + + if item.generics.where_clause.is_some() { + let msg = "Invalid pallet::error, where clause is not allowed on pallet error item"; + return Err(syn::Error::new(item.generics.where_clause.as_ref().unwrap().span(), msg)) + } + + let error = syn::parse2::(item.ident.to_token_stream())?; + + let variants = item + .variants + .iter() + .map(|variant| { + let field_ty = match &variant.fields { + Fields::Unit => None, + Fields::Named(_) => Some(VariantField { is_named: true }), + Fields::Unnamed(_) => Some(VariantField { is_named: false }), + }; + if variant.discriminant.is_some() { + let msg = "Invalid pallet::error, unexpected discriminant, discriminants \ + are not supported"; + let span = variant.discriminant.as_ref().unwrap().0.span(); + return Err(syn::Error::new(span, msg)) + } + let cfg_attrs: Vec = helper::get_item_cfg_attrs(&variant.attrs); + + Ok(VariantDef { + ident: variant.ident.clone(), + field: field_ty, + docs: get_doc_literals(&variant.attrs), + cfg_attrs, + }) + }) + .collect::>()?; + + Ok(ErrorDef { attr_span, index, variants, instances, error }) + } +} diff --git a/support/procedural-fork/src/pallet/parse/event.rs b/support/procedural-fork/src/pallet/parse/event.rs new file mode 100644 index 000000000..0fb8ee4f5 --- /dev/null +++ b/support/procedural-fork/src/pallet/parse/event.rs @@ -0,0 +1,141 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::helper; +use quote::ToTokens; +use syn::spanned::Spanned; + +/// List of additional token to be used for parsing. +mod keyword { + syn::custom_keyword!(Event); + syn::custom_keyword!(pallet); + syn::custom_keyword!(generate_deposit); + syn::custom_keyword!(deposit_event); +} + +/// Definition for pallet event enum. +pub struct EventDef { + /// The index of event item in pallet module. + pub index: usize, + /// The keyword Event used (contains span). + pub event: keyword::Event, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Vec, + /// The kind of generic the type `Event` has. + pub gen_kind: super::GenericKind, + /// Whether the function `deposit_event` must be generated. + pub deposit_event: Option, + /// Where clause used in event definition. + pub where_clause: Option, + /// The span of the pallet::event attribute. + pub attr_span: proc_macro2::Span, +} + +/// Attribute for a pallet's Event. +/// +/// Syntax is: +/// * `#[pallet::generate_deposit($vis fn deposit_event)]` +pub struct PalletEventDepositAttr { + pub fn_vis: syn::Visibility, + // Span for the keyword deposit_event + pub fn_span: proc_macro2::Span, + // Span of the attribute + pub span: proc_macro2::Span, +} + +impl syn::parse::Parse for PalletEventDepositAttr { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + let span = content.parse::()?.span(); + let generate_content; + syn::parenthesized!(generate_content in content); + let fn_vis = generate_content.parse::()?; + generate_content.parse::()?; + let fn_span = generate_content.parse::()?.span(); + + Ok(PalletEventDepositAttr { fn_vis, span, fn_span }) + } +} + +struct PalletEventAttrInfo { + deposit_event: Option, +} + +impl PalletEventAttrInfo { + fn from_attrs(attrs: Vec) -> syn::Result { + let mut deposit_event = None; + for attr in attrs { + if deposit_event.is_none() { + deposit_event = Some(attr) + } else { + return Err(syn::Error::new(attr.span, "Duplicate attribute")) + } + } + + Ok(PalletEventAttrInfo { deposit_event }) + } +} + +impl EventDef { + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + ) -> syn::Result { + let item = if let syn::Item::Enum(item) = item { + item + } else { + return Err(syn::Error::new(item.span(), "Invalid pallet::event, expected enum item")) + }; + + let event_attrs: Vec = + helper::take_item_pallet_attrs(&mut item.attrs)?; + let attr_info = PalletEventAttrInfo::from_attrs(event_attrs)?; + let deposit_event = attr_info.deposit_event; + + if !matches!(item.vis, syn::Visibility::Public(_)) { + let msg = "Invalid pallet::event, `Event` must be public"; + return Err(syn::Error::new(item.span(), msg)) + } + + let where_clause = item.generics.where_clause.clone(); + + let mut instances = vec![]; + // NOTE: Event is not allowed to be only generic on I because it is not supported + // by construct_runtime. + if let Some(u) = helper::check_type_def_optional_gen(&item.generics, item.ident.span())? { + instances.push(u); + } else { + // construct_runtime only allow non generic event for non instantiable pallet. + instances.push(helper::InstanceUsage { has_instance: false, span: item.ident.span() }) + } + + let has_instance = item.generics.type_params().any(|t| t.ident == "I"); + let has_config = item.generics.type_params().any(|t| t.ident == "T"); + let gen_kind = super::GenericKind::from_gens(has_config, has_instance) + .expect("Checked by `helper::check_type_def_optional_gen` above"); + + let event = syn::parse2::(item.ident.to_token_stream())?; + + Ok(EventDef { attr_span, index, instances, deposit_event, event, gen_kind, where_clause }) + } +} diff --git a/support/procedural-fork/src/pallet/parse/extra_constants.rs b/support/procedural-fork/src/pallet/parse/extra_constants.rs new file mode 100644 index 000000000..2ba6c44b7 --- /dev/null +++ b/support/procedural-fork/src/pallet/parse/extra_constants.rs @@ -0,0 +1,160 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::helper; +use frame_support_procedural_tools::get_doc_literals; +use syn::spanned::Spanned; + +/// List of additional token to be used for parsing. +mod keyword { + syn::custom_keyword!(DispatchResultWithPostInfo); + syn::custom_keyword!(Call); + syn::custom_keyword!(OriginFor); + syn::custom_keyword!(weight); + syn::custom_keyword!(compact); + syn::custom_keyword!(T); + syn::custom_keyword!(pallet); + syn::custom_keyword!(constant_name); +} + +/// Definition of extra constants typically `impl Pallet { ... }` +pub struct ExtraConstantsDef { + /// The where_clause used. + pub where_clause: Option, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Vec, + /// The index of call item in pallet module. + pub index: usize, + /// The extra constant defined. + pub extra_constants: Vec, +} + +/// Input definition for an constant in pallet. +pub struct ExtraConstantDef { + /// Name of the function + pub ident: syn::Ident, + /// The type returned by the function + pub type_: syn::Type, + /// The doc associated + pub doc: Vec, + /// Optional MetaData Name + pub metadata_name: Option, +} + +/// Attributes for functions in extra_constants impl block. +/// Parse for `#[pallet::constant_name(ConstantName)]` +pub struct ExtraConstAttr { + metadata_name: syn::Ident, +} + +impl syn::parse::Parse for ExtraConstAttr { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + content.parse::()?; + + let metadata_name; + syn::parenthesized!(metadata_name in content); + Ok(ExtraConstAttr { metadata_name: metadata_name.parse::()? }) + } +} + +impl ExtraConstantsDef { + pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { + let item = if let syn::Item::Impl(item) = item { + item + } else { + return Err(syn::Error::new( + item.span(), + "Invalid pallet::extra_constants, expected item impl", + )) + }; + + let instances = vec![ + helper::check_impl_gen(&item.generics, item.impl_token.span())?, + helper::check_pallet_struct_usage(&item.self_ty)?, + ]; + + if let Some((_, _, for_)) = item.trait_ { + let msg = "Invalid pallet::call, expected no trait ident as in \ + `impl<..> Pallet<..> { .. }`"; + return Err(syn::Error::new(for_.span(), msg)) + } + + let mut extra_constants = vec![]; + for impl_item in &mut item.items { + let method = if let syn::ImplItem::Fn(method) = impl_item { + method + } else { + let msg = "Invalid pallet::call, only method accepted"; + return Err(syn::Error::new(impl_item.span(), msg)) + }; + + if !method.sig.inputs.is_empty() { + let msg = "Invalid pallet::extra_constants, method must have 0 args"; + return Err(syn::Error::new(method.sig.span(), msg)) + } + + if !method.sig.generics.params.is_empty() { + let msg = "Invalid pallet::extra_constants, method must have 0 generics"; + return Err(syn::Error::new(method.sig.generics.params[0].span(), msg)) + } + + if method.sig.generics.where_clause.is_some() { + let msg = "Invalid pallet::extra_constants, method must have no where clause"; + return Err(syn::Error::new(method.sig.generics.where_clause.span(), msg)) + } + + let type_ = match &method.sig.output { + syn::ReturnType::Default => { + let msg = "Invalid pallet::extra_constants, method must have a return type"; + return Err(syn::Error::new(method.span(), msg)) + }, + syn::ReturnType::Type(_, type_) => *type_.clone(), + }; + + // parse metadata_name + let mut extra_constant_attrs: Vec = + helper::take_item_pallet_attrs(method)?; + + if extra_constant_attrs.len() > 1 { + let msg = + "Invalid attribute in pallet::constant_name, only one attribute is expected"; + return Err(syn::Error::new(extra_constant_attrs[1].metadata_name.span(), msg)) + } + + let metadata_name = extra_constant_attrs.pop().map(|attr| attr.metadata_name); + + extra_constants.push(ExtraConstantDef { + ident: method.sig.ident.clone(), + type_, + doc: get_doc_literals(&method.attrs), + metadata_name, + }); + } + + Ok(Self { + index, + instances, + where_clause: item.generics.where_clause.clone(), + extra_constants, + }) + } +} diff --git a/support/procedural-fork/src/pallet/parse/genesis_build.rs b/support/procedural-fork/src/pallet/parse/genesis_build.rs new file mode 100644 index 000000000..d0e1d9ec9 --- /dev/null +++ b/support/procedural-fork/src/pallet/parse/genesis_build.rs @@ -0,0 +1,61 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::helper; +use syn::spanned::Spanned; + +/// Definition for pallet genesis build implementation. +pub struct GenesisBuildDef { + /// The index of item in pallet module. + pub index: usize, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Option>, + /// The where_clause used. + pub where_clause: Option, + /// The span of the pallet::genesis_build attribute. + pub attr_span: proc_macro2::Span, +} + +impl GenesisBuildDef { + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + ) -> syn::Result { + let item = if let syn::Item::Impl(item) = item { + item + } else { + let msg = "Invalid pallet::genesis_build, expected item impl"; + return Err(syn::Error::new(item.span(), msg)) + }; + + let item_trait = &item + .trait_ + .as_ref() + .ok_or_else(|| { + let msg = "Invalid pallet::genesis_build, expected impl<..> GenesisBuild<..> \ + for GenesisConfig<..>"; + syn::Error::new(item.span(), msg) + })? + .1; + + let instances = + helper::check_genesis_builder_usage(item_trait)?.map(|instances| vec![instances]); + + Ok(Self { attr_span, index, instances, where_clause: item.generics.where_clause.clone() }) + } +} diff --git a/support/procedural-fork/src/pallet/parse/genesis_config.rs b/support/procedural-fork/src/pallet/parse/genesis_config.rs new file mode 100644 index 000000000..62da6ba13 --- /dev/null +++ b/support/procedural-fork/src/pallet/parse/genesis_config.rs @@ -0,0 +1,73 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::helper; +use syn::spanned::Spanned; + +/// Definition for pallet genesis config type. +/// +/// Either: +/// * `struct GenesisConfig` +/// * `enum GenesisConfig` +pub struct GenesisConfigDef { + /// The index of item in pallet module. + pub index: usize, + /// The kind of generic the type `GenesisConfig` has. + pub gen_kind: super::GenericKind, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Vec, + /// The ident of genesis_config, can be used for span. + pub genesis_config: syn::Ident, +} + +impl GenesisConfigDef { + pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { + let item_span = item.span(); + let (vis, ident, generics) = match &item { + syn::Item::Enum(item) => (&item.vis, &item.ident, &item.generics), + syn::Item::Struct(item) => (&item.vis, &item.ident, &item.generics), + _ => { + let msg = "Invalid pallet::genesis_config, expected enum or struct"; + return Err(syn::Error::new(item.span(), msg)) + }, + }; + + let mut instances = vec![]; + // NOTE: GenesisConfig is not allowed to be only generic on I because it is not supported + // by construct_runtime. + if let Some(u) = helper::check_type_def_optional_gen(generics, ident.span())? { + instances.push(u); + } + + let has_instance = generics.type_params().any(|t| t.ident == "I"); + let has_config = generics.type_params().any(|t| t.ident == "T"); + let gen_kind = super::GenericKind::from_gens(has_config, has_instance) + .expect("Checked by `helper::check_type_def_optional_gen` above"); + + if !matches!(vis, syn::Visibility::Public(_)) { + let msg = "Invalid pallet::genesis_config, GenesisConfig must be public"; + return Err(syn::Error::new(item_span, msg)) + } + + if ident != "GenesisConfig" { + let msg = "Invalid pallet::genesis_config, ident must `GenesisConfig`"; + return Err(syn::Error::new(ident.span(), msg)) + } + + Ok(GenesisConfigDef { index, genesis_config: ident.clone(), instances, gen_kind }) + } +} diff --git a/support/procedural-fork/src/pallet/parse/helper.rs b/support/procedural-fork/src/pallet/parse/helper.rs new file mode 100644 index 000000000..3187c9139 --- /dev/null +++ b/support/procedural-fork/src/pallet/parse/helper.rs @@ -0,0 +1,632 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use proc_macro2::TokenStream; +use quote::{quote, ToTokens}; +use syn::spanned::Spanned; + +/// List of additional token to be used for parsing. +mod keyword { + syn::custom_keyword!(I); + syn::custom_keyword!(compact); + syn::custom_keyword!(GenesisBuild); + syn::custom_keyword!(BuildGenesisConfig); + syn::custom_keyword!(Config); + syn::custom_keyword!(T); + syn::custom_keyword!(Pallet); + syn::custom_keyword!(origin); + syn::custom_keyword!(DispatchResult); + syn::custom_keyword!(DispatchResultWithPostInfo); +} + +/// A usage of instance, either the trait `Config` has been used with instance or without instance. +/// Used to check for consistency. +#[derive(Clone)] +pub struct InstanceUsage { + pub has_instance: bool, + pub span: proc_macro2::Span, +} + +/// Trait implemented for syn items to get mutable references on their attributes. +/// +/// NOTE: verbatim variants are not supported. +pub trait MutItemAttrs { + fn mut_item_attrs(&mut self) -> Option<&mut Vec>; +} + +/// Take the first pallet attribute (e.g. attribute like `#[pallet..]`) and decode it to `Attr` +pub(crate) fn take_first_item_pallet_attr( + item: &mut impl MutItemAttrs, +) -> syn::Result> +where + Attr: syn::parse::Parse, +{ + let attrs = if let Some(attrs) = item.mut_item_attrs() { attrs } else { return Ok(None) }; + + if let Some(index) = attrs.iter().position(|attr| { + attr.path().segments.first().map_or(false, |segment| segment.ident == "pallet") + }) { + let pallet_attr = attrs.remove(index); + Ok(Some(syn::parse2(pallet_attr.into_token_stream())?)) + } else { + Ok(None) + } +} + +/// Take all the pallet attributes (e.g. attribute like `#[pallet..]`) and decode them to `Attr` +pub(crate) fn take_item_pallet_attrs(item: &mut impl MutItemAttrs) -> syn::Result> +where + Attr: syn::parse::Parse, +{ + let mut pallet_attrs = Vec::new(); + + while let Some(attr) = take_first_item_pallet_attr(item)? { + pallet_attrs.push(attr) + } + + Ok(pallet_attrs) +} + +/// Get all the cfg attributes (e.g. attribute like `#[cfg..]`) and decode them to `Attr` +pub fn get_item_cfg_attrs(attrs: &[syn::Attribute]) -> Vec { + attrs + .iter() + .filter_map(|attr| { + if attr.path().segments.first().map_or(false, |segment| segment.ident == "cfg") { + Some(attr.clone()) + } else { + None + } + }) + .collect::>() +} + +impl MutItemAttrs for syn::Item { + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + match self { + Self::Const(item) => Some(item.attrs.as_mut()), + Self::Enum(item) => Some(item.attrs.as_mut()), + Self::ExternCrate(item) => Some(item.attrs.as_mut()), + Self::Fn(item) => Some(item.attrs.as_mut()), + Self::ForeignMod(item) => Some(item.attrs.as_mut()), + Self::Impl(item) => Some(item.attrs.as_mut()), + Self::Macro(item) => Some(item.attrs.as_mut()), + Self::Mod(item) => Some(item.attrs.as_mut()), + Self::Static(item) => Some(item.attrs.as_mut()), + Self::Struct(item) => Some(item.attrs.as_mut()), + Self::Trait(item) => Some(item.attrs.as_mut()), + Self::TraitAlias(item) => Some(item.attrs.as_mut()), + Self::Type(item) => Some(item.attrs.as_mut()), + Self::Union(item) => Some(item.attrs.as_mut()), + Self::Use(item) => Some(item.attrs.as_mut()), + _ => None, + } + } +} + +impl MutItemAttrs for syn::TraitItem { + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + match self { + Self::Const(item) => Some(item.attrs.as_mut()), + Self::Fn(item) => Some(item.attrs.as_mut()), + Self::Type(item) => Some(item.attrs.as_mut()), + Self::Macro(item) => Some(item.attrs.as_mut()), + _ => None, + } + } +} + +impl MutItemAttrs for Vec { + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + Some(self) + } +} + +impl MutItemAttrs for syn::ItemMod { + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + Some(&mut self.attrs) + } +} + +impl MutItemAttrs for syn::ImplItemFn { + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + Some(&mut self.attrs) + } +} + +impl MutItemAttrs for syn::ItemType { + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + Some(&mut self.attrs) + } +} + +/// Parse for `()` +struct Unit; +impl syn::parse::Parse for Unit { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let content; + syn::parenthesized!(content in input); + if !content.is_empty() { + let msg = "unexpected tokens, expected nothing inside parenthesis as `()`"; + return Err(syn::Error::new(content.span(), msg)) + } + Ok(Self) + } +} + +/// Parse for `'static` +struct StaticLifetime; +impl syn::parse::Parse for StaticLifetime { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let lifetime = input.parse::()?; + if lifetime.ident != "static" { + let msg = "unexpected tokens, expected `static`"; + return Err(syn::Error::new(lifetime.ident.span(), msg)) + } + Ok(Self) + } +} + +/// Check the syntax: `I: 'static = ()` +/// +/// `span` is used in case generics is empty (empty generics has span == call_site). +/// +/// return the instance if found. +pub fn check_config_def_gen(gen: &syn::Generics, span: proc_macro2::Span) -> syn::Result<()> { + let expected = "expected `I: 'static = ()`"; + pub struct CheckTraitDefGenerics; + impl syn::parse::Parse for CheckTraitDefGenerics { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + + Ok(Self) + } + } + + syn::parse2::(gen.params.to_token_stream()).map_err(|e| { + let msg = format!("Invalid generics: {}", expected); + let mut err = syn::Error::new(span, msg); + err.combine(e); + err + })?; + + Ok(()) +} + +/// Check the syntax: +/// * either `T` +/// * or `T, I = ()` +/// +/// `span` is used in case generics is empty (empty generics has span == call_site). +/// +/// return the instance if found. +pub fn check_type_def_gen_no_bounds( + gen: &syn::Generics, + span: proc_macro2::Span, +) -> syn::Result { + let expected = "expected `T` or `T, I = ()`"; + pub struct Checker(InstanceUsage); + impl syn::parse::Parse for Checker { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut instance_usage = InstanceUsage { has_instance: false, span: input.span() }; + + input.parse::()?; + if input.peek(syn::Token![,]) { + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + } + + Ok(Self(instance_usage)) + } + } + + let i = syn::parse2::(gen.params.to_token_stream()) + .map_err(|e| { + let msg = format!("Invalid type def generics: {}", expected); + let mut err = syn::Error::new(span, msg); + err.combine(e); + err + })? + .0; + + Ok(i) +} + +/// Check the syntax: +/// * either `` (no generics +/// * or `T` +/// * or `T: Config` +/// * or `T, I = ()` +/// * or `T: Config, I: 'static = ()` +/// +/// `span` is used in case generics is empty (empty generics has span == call_site). +/// +/// return some instance usage if there is some generic, or none otherwise. +pub fn check_type_def_optional_gen( + gen: &syn::Generics, + span: proc_macro2::Span, +) -> syn::Result> { + let expected = "expected `` or `T` or `T: Config` or `T, I = ()` or \ + `T: Config, I: 'static = ()`"; + pub struct Checker(Option); + impl syn::parse::Parse for Checker { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + if input.is_empty() { + return Ok(Self(None)) + } + + let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false }; + + input.parse::()?; + + if input.is_empty() { + return Ok(Self(Some(instance_usage))) + } + + let lookahead = input.lookahead1(); + if lookahead.peek(syn::Token![,]) { + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + + Ok(Self(Some(instance_usage))) + } else if lookahead.peek(syn::Token![:]) { + input.parse::()?; + input.parse::()?; + + if input.is_empty() { + return Ok(Self(Some(instance_usage))) + } + + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + input.parse::]>()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + + Ok(Self(Some(instance_usage))) + } else { + Err(lookahead.error()) + } + } + } + + let i = syn::parse2::(gen.params.to_token_stream()) + .map_err(|e| { + let msg = format!("Invalid type def generics: {}", expected); + let mut err = syn::Error::new(span, msg); + err.combine(e); + err + })? + .0 + // Span can be call_site if generic is empty. Thus we replace it. + .map(|mut i| { + i.span = span; + i + }); + + Ok(i) +} + +/// Check the syntax: +/// * either `Pallet` +/// * or `Pallet` +/// +/// return the instance if found. +pub fn check_pallet_struct_usage(type_: &Box) -> syn::Result { + let expected = "expected `Pallet` or `Pallet`"; + pub struct Checker(InstanceUsage); + impl syn::parse::Parse for Checker { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false }; + + input.parse::()?; + input.parse::()?; + input.parse::()?; + if input.peek(syn::Token![,]) { + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + } + input.parse::]>()?; + + Ok(Self(instance_usage)) + } + } + + let i = syn::parse2::(type_.to_token_stream()) + .map_err(|e| { + let msg = format!("Invalid pallet struct: {}", expected); + let mut err = syn::Error::new(type_.span(), msg); + err.combine(e); + err + })? + .0; + + Ok(i) +} + +/// Check the generic is: +/// * either `T: Config` +/// * or `T: Config, I: 'static` +/// +/// `span` is used in case generics is empty (empty generics has span == call_site). +/// +/// return whether it contains instance. +pub fn check_impl_gen(gen: &syn::Generics, span: proc_macro2::Span) -> syn::Result { + let expected = "expected `impl` or `impl, I: 'static>`"; + pub struct Checker(InstanceUsage); + impl syn::parse::Parse for Checker { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false }; + + input.parse::()?; + input.parse::()?; + input.parse::()?; + if input.peek(syn::Token![<]) { + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + input.parse::]>()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + } + + Ok(Self(instance_usage)) + } + } + + let i = syn::parse2::(gen.params.to_token_stream()) + .map_err(|e| { + let mut err = syn::Error::new(span, format!("Invalid generics: {}", expected)); + err.combine(e); + err + })? + .0; + + Ok(i) +} + +/// Check the syntax: +/// * or `T` +/// * or `T: Config` +/// * or `T, I = ()` +/// * or `T: Config, I: 'static = ()` +/// +/// `span` is used in case generics is empty (empty generics has span == call_site). +/// +/// return the instance if found. +pub fn check_type_def_gen( + gen: &syn::Generics, + span: proc_macro2::Span, +) -> syn::Result { + let expected = "expected `T` or `T: Config` or `T, I = ()` or \ + `T: Config, I: 'static = ()`"; + pub struct Checker(InstanceUsage); + impl syn::parse::Parse for Checker { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false }; + + input.parse::()?; + + if input.is_empty() { + return Ok(Self(instance_usage)) + } + + let lookahead = input.lookahead1(); + if lookahead.peek(syn::Token![,]) { + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + + Ok(Self(instance_usage)) + } else if lookahead.peek(syn::Token![:]) { + input.parse::()?; + input.parse::()?; + + if input.is_empty() { + return Ok(Self(instance_usage)) + } + + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + input.parse::]>()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + + Ok(Self(instance_usage)) + } else { + Err(lookahead.error()) + } + } + } + + let mut i = syn::parse2::(gen.params.to_token_stream()) + .map_err(|e| { + let msg = format!("Invalid type def generics: {}", expected); + let mut err = syn::Error::new(span, msg); + err.combine(e); + err + })? + .0; + + // Span can be call_site if generic is empty. Thus we replace it. + i.span = span; + + Ok(i) +} + +/// Check the syntax: +/// * either `GenesisBuild` +/// * or `GenesisBuild` +/// * or `BuildGenesisConfig` +/// +/// return the instance if found for `GenesisBuild` +/// return None for BuildGenesisConfig +pub fn check_genesis_builder_usage(type_: &syn::Path) -> syn::Result> { + let expected = "expected `BuildGenesisConfig` (or the deprecated `GenesisBuild` or `GenesisBuild`)"; + pub struct Checker(Option); + impl syn::parse::Parse for Checker { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false }; + + if input.peek(keyword::GenesisBuild) { + input.parse::()?; + input.parse::()?; + input.parse::()?; + if input.peek(syn::Token![,]) { + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + } + input.parse::]>()?; + return Ok(Self(Some(instance_usage))) + } else { + input.parse::()?; + return Ok(Self(None)) + } + } + } + + let i = syn::parse2::(type_.to_token_stream()) + .map_err(|e| { + let msg = format!("Invalid genesis builder: {}", expected); + let mut err = syn::Error::new(type_.span(), msg); + err.combine(e); + err + })? + .0; + + Ok(i) +} + +/// Check the syntax: +/// * either `` (no generics) +/// * or `T: Config` +/// * or `T: Config, I: 'static` +/// +/// `span` is used in case generics is empty (empty generics has span == call_site). +/// +/// return the instance if found. +pub fn check_type_value_gen( + gen: &syn::Generics, + span: proc_macro2::Span, +) -> syn::Result> { + let expected = "expected `` or `T: Config` or `T: Config, I: 'static`"; + pub struct Checker(Option); + impl syn::parse::Parse for Checker { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + if input.is_empty() { + return Ok(Self(None)) + } + + input.parse::()?; + input.parse::()?; + input.parse::()?; + + let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false }; + + if input.is_empty() { + return Ok(Self(Some(instance_usage))) + } + + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + input.parse::]>()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + + Ok(Self(Some(instance_usage))) + } + } + + let i = syn::parse2::(gen.params.to_token_stream()) + .map_err(|e| { + let msg = format!("Invalid type def generics: {}", expected); + let mut err = syn::Error::new(span, msg); + err.combine(e); + err + })? + .0 + // Span can be call_site if generic is empty. Thus we replace it. + .map(|mut i| { + i.span = span; + i + }); + + Ok(i) +} + +/// Check the keyword `DispatchResultWithPostInfo` or `DispatchResult`. +pub fn check_pallet_call_return_type(type_: &syn::Type) -> syn::Result<()> { + pub struct Checker; + impl syn::parse::Parse for Checker { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let lookahead = input.lookahead1(); + if lookahead.peek(keyword::DispatchResultWithPostInfo) { + input.parse::()?; + Ok(Self) + } else if lookahead.peek(keyword::DispatchResult) { + input.parse::()?; + Ok(Self) + } else { + Err(lookahead.error()) + } + } + } + + syn::parse2::(type_.to_token_stream()).map(|_| ()) +} + +pub(crate) fn two128_str(s: &str) -> TokenStream { + bytes_to_array(sp_crypto_hashing::twox_128(s.as_bytes()).into_iter()) +} + +pub(crate) fn bytes_to_array(bytes: impl IntoIterator) -> TokenStream { + let bytes = bytes.into_iter(); + + quote!( + [ #( #bytes ),* ] + ) + .into() +} diff --git a/support/procedural-fork/src/pallet/parse/hooks.rs b/support/procedural-fork/src/pallet/parse/hooks.rs new file mode 100644 index 000000000..37d7d22f4 --- /dev/null +++ b/support/procedural-fork/src/pallet/parse/hooks.rs @@ -0,0 +1,86 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::helper; +use syn::spanned::Spanned; + +/// Implementation of the pallet hooks. +pub struct HooksDef { + /// The index of item in pallet. + pub index: usize, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Vec, + /// The where_clause used. + pub where_clause: Option, + /// The span of the pallet::hooks attribute. + pub attr_span: proc_macro2::Span, + /// Boolean flag, set to true if the `on_runtime_upgrade` method of hooks was implemented. + pub has_runtime_upgrade: bool, +} + +impl HooksDef { + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + ) -> syn::Result { + let item = if let syn::Item::Impl(item) = item { + item + } else { + let msg = "Invalid pallet::hooks, expected item impl"; + return Err(syn::Error::new(item.span(), msg)) + }; + + let instances = vec![ + helper::check_pallet_struct_usage(&item.self_ty)?, + helper::check_impl_gen(&item.generics, item.impl_token.span())?, + ]; + + let item_trait = &item + .trait_ + .as_ref() + .ok_or_else(|| { + let msg = "Invalid pallet::hooks, expected impl<..> Hooks \ + for Pallet<..>"; + syn::Error::new(item.span(), msg) + })? + .1; + + if item_trait.segments.len() != 1 || item_trait.segments[0].ident != "Hooks" { + let msg = format!( + "Invalid pallet::hooks, expected trait to be `Hooks` found `{}`\ + , you can import from `frame_support::pallet_prelude`", + quote::quote!(#item_trait) + ); + + return Err(syn::Error::new(item_trait.span(), msg)) + } + + let has_runtime_upgrade = item.items.iter().any(|i| match i { + syn::ImplItem::Fn(method) => method.sig.ident == "on_runtime_upgrade", + _ => false, + }); + + Ok(Self { + attr_span, + index, + instances, + has_runtime_upgrade, + where_clause: item.generics.where_clause.clone(), + }) + } +} diff --git a/support/procedural-fork/src/pallet/parse/inherent.rs b/support/procedural-fork/src/pallet/parse/inherent.rs new file mode 100644 index 000000000..d8641691a --- /dev/null +++ b/support/procedural-fork/src/pallet/parse/inherent.rs @@ -0,0 +1,60 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::helper; +use syn::spanned::Spanned; + +/// The definition of the pallet inherent implementation. +pub struct InherentDef { + /// The index of inherent item in pallet module. + pub index: usize, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Vec, +} + +impl InherentDef { + pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { + let item = if let syn::Item::Impl(item) = item { + item + } else { + let msg = "Invalid pallet::inherent, expected item impl"; + return Err(syn::Error::new(item.span(), msg)) + }; + + if item.trait_.is_none() { + let msg = "Invalid pallet::inherent, expected impl<..> ProvideInherent for Pallet<..>"; + return Err(syn::Error::new(item.span(), msg)) + } + + if let Some(last) = item.trait_.as_ref().unwrap().1.segments.last() { + if last.ident != "ProvideInherent" { + let msg = "Invalid pallet::inherent, expected trait ProvideInherent"; + return Err(syn::Error::new(last.span(), msg)) + } + } else { + let msg = "Invalid pallet::inherent, expected impl<..> ProvideInherent for Pallet<..>"; + return Err(syn::Error::new(item.span(), msg)) + } + + let instances = vec![ + helper::check_pallet_struct_usage(&item.self_ty)?, + helper::check_impl_gen(&item.generics, item.impl_token.span())?, + ]; + + Ok(InherentDef { index, instances }) + } +} diff --git a/support/procedural-fork/src/pallet/parse/mod.rs b/support/procedural-fork/src/pallet/parse/mod.rs new file mode 100644 index 000000000..6e1277461 --- /dev/null +++ b/support/procedural-fork/src/pallet/parse/mod.rs @@ -0,0 +1,749 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Parse for pallet macro. +//! +//! Parse the module into `Def` struct through `Def::try_from` function. + +pub mod call; +pub mod composite; +pub mod config; +pub mod error; +pub mod event; +pub mod extra_constants; +pub mod genesis_build; +pub mod genesis_config; +pub mod helper; +pub mod hooks; +pub mod inherent; +pub mod origin; +pub mod pallet_struct; +pub mod storage; +pub mod tasks; +pub mod type_value; +pub mod validate_unsigned; + +#[cfg(test)] +pub mod tests; + +use composite::{keyword::CompositeKeyword, CompositeDef}; +use frame_support_procedural_tools::generate_access_from_frame_or_crate; +use quote::ToTokens; +use syn::spanned::Spanned; + +/// Parsed definition of a pallet. +pub struct Def { + /// The module items. + /// (their order must not be modified because they are registered in individual definitions). + pub item: syn::ItemMod, + pub config: config::ConfigDef, + pub pallet_struct: pallet_struct::PalletStructDef, + pub hooks: Option, + pub call: Option, + pub tasks: Option, + pub task_enum: Option, + pub storages: Vec, + pub error: Option, + pub event: Option, + pub origin: Option, + pub inherent: Option, + pub genesis_config: Option, + pub genesis_build: Option, + pub validate_unsigned: Option, + pub extra_constants: Option, + pub composites: Vec, + pub type_values: Vec, + pub frame_system: syn::Path, + pub frame_support: syn::Path, + pub dev_mode: bool, +} + +impl Def { + pub fn try_from(mut item: syn::ItemMod, dev_mode: bool) -> syn::Result { + let frame_system = generate_access_from_frame_or_crate("frame-system")?; + let frame_support = generate_access_from_frame_or_crate("frame-support")?; + + let item_span = item.span(); + let items = &mut item + .content + .as_mut() + .ok_or_else(|| { + let msg = "Invalid pallet definition, expected mod to be inlined."; + syn::Error::new(item_span, msg) + })? + .1; + + let mut config = None; + let mut pallet_struct = None; + let mut hooks = None; + let mut call = None; + let mut tasks = None; + let mut task_enum = None; + let mut error = None; + let mut event = None; + let mut origin = None; + let mut inherent = None; + let mut genesis_config = None; + let mut genesis_build = None; + let mut validate_unsigned = None; + let mut extra_constants = None; + let mut storages = vec![]; + let mut type_values = vec![]; + let mut composites: Vec = vec![]; + + for (index, item) in items.iter_mut().enumerate() { + let pallet_attr: Option = helper::take_first_item_pallet_attr(item)?; + + match pallet_attr { + Some(PalletAttr::Config(span, with_default)) if config.is_none() => + config = Some(config::ConfigDef::try_from( + &frame_system, + span, + index, + item, + with_default, + )?), + Some(PalletAttr::Pallet(span)) if pallet_struct.is_none() => { + let p = pallet_struct::PalletStructDef::try_from(span, index, item)?; + pallet_struct = Some(p); + }, + Some(PalletAttr::Hooks(span)) if hooks.is_none() => { + let m = hooks::HooksDef::try_from(span, index, item)?; + hooks = Some(m); + }, + Some(PalletAttr::RuntimeCall(cw, span)) if call.is_none() => + call = Some(call::CallDef::try_from(span, index, item, dev_mode, cw)?), + Some(PalletAttr::Tasks(_)) if tasks.is_none() => { + let item_tokens = item.to_token_stream(); + // `TasksDef::parse` needs to know if attr was provided so we artificially + // re-insert it here + tasks = Some(syn::parse2::(quote::quote! { + #[pallet::tasks_experimental] + #item_tokens + })?); + + // replace item with a no-op because it will be handled by the expansion of tasks + *item = syn::Item::Verbatim(quote::quote!()); + } + Some(PalletAttr::TaskCondition(span)) => return Err(syn::Error::new( + span, + "`#[pallet::task_condition]` can only be used on items within an `impl` statement." + )), + Some(PalletAttr::TaskIndex(span)) => return Err(syn::Error::new( + span, + "`#[pallet::task_index]` can only be used on items within an `impl` statement." + )), + Some(PalletAttr::TaskList(span)) => return Err(syn::Error::new( + span, + "`#[pallet::task_list]` can only be used on items within an `impl` statement." + )), + Some(PalletAttr::RuntimeTask(_)) if task_enum.is_none() => + task_enum = Some(syn::parse2::(item.to_token_stream())?), + Some(PalletAttr::Error(span)) if error.is_none() => + error = Some(error::ErrorDef::try_from(span, index, item)?), + Some(PalletAttr::RuntimeEvent(span)) if event.is_none() => + event = Some(event::EventDef::try_from(span, index, item)?), + Some(PalletAttr::GenesisConfig(_)) if genesis_config.is_none() => { + let g = genesis_config::GenesisConfigDef::try_from(index, item)?; + genesis_config = Some(g); + }, + Some(PalletAttr::GenesisBuild(span)) if genesis_build.is_none() => { + let g = genesis_build::GenesisBuildDef::try_from(span, index, item)?; + genesis_build = Some(g); + }, + Some(PalletAttr::RuntimeOrigin(_)) if origin.is_none() => + origin = Some(origin::OriginDef::try_from(index, item)?), + Some(PalletAttr::Inherent(_)) if inherent.is_none() => + inherent = Some(inherent::InherentDef::try_from(index, item)?), + Some(PalletAttr::Storage(span)) => + storages.push(storage::StorageDef::try_from(span, index, item, dev_mode)?), + Some(PalletAttr::ValidateUnsigned(_)) if validate_unsigned.is_none() => { + let v = validate_unsigned::ValidateUnsignedDef::try_from(index, item)?; + validate_unsigned = Some(v); + }, + Some(PalletAttr::TypeValue(span)) => + type_values.push(type_value::TypeValueDef::try_from(span, index, item)?), + Some(PalletAttr::ExtraConstants(_)) => + extra_constants = + Some(extra_constants::ExtraConstantsDef::try_from(index, item)?), + Some(PalletAttr::Composite(span)) => { + let composite = + composite::CompositeDef::try_from(span, index, &frame_support, item)?; + if composites.iter().any(|def| { + match (&def.composite_keyword, &composite.composite_keyword) { + ( + CompositeKeyword::FreezeReason(_), + CompositeKeyword::FreezeReason(_), + ) | + (CompositeKeyword::HoldReason(_), CompositeKeyword::HoldReason(_)) | + (CompositeKeyword::LockId(_), CompositeKeyword::LockId(_)) | + ( + CompositeKeyword::SlashReason(_), + CompositeKeyword::SlashReason(_), + ) => true, + _ => false, + } + }) { + let msg = format!( + "Invalid duplicated `{}` definition", + composite.composite_keyword + ); + return Err(syn::Error::new(composite.composite_keyword.span(), &msg)) + } + composites.push(composite); + }, + Some(attr) => { + let msg = "Invalid duplicated attribute"; + return Err(syn::Error::new(attr.span(), msg)) + }, + None => (), + } + } + + if genesis_config.is_some() != genesis_build.is_some() { + let msg = format!( + "`#[pallet::genesis_config]` and `#[pallet::genesis_build]` attributes must be \ + either both used or both not used, instead genesis_config is {} and genesis_build \ + is {}", + genesis_config.as_ref().map_or("unused", |_| "used"), + genesis_build.as_ref().map_or("unused", |_| "used"), + ); + return Err(syn::Error::new(item_span, msg)) + } + + Self::resolve_tasks(&item_span, &mut tasks, &mut task_enum, items)?; + + let def = Def { + item, + config: config + .ok_or_else(|| syn::Error::new(item_span, "Missing `#[pallet::config]`"))?, + pallet_struct: pallet_struct + .ok_or_else(|| syn::Error::new(item_span, "Missing `#[pallet::pallet]`"))?, + hooks, + call, + tasks, + task_enum, + extra_constants, + genesis_config, + genesis_build, + validate_unsigned, + error, + event, + origin, + inherent, + storages, + composites, + type_values, + frame_system, + frame_support, + dev_mode, + }; + + def.check_instance_usage()?; + def.check_event_usage()?; + + Ok(def) + } + + /// Performs extra logic checks necessary for the `#[pallet::tasks_experimental]` feature. + fn resolve_tasks( + item_span: &proc_macro2::Span, + tasks: &mut Option, + task_enum: &mut Option, + items: &mut Vec, + ) -> syn::Result<()> { + // fallback for manual (without macros) definition of tasks impl + Self::resolve_manual_tasks_impl(tasks, task_enum, items)?; + + // fallback for manual (without macros) definition of task enum + Self::resolve_manual_task_enum(tasks, task_enum, items)?; + + // ensure that if `task_enum` is specified, `tasks` is also specified + match (&task_enum, &tasks) { + (Some(_), None) => + return Err(syn::Error::new( + *item_span, + "Missing `#[pallet::tasks_experimental]` impl", + )), + (None, Some(tasks)) => + if tasks.tasks_attr.is_none() { + return Err(syn::Error::new( + tasks.item_impl.impl_token.span(), + "A `#[pallet::tasks_experimental]` attribute must be attached to your `Task` impl if the \ + task enum has been omitted", + )) + } else { + }, + _ => (), + } + + Ok(()) + } + + /// Tries to locate task enum based on the tasks impl target if attribute is not specified + /// but impl is present. If one is found, `task_enum` is set appropriately. + fn resolve_manual_task_enum( + tasks: &Option, + task_enum: &mut Option, + items: &mut Vec, + ) -> syn::Result<()> { + let (None, Some(tasks)) = (&task_enum, &tasks) else { return Ok(()) }; + let syn::Type::Path(type_path) = &*tasks.item_impl.self_ty else { return Ok(()) }; + let type_path = type_path.path.segments.iter().collect::>(); + let (Some(seg), None) = (type_path.get(0), type_path.get(1)) else { return Ok(()) }; + let mut result = None; + for item in items { + let syn::Item::Enum(item_enum) = item else { continue }; + if item_enum.ident == seg.ident { + result = Some(syn::parse2::(item_enum.to_token_stream())?); + // replace item with a no-op because it will be handled by the expansion of + // `task_enum`. We use a no-op instead of simply removing it from the vec + // so that any indices collected by `Def::try_from` remain accurate + *item = syn::Item::Verbatim(quote::quote!()); + break + } + } + *task_enum = result; + Ok(()) + } + + /// Tries to locate a manual tasks impl (an impl implementing a trait whose last path segment is + /// `Task`) in the event that one has not been found already via the attribute macro + pub fn resolve_manual_tasks_impl( + tasks: &mut Option, + task_enum: &Option, + items: &Vec, + ) -> syn::Result<()> { + let None = tasks else { return Ok(()) }; + let mut result = None; + for item in items { + let syn::Item::Impl(item_impl) = item else { continue }; + let Some((_, path, _)) = &item_impl.trait_ else { continue }; + let Some(trait_last_seg) = path.segments.last() else { continue }; + let syn::Type::Path(target_path) = &*item_impl.self_ty else { continue }; + let target_path = target_path.path.segments.iter().collect::>(); + let (Some(target_ident), None) = (target_path.get(0), target_path.get(1)) else { + continue + }; + let matches_task_enum = match task_enum { + Some(task_enum) => task_enum.item_enum.ident == target_ident.ident, + None => true, + }; + if trait_last_seg.ident == "Task" && matches_task_enum { + result = Some(syn::parse2::(item_impl.to_token_stream())?); + break + } + } + *tasks = result; + Ok(()) + } + + /// Check that usage of trait `Event` is consistent with the definition, i.e. it is declared + /// and trait defines type RuntimeEvent, or not declared and no trait associated type. + fn check_event_usage(&self) -> syn::Result<()> { + match (self.config.has_event_type, self.event.is_some()) { + (true, false) => { + let msg = "Invalid usage of RuntimeEvent, `Config` contains associated type `RuntimeEvent`, \ + but enum `Event` is not declared (i.e. no use of `#[pallet::event]`). \ + Note that type `RuntimeEvent` in trait is reserved to work alongside pallet event."; + Err(syn::Error::new(proc_macro2::Span::call_site(), msg)) + }, + (false, true) => { + let msg = "Invalid usage of RuntimeEvent, `Config` contains no associated type \ + `RuntimeEvent`, but enum `Event` is declared (in use of `#[pallet::event]`). \ + An RuntimeEvent associated type must be declare on trait `Config`."; + Err(syn::Error::new(proc_macro2::Span::call_site(), msg)) + }, + _ => Ok(()), + } + } + + /// Check that usage of trait `Config` is consistent with the definition, i.e. it is used with + /// instance iff it is defined with instance. + fn check_instance_usage(&self) -> syn::Result<()> { + let mut instances = vec![]; + instances.extend_from_slice(&self.pallet_struct.instances[..]); + instances.extend(&mut self.storages.iter().flat_map(|s| s.instances.clone())); + if let Some(call) = &self.call { + instances.extend_from_slice(&call.instances[..]); + } + if let Some(hooks) = &self.hooks { + instances.extend_from_slice(&hooks.instances[..]); + } + if let Some(event) = &self.event { + instances.extend_from_slice(&event.instances[..]); + } + if let Some(error) = &self.error { + instances.extend_from_slice(&error.instances[..]); + } + if let Some(inherent) = &self.inherent { + instances.extend_from_slice(&inherent.instances[..]); + } + if let Some(origin) = &self.origin { + instances.extend_from_slice(&origin.instances[..]); + } + if let Some(genesis_config) = &self.genesis_config { + instances.extend_from_slice(&genesis_config.instances[..]); + } + if let Some(genesis_build) = &self.genesis_build { + genesis_build.instances.as_ref().map(|i| instances.extend_from_slice(&i)); + } + if let Some(extra_constants) = &self.extra_constants { + instances.extend_from_slice(&extra_constants.instances[..]); + } + + let mut errors = instances.into_iter().filter_map(|instances| { + if instances.has_instance == self.config.has_instance { + return None + } + let msg = if self.config.has_instance { + "Invalid generic declaration, trait is defined with instance but generic use none" + } else { + "Invalid generic declaration, trait is defined without instance but generic use \ + some" + }; + Some(syn::Error::new(instances.span, msg)) + }); + + if let Some(mut first_error) = errors.next() { + for error in errors { + first_error.combine(error) + } + Err(first_error) + } else { + Ok(()) + } + } + + /// Depending on if pallet is instantiable: + /// * either `T: Config` + /// * or `T: Config, I: 'static` + pub fn type_impl_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { + if self.config.has_instance { + quote::quote_spanned!(span => T: Config, I: 'static) + } else { + quote::quote_spanned!(span => T: Config) + } + } + + /// Depending on if pallet is instantiable: + /// * either `T: Config` + /// * or `T: Config, I: 'static = ()` + pub fn type_decl_bounded_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { + if self.config.has_instance { + quote::quote_spanned!(span => T: Config, I: 'static = ()) + } else { + quote::quote_spanned!(span => T: Config) + } + } + + /// Depending on if pallet is instantiable: + /// * either `T` + /// * or `T, I = ()` + pub fn type_decl_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { + if self.config.has_instance { + quote::quote_spanned!(span => T, I = ()) + } else { + quote::quote_spanned!(span => T) + } + } + + /// Depending on if pallet is instantiable: + /// * either `` + /// * or `` + /// to be used when using pallet trait `Config` + pub fn trait_use_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { + if self.config.has_instance { + quote::quote_spanned!(span => ) + } else { + quote::quote_spanned!(span => ) + } + } + + /// Depending on if pallet is instantiable: + /// * either `T` + /// * or `T, I` + pub fn type_use_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { + if self.config.has_instance { + quote::quote_spanned!(span => T, I) + } else { + quote::quote_spanned!(span => T) + } + } +} + +/// Some generic kind for type which can be not generic, or generic over config, +/// or generic over config and instance, but not generic only over instance. +pub enum GenericKind { + None, + Config, + ConfigAndInstance, +} + +impl GenericKind { + /// Return Err if it is only generics over instance but not over config. + pub fn from_gens(has_config: bool, has_instance: bool) -> Result { + match (has_config, has_instance) { + (false, false) => Ok(GenericKind::None), + (true, false) => Ok(GenericKind::Config), + (true, true) => Ok(GenericKind::ConfigAndInstance), + (false, true) => Err(()), + } + } + + /// Return the generic to be used when using the type. + /// + /// Depending on its definition it can be: ``, `T` or `T, I` + pub fn type_use_gen(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { + match self { + GenericKind::None => quote::quote!(), + GenericKind::Config => quote::quote_spanned!(span => T), + GenericKind::ConfigAndInstance => quote::quote_spanned!(span => T, I), + } + } + + /// Return the generic to be used in `impl<..>` when implementing on the type. + pub fn type_impl_gen(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { + match self { + GenericKind::None => quote::quote!(), + GenericKind::Config => quote::quote_spanned!(span => T: Config), + GenericKind::ConfigAndInstance => { + quote::quote_spanned!(span => T: Config, I: 'static) + }, + } + } + + /// Return whereas the type has some generic. + pub fn is_generic(&self) -> bool { + match self { + GenericKind::None => false, + GenericKind::Config | GenericKind::ConfigAndInstance => true, + } + } +} + +/// List of additional token to be used for parsing. +mod keyword { + syn::custom_keyword!(origin); + syn::custom_keyword!(call); + syn::custom_keyword!(tasks_experimental); + syn::custom_keyword!(task_enum); + syn::custom_keyword!(task_list); + syn::custom_keyword!(task_condition); + syn::custom_keyword!(task_index); + syn::custom_keyword!(weight); + syn::custom_keyword!(event); + syn::custom_keyword!(config); + syn::custom_keyword!(with_default); + syn::custom_keyword!(hooks); + syn::custom_keyword!(inherent); + syn::custom_keyword!(error); + syn::custom_keyword!(storage); + syn::custom_keyword!(genesis_build); + syn::custom_keyword!(genesis_config); + syn::custom_keyword!(validate_unsigned); + syn::custom_keyword!(type_value); + syn::custom_keyword!(pallet); + syn::custom_keyword!(extra_constants); + syn::custom_keyword!(composite_enum); +} + +/// Parse attributes for item in pallet module +/// syntax must be `pallet::` (e.g. `#[pallet::config]`) +enum PalletAttr { + Config(proc_macro2::Span, bool), + Pallet(proc_macro2::Span), + Hooks(proc_macro2::Span), + /// A `#[pallet::call]` with optional attributes to specialize the behaviour. + /// + /// # Attributes + /// + /// Each attribute `attr` can take the form of `#[pallet::call(attr = …)]` or + /// `#[pallet::call(attr(…))]`. The possible attributes are: + /// + /// ## `weight` + /// + /// Can be used to reduce the repetitive weight annotation in the trivial case. It accepts one + /// argument that is expected to be an implementation of the `WeightInfo` or something that + /// behaves syntactically equivalent. This allows to annotate a `WeightInfo` for all the calls. + /// Now each call does not need to specify its own `#[pallet::weight]` but can instead use the + /// one from the `#[pallet::call]` definition. So instead of having to write it on each call: + /// + /// ```ignore + /// #[pallet::call] + /// impl Pallet { + /// #[pallet::weight(T::WeightInfo::create())] + /// pub fn create( + /// ``` + /// you can now omit it on the call itself, if the name of the weigh function matches the call: + /// + /// ```ignore + /// #[pallet::call(weight = ::WeightInfo)] + /// impl Pallet { + /// pub fn create( + /// ``` + /// + /// It is possible to use this syntax together with instantiated pallets by using `Config` + /// instead. + /// + /// ### Dev Mode + /// + /// Normally the `dev_mode` sets all weights of calls without a `#[pallet::weight]` annotation + /// to zero. Now when there is a `weight` attribute on the `#[pallet::call]`, then that is used + /// instead of the zero weight. So to say: it works together with `dev_mode`. + RuntimeCall(Option, proc_macro2::Span), + Error(proc_macro2::Span), + Tasks(proc_macro2::Span), + TaskList(proc_macro2::Span), + TaskCondition(proc_macro2::Span), + TaskIndex(proc_macro2::Span), + RuntimeTask(proc_macro2::Span), + RuntimeEvent(proc_macro2::Span), + RuntimeOrigin(proc_macro2::Span), + Inherent(proc_macro2::Span), + Storage(proc_macro2::Span), + GenesisConfig(proc_macro2::Span), + GenesisBuild(proc_macro2::Span), + ValidateUnsigned(proc_macro2::Span), + TypeValue(proc_macro2::Span), + ExtraConstants(proc_macro2::Span), + Composite(proc_macro2::Span), +} + +impl PalletAttr { + fn span(&self) -> proc_macro2::Span { + match self { + Self::Config(span, _) => *span, + Self::Pallet(span) => *span, + Self::Hooks(span) => *span, + Self::Tasks(span) => *span, + Self::TaskCondition(span) => *span, + Self::TaskIndex(span) => *span, + Self::TaskList(span) => *span, + Self::Error(span) => *span, + Self::RuntimeTask(span) => *span, + Self::RuntimeCall(_, span) => *span, + Self::RuntimeEvent(span) => *span, + Self::RuntimeOrigin(span) => *span, + Self::Inherent(span) => *span, + Self::Storage(span) => *span, + Self::GenesisConfig(span) => *span, + Self::GenesisBuild(span) => *span, + Self::ValidateUnsigned(span) => *span, + Self::TypeValue(span) => *span, + Self::ExtraConstants(span) => *span, + Self::Composite(span) => *span, + } + } +} + +impl syn::parse::Parse for PalletAttr { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + let lookahead = content.lookahead1(); + if lookahead.peek(keyword::config) { + let span = content.parse::()?.span(); + let with_default = content.peek(syn::token::Paren); + if with_default { + let inside_config; + let _paren = syn::parenthesized!(inside_config in content); + inside_config.parse::()?; + } + Ok(PalletAttr::Config(span, with_default)) + } else if lookahead.peek(keyword::pallet) { + Ok(PalletAttr::Pallet(content.parse::()?.span())) + } else if lookahead.peek(keyword::hooks) { + Ok(PalletAttr::Hooks(content.parse::()?.span())) + } else if lookahead.peek(keyword::call) { + let span = content.parse::().expect("peeked").span(); + let attr = match content.is_empty() { + true => None, + false => Some(InheritedCallWeightAttr::parse(&content)?), + }; + Ok(PalletAttr::RuntimeCall(attr, span)) + } else if lookahead.peek(keyword::tasks_experimental) { + Ok(PalletAttr::Tasks(content.parse::()?.span())) + } else if lookahead.peek(keyword::task_enum) { + Ok(PalletAttr::RuntimeTask(content.parse::()?.span())) + } else if lookahead.peek(keyword::task_condition) { + Ok(PalletAttr::TaskCondition(content.parse::()?.span())) + } else if lookahead.peek(keyword::task_index) { + Ok(PalletAttr::TaskIndex(content.parse::()?.span())) + } else if lookahead.peek(keyword::task_list) { + Ok(PalletAttr::TaskList(content.parse::()?.span())) + } else if lookahead.peek(keyword::error) { + Ok(PalletAttr::Error(content.parse::()?.span())) + } else if lookahead.peek(keyword::event) { + Ok(PalletAttr::RuntimeEvent(content.parse::()?.span())) + } else if lookahead.peek(keyword::origin) { + Ok(PalletAttr::RuntimeOrigin(content.parse::()?.span())) + } else if lookahead.peek(keyword::inherent) { + Ok(PalletAttr::Inherent(content.parse::()?.span())) + } else if lookahead.peek(keyword::storage) { + Ok(PalletAttr::Storage(content.parse::()?.span())) + } else if lookahead.peek(keyword::genesis_config) { + Ok(PalletAttr::GenesisConfig(content.parse::()?.span())) + } else if lookahead.peek(keyword::genesis_build) { + Ok(PalletAttr::GenesisBuild(content.parse::()?.span())) + } else if lookahead.peek(keyword::validate_unsigned) { + Ok(PalletAttr::ValidateUnsigned(content.parse::()?.span())) + } else if lookahead.peek(keyword::type_value) { + Ok(PalletAttr::TypeValue(content.parse::()?.span())) + } else if lookahead.peek(keyword::extra_constants) { + Ok(PalletAttr::ExtraConstants(content.parse::()?.span())) + } else if lookahead.peek(keyword::composite_enum) { + Ok(PalletAttr::Composite(content.parse::()?.span())) + } else { + Err(lookahead.error()) + } + } +} + +/// The optional weight annotation on a `#[pallet::call]` like `#[pallet::call(weight($type))]`. +#[derive(Clone)] +pub struct InheritedCallWeightAttr { + pub typename: syn::Type, + pub span: proc_macro2::Span, +} + +impl syn::parse::Parse for InheritedCallWeightAttr { + // Parses `(weight($type))` or `(weight = $type)`. + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let content; + syn::parenthesized!(content in input); + content.parse::()?; + let lookahead = content.lookahead1(); + + let buffer = if lookahead.peek(syn::token::Paren) { + let inner; + syn::parenthesized!(inner in content); + inner + } else if lookahead.peek(syn::Token![=]) { + content.parse::().expect("peeked"); + content + } else { + return Err(lookahead.error()) + }; + + Ok(Self { typename: buffer.parse()?, span: input.span() }) + } +} diff --git a/support/procedural-fork/src/pallet/parse/origin.rs b/support/procedural-fork/src/pallet/parse/origin.rs new file mode 100644 index 000000000..76e2a8841 --- /dev/null +++ b/support/procedural-fork/src/pallet/parse/origin.rs @@ -0,0 +1,72 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::helper; +use syn::spanned::Spanned; + +/// Definition of the pallet origin type. +/// +/// Either: +/// * `type Origin` +/// * `struct Origin` +/// * `enum Origin` +pub struct OriginDef { + /// The index of item in pallet module. + pub index: usize, + pub has_instance: bool, + pub is_generic: bool, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Vec, +} + +impl OriginDef { + pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { + let item_span = item.span(); + let (vis, ident, generics) = match &item { + syn::Item::Enum(item) => (&item.vis, &item.ident, &item.generics), + syn::Item::Struct(item) => (&item.vis, &item.ident, &item.generics), + syn::Item::Type(item) => (&item.vis, &item.ident, &item.generics), + _ => { + let msg = "Invalid pallet::origin, expected enum or struct or type"; + return Err(syn::Error::new(item.span(), msg)) + }, + }; + + let has_instance = generics.params.len() == 2; + let is_generic = !generics.params.is_empty(); + + let mut instances = vec![]; + if let Some(u) = helper::check_type_def_optional_gen(generics, item.span())? { + instances.push(u); + } else { + // construct_runtime only allow generic event for instantiable pallet. + instances.push(helper::InstanceUsage { has_instance: false, span: ident.span() }) + } + + if !matches!(vis, syn::Visibility::Public(_)) { + let msg = "Invalid pallet::origin, Origin must be public"; + return Err(syn::Error::new(item_span, msg)) + } + + if ident != "Origin" { + let msg = "Invalid pallet::origin, ident must `Origin`"; + return Err(syn::Error::new(ident.span(), msg)) + } + + Ok(OriginDef { index, has_instance, is_generic, instances }) + } +} diff --git a/support/procedural-fork/src/pallet/parse/pallet_struct.rs b/support/procedural-fork/src/pallet/parse/pallet_struct.rs new file mode 100644 index 000000000..b64576099 --- /dev/null +++ b/support/procedural-fork/src/pallet/parse/pallet_struct.rs @@ -0,0 +1,149 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::helper; +use quote::ToTokens; +use syn::spanned::Spanned; + +/// List of additional token to be used for parsing. +mod keyword { + syn::custom_keyword!(pallet); + syn::custom_keyword!(Pallet); + syn::custom_keyword!(without_storage_info); + syn::custom_keyword!(storage_version); +} + +/// Definition of the pallet pallet. +pub struct PalletStructDef { + /// The index of item in pallet pallet. + pub index: usize, + /// A set of usage of instance, must be check for consistency with config trait. + pub instances: Vec, + /// The keyword Pallet used (contains span). + pub pallet: keyword::Pallet, + /// The span of the pallet::pallet attribute. + pub attr_span: proc_macro2::Span, + /// Whether to specify the storages max encoded len when implementing `StorageInfoTrait`. + /// Contains the span of the attribute. + pub without_storage_info: Option, + /// The in-code storage version of the pallet. + pub storage_version: Option, +} + +/// Parse for one variant of: +/// * `#[pallet::without_storage_info]` +/// * `#[pallet::storage_version(STORAGE_VERSION)]` +pub enum PalletStructAttr { + WithoutStorageInfoTrait(proc_macro2::Span), + StorageVersion { storage_version: syn::Path, span: proc_macro2::Span }, +} + +impl PalletStructAttr { + fn span(&self) -> proc_macro2::Span { + match self { + Self::WithoutStorageInfoTrait(span) | Self::StorageVersion { span, .. } => *span, + } + } +} + +impl syn::parse::Parse for PalletStructAttr { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + let lookahead = content.lookahead1(); + if lookahead.peek(keyword::without_storage_info) { + let span = content.parse::()?.span(); + Ok(Self::WithoutStorageInfoTrait(span)) + } else if lookahead.peek(keyword::storage_version) { + let span = content.parse::()?.span(); + + let version_content; + syn::parenthesized!(version_content in content); + let storage_version = version_content.parse::()?; + + Ok(Self::StorageVersion { storage_version, span }) + } else { + Err(lookahead.error()) + } + } +} + +impl PalletStructDef { + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + ) -> syn::Result { + let item = if let syn::Item::Struct(item) = item { + item + } else { + let msg = "Invalid pallet::pallet, expected struct definition"; + return Err(syn::Error::new(item.span(), msg)) + }; + + let mut without_storage_info = None; + let mut storage_version_found = None; + + let struct_attrs: Vec = helper::take_item_pallet_attrs(&mut item.attrs)?; + for attr in struct_attrs { + match attr { + PalletStructAttr::WithoutStorageInfoTrait(span) + if without_storage_info.is_none() => + { + without_storage_info = Some(span); + }, + PalletStructAttr::StorageVersion { storage_version, .. } + if storage_version_found.is_none() => + { + storage_version_found = Some(storage_version); + }, + attr => { + let msg = "Unexpected duplicated attribute"; + return Err(syn::Error::new(attr.span(), msg)) + }, + } + } + + let pallet = syn::parse2::(item.ident.to_token_stream())?; + + if !matches!(item.vis, syn::Visibility::Public(_)) { + let msg = "Invalid pallet::pallet, Pallet must be public"; + return Err(syn::Error::new(item.span(), msg)) + } + + if item.generics.where_clause.is_some() { + let msg = "Invalid pallet::pallet, where clause not supported on Pallet declaration"; + return Err(syn::Error::new(item.generics.where_clause.span(), msg)) + } + + let instances = + vec![helper::check_type_def_gen_no_bounds(&item.generics, item.ident.span())?]; + + Ok(Self { + index, + instances, + pallet, + attr_span, + without_storage_info, + storage_version: storage_version_found, + }) + } +} diff --git a/support/procedural-fork/src/pallet/parse/storage.rs b/support/procedural-fork/src/pallet/parse/storage.rs new file mode 100644 index 000000000..9d96a18b5 --- /dev/null +++ b/support/procedural-fork/src/pallet/parse/storage.rs @@ -0,0 +1,947 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::helper; +use frame_support_procedural_tools::get_doc_literals; +use quote::ToTokens; +use std::collections::HashMap; +use syn::spanned::Spanned; + +/// List of additional token to be used for parsing. +mod keyword { + syn::custom_keyword!(Error); + syn::custom_keyword!(pallet); + syn::custom_keyword!(getter); + syn::custom_keyword!(storage_prefix); + syn::custom_keyword!(unbounded); + syn::custom_keyword!(whitelist_storage); + syn::custom_keyword!(disable_try_decode_storage); + syn::custom_keyword!(OptionQuery); + syn::custom_keyword!(ResultQuery); + syn::custom_keyword!(ValueQuery); +} + +/// Parse for one of the following: +/// * `#[pallet::getter(fn dummy)]` +/// * `#[pallet::storage_prefix = "CustomName"]` +/// * `#[pallet::unbounded]` +/// * `#[pallet::whitelist_storage] +/// * `#[pallet::disable_try_decode_storage]` +pub enum PalletStorageAttr { + Getter(syn::Ident, proc_macro2::Span), + StorageName(syn::LitStr, proc_macro2::Span), + Unbounded(proc_macro2::Span), + WhitelistStorage(proc_macro2::Span), + DisableTryDecodeStorage(proc_macro2::Span), +} + +impl PalletStorageAttr { + fn attr_span(&self) -> proc_macro2::Span { + match self { + Self::Getter(_, span) | + Self::StorageName(_, span) | + Self::Unbounded(span) | + Self::WhitelistStorage(span) => *span, + Self::DisableTryDecodeStorage(span) => *span, + } + } +} + +impl syn::parse::Parse for PalletStorageAttr { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let attr_span = input.span(); + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + let lookahead = content.lookahead1(); + if lookahead.peek(keyword::getter) { + content.parse::()?; + + let generate_content; + syn::parenthesized!(generate_content in content); + generate_content.parse::()?; + Ok(Self::Getter(generate_content.parse::()?, attr_span)) + } else if lookahead.peek(keyword::storage_prefix) { + content.parse::()?; + content.parse::()?; + + let renamed_prefix = content.parse::()?; + // Ensure the renamed prefix is a proper Rust identifier + syn::parse_str::(&renamed_prefix.value()).map_err(|_| { + let msg = format!("`{}` is not a valid identifier", renamed_prefix.value()); + syn::Error::new(renamed_prefix.span(), msg) + })?; + + Ok(Self::StorageName(renamed_prefix, attr_span)) + } else if lookahead.peek(keyword::unbounded) { + content.parse::()?; + + Ok(Self::Unbounded(attr_span)) + } else if lookahead.peek(keyword::whitelist_storage) { + content.parse::()?; + Ok(Self::WhitelistStorage(attr_span)) + } else if lookahead.peek(keyword::disable_try_decode_storage) { + content.parse::()?; + Ok(Self::DisableTryDecodeStorage(attr_span)) + } else { + Err(lookahead.error()) + } + } +} + +struct PalletStorageAttrInfo { + getter: Option, + rename_as: Option, + unbounded: bool, + whitelisted: bool, + try_decode: bool, +} + +impl PalletStorageAttrInfo { + fn from_attrs(attrs: Vec) -> syn::Result { + let mut getter = None; + let mut rename_as = None; + let mut unbounded = false; + let mut whitelisted = false; + let mut disable_try_decode_storage = false; + for attr in attrs { + match attr { + PalletStorageAttr::Getter(ident, ..) if getter.is_none() => getter = Some(ident), + PalletStorageAttr::StorageName(name, ..) if rename_as.is_none() => + rename_as = Some(name), + PalletStorageAttr::Unbounded(..) if !unbounded => unbounded = true, + PalletStorageAttr::WhitelistStorage(..) if !whitelisted => whitelisted = true, + PalletStorageAttr::DisableTryDecodeStorage(..) if !disable_try_decode_storage => + disable_try_decode_storage = true, + attr => + return Err(syn::Error::new( + attr.attr_span(), + "Invalid attribute: Duplicate attribute", + )), + } + } + + Ok(PalletStorageAttrInfo { + getter, + rename_as, + unbounded, + whitelisted, + try_decode: !disable_try_decode_storage, + }) + } +} + +/// The value and key types used by storages. Needed to expand metadata. +pub enum Metadata { + Value { value: syn::Type }, + Map { value: syn::Type, key: syn::Type }, + CountedMap { value: syn::Type, key: syn::Type }, + DoubleMap { value: syn::Type, key1: syn::Type, key2: syn::Type }, + NMap { keys: Vec, keygen: syn::Type, value: syn::Type }, + CountedNMap { keys: Vec, keygen: syn::Type, value: syn::Type }, +} + +pub enum QueryKind { + OptionQuery, + ResultQuery(syn::Path, syn::Ident), + ValueQuery, +} + +/// Definition of a storage, storage is a storage type like +/// `type MyStorage = StorageValue` +/// The keys and values types are parsed in order to get metadata +pub struct StorageDef { + /// The index of storage item in pallet module. + pub index: usize, + /// Visibility of the storage type. + pub vis: syn::Visibility, + /// The type ident, to generate the StoragePrefix for. + pub ident: syn::Ident, + /// The keys and value metadata of the storage. + pub metadata: Metadata, + /// The doc associated to the storage. + pub docs: Vec, + /// A set of usage of instance, must be check for consistency with config. + pub instances: Vec, + /// Optional getter to generate. If some then query_kind is ensured to be some as well. + pub getter: Option, + /// Optional expression that evaluates to a type that can be used as StoragePrefix instead of + /// ident. + pub rename_as: Option, + /// Whereas the querytype of the storage is OptionQuery, ResultQuery or ValueQuery. + /// Note that this is best effort as it can't be determined when QueryKind is generic, and + /// result can be false if user do some unexpected type alias. + pub query_kind: Option, + /// Where clause of type definition. + pub where_clause: Option, + /// The span of the pallet::storage attribute. + pub attr_span: proc_macro2::Span, + /// The `cfg` attributes. + pub cfg_attrs: Vec, + /// If generics are named (e.g. `StorageValue`) then this contains all the + /// generics of the storage. + /// If generics are not named, this is none. + pub named_generics: Option, + /// If the value stored in this storage is unbounded. + pub unbounded: bool, + /// Whether or not reads to this storage key will be ignored by benchmarking + pub whitelisted: bool, + /// Whether or not to try to decode the storage key when running try-runtime checks. + pub try_decode: bool, + /// Whether or not a default hasher is allowed to replace `_` + pub use_default_hasher: bool, +} + +/// The parsed generic from the +#[derive(Clone)] +pub enum StorageGenerics { + DoubleMap { + hasher1: syn::Type, + key1: syn::Type, + hasher2: syn::Type, + key2: syn::Type, + value: syn::Type, + query_kind: Option, + on_empty: Option, + max_values: Option, + }, + Map { + hasher: syn::Type, + key: syn::Type, + value: syn::Type, + query_kind: Option, + on_empty: Option, + max_values: Option, + }, + CountedMap { + hasher: syn::Type, + key: syn::Type, + value: syn::Type, + query_kind: Option, + on_empty: Option, + max_values: Option, + }, + Value { + value: syn::Type, + query_kind: Option, + on_empty: Option, + }, + NMap { + keygen: syn::Type, + value: syn::Type, + query_kind: Option, + on_empty: Option, + max_values: Option, + }, + CountedNMap { + keygen: syn::Type, + value: syn::Type, + query_kind: Option, + on_empty: Option, + max_values: Option, + }, +} + +impl StorageGenerics { + /// Return the metadata from the defined generics + fn metadata(&self) -> syn::Result { + let res = match self.clone() { + Self::DoubleMap { value, key1, key2, .. } => Metadata::DoubleMap { value, key1, key2 }, + Self::Map { value, key, .. } => Metadata::Map { value, key }, + Self::CountedMap { value, key, .. } => Metadata::CountedMap { value, key }, + Self::Value { value, .. } => Metadata::Value { value }, + Self::NMap { keygen, value, .. } => + Metadata::NMap { keys: collect_keys(&keygen)?, keygen, value }, + Self::CountedNMap { keygen, value, .. } => + Metadata::CountedNMap { keys: collect_keys(&keygen)?, keygen, value }, + }; + + Ok(res) + } + + /// Return the query kind from the defined generics + fn query_kind(&self) -> Option { + match &self { + Self::DoubleMap { query_kind, .. } | + Self::Map { query_kind, .. } | + Self::CountedMap { query_kind, .. } | + Self::Value { query_kind, .. } | + Self::NMap { query_kind, .. } | + Self::CountedNMap { query_kind, .. } => query_kind.clone(), + } + } +} + +enum StorageKind { + Value, + Map, + CountedMap, + DoubleMap, + NMap, + CountedNMap, +} + +/// Check the generics in the `map` contains the generics in `gen` may contains generics in +/// `optional_gen`, and doesn't contains any other. +fn check_generics( + map: &HashMap, + mandatory_generics: &[&str], + optional_generics: &[&str], + storage_type_name: &str, + args_span: proc_macro2::Span, +) -> syn::Result<()> { + let mut errors = vec![]; + + let expectation = { + let mut e = format!( + "`{}` expect generics {}and optional generics {}", + storage_type_name, + mandatory_generics + .iter() + .map(|name| format!("`{}`, ", name)) + .collect::(), + &optional_generics.iter().map(|name| format!("`{}`, ", name)).collect::(), + ); + e.pop(); + e.pop(); + e.push('.'); + e + }; + + for (gen_name, gen_binding) in map { + if !mandatory_generics.contains(&gen_name.as_str()) && + !optional_generics.contains(&gen_name.as_str()) + { + let msg = format!( + "Invalid pallet::storage, Unexpected generic `{}` for `{}`. {}", + gen_name, storage_type_name, expectation, + ); + errors.push(syn::Error::new(gen_binding.span(), msg)); + } + } + + for mandatory_generic in mandatory_generics { + if !map.contains_key(&mandatory_generic.to_string()) { + let msg = format!( + "Invalid pallet::storage, cannot find `{}` generic, required for `{}`.", + mandatory_generic, storage_type_name + ); + errors.push(syn::Error::new(args_span, msg)); + } + } + + let mut errors = errors.drain(..); + if let Some(mut error) = errors.next() { + for other_error in errors { + error.combine(other_error); + } + Err(error) + } else { + Ok(()) + } +} + +/// Returns `(named generics, metadata, query kind, use_default_hasher)` +fn process_named_generics( + storage: &StorageKind, + args_span: proc_macro2::Span, + args: &[syn::AssocType], + dev_mode: bool, +) -> syn::Result<(Option, Metadata, Option, bool)> { + let mut parsed = HashMap::::new(); + + // Ensure no duplicate. + for arg in args { + if let Some(other) = parsed.get(&arg.ident.to_string()) { + let msg = "Invalid pallet::storage, Duplicated named generic"; + let mut err = syn::Error::new(arg.ident.span(), msg); + err.combine(syn::Error::new(other.ident.span(), msg)); + return Err(err) + } + parsed.insert(arg.ident.to_string(), arg.clone()); + } + + let mut map_mandatory_generics = vec!["Key", "Value"]; + let mut map_optional_generics = vec!["QueryKind", "OnEmpty", "MaxValues"]; + if dev_mode { + map_optional_generics.push("Hasher"); + } else { + map_mandatory_generics.push("Hasher"); + } + + let generics = match storage { + StorageKind::Value => { + check_generics( + &parsed, + &["Value"], + &["QueryKind", "OnEmpty"], + "StorageValue", + args_span, + )?; + + StorageGenerics::Value { + value: parsed + .remove("Value") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), + on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), + } + }, + StorageKind::Map => { + check_generics( + &parsed, + &map_mandatory_generics, + &map_optional_generics, + "StorageMap", + args_span, + )?; + + StorageGenerics::Map { + hasher: parsed + .remove("Hasher") + .map(|binding| binding.ty) + .unwrap_or(syn::parse_quote!(Blake2_128Concat)), + key: parsed + .remove("Key") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + value: parsed + .remove("Value") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), + on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), + max_values: parsed.remove("MaxValues").map(|binding| binding.ty), + } + }, + StorageKind::CountedMap => { + check_generics( + &parsed, + &map_mandatory_generics, + &map_optional_generics, + "CountedStorageMap", + args_span, + )?; + + StorageGenerics::CountedMap { + hasher: parsed + .remove("Hasher") + .map(|binding| binding.ty) + .unwrap_or(syn::Type::Verbatim(quote::quote! { Blake2_128Concat })), + key: parsed + .remove("Key") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + value: parsed + .remove("Value") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), + on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), + max_values: parsed.remove("MaxValues").map(|binding| binding.ty), + } + }, + StorageKind::DoubleMap => { + let mut double_map_mandatory_generics = vec!["Key1", "Key2", "Value"]; + if dev_mode { + map_optional_generics.extend(["Hasher1", "Hasher2"]); + } else { + double_map_mandatory_generics.extend(["Hasher1", "Hasher2"]); + } + + check_generics( + &parsed, + &double_map_mandatory_generics, + &map_optional_generics, + "StorageDoubleMap", + args_span, + )?; + + StorageGenerics::DoubleMap { + hasher1: parsed + .remove("Hasher1") + .map(|binding| binding.ty) + .unwrap_or(syn::parse_quote!(Blake2_128Concat)), + key1: parsed + .remove("Key1") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + hasher2: parsed + .remove("Hasher2") + .map(|binding| binding.ty) + .unwrap_or(syn::parse_quote!(Blake2_128Concat)), + key2: parsed + .remove("Key2") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + value: parsed + .remove("Value") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), + on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), + max_values: parsed.remove("MaxValues").map(|binding| binding.ty), + } + }, + StorageKind::NMap => { + check_generics( + &parsed, + &["Key", "Value"], + &["QueryKind", "OnEmpty", "MaxValues"], + "StorageNMap", + args_span, + )?; + + StorageGenerics::NMap { + keygen: parsed + .remove("Key") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + value: parsed + .remove("Value") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), + on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), + max_values: parsed.remove("MaxValues").map(|binding| binding.ty), + } + }, + StorageKind::CountedNMap => { + check_generics( + &parsed, + &["Key", "Value"], + &["QueryKind", "OnEmpty", "MaxValues"], + "CountedStorageNMap", + args_span, + )?; + + StorageGenerics::CountedNMap { + keygen: parsed + .remove("Key") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + value: parsed + .remove("Value") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), + on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), + max_values: parsed.remove("MaxValues").map(|binding| binding.ty), + } + }, + }; + + let metadata = generics.metadata()?; + let query_kind = generics.query_kind(); + + Ok((Some(generics), metadata, query_kind, false)) +} + +/// Returns `(named generics, metadata, query kind, use_default_hasher)` +fn process_unnamed_generics( + storage: &StorageKind, + args_span: proc_macro2::Span, + args: &[syn::Type], + dev_mode: bool, +) -> syn::Result<(Option, Metadata, Option, bool)> { + let retrieve_arg = |arg_pos| { + args.get(arg_pos).cloned().ok_or_else(|| { + let msg = format!( + "Invalid pallet::storage, unexpected number of generic argument, \ + expect at least {} args, found {}.", + arg_pos + 1, + args.len(), + ); + syn::Error::new(args_span, msg) + }) + }; + + let prefix_arg = retrieve_arg(0)?; + syn::parse2::(prefix_arg.to_token_stream()).map_err(|e| { + let msg = "Invalid pallet::storage, for unnamed generic arguments the type \ + first generic argument must be `_`, the argument is then replaced by macro."; + let mut err = syn::Error::new(prefix_arg.span(), msg); + err.combine(e); + err + })?; + + let use_default_hasher = |arg_pos| { + let arg = retrieve_arg(arg_pos)?; + if syn::parse2::(arg.to_token_stream()).is_ok() { + if dev_mode { + Ok(true) + } else { + let msg = "`_` can only be used in dev_mode. Please specify an appropriate hasher."; + Err(syn::Error::new(arg.span(), msg)) + } + } else { + Ok(false) + } + }; + + let res = match storage { + StorageKind::Value => + (None, Metadata::Value { value: retrieve_arg(1)? }, retrieve_arg(2).ok(), false), + StorageKind::Map => ( + None, + Metadata::Map { key: retrieve_arg(2)?, value: retrieve_arg(3)? }, + retrieve_arg(4).ok(), + use_default_hasher(1)?, + ), + StorageKind::CountedMap => ( + None, + Metadata::CountedMap { key: retrieve_arg(2)?, value: retrieve_arg(3)? }, + retrieve_arg(4).ok(), + use_default_hasher(1)?, + ), + StorageKind::DoubleMap => ( + None, + Metadata::DoubleMap { + key1: retrieve_arg(2)?, + key2: retrieve_arg(4)?, + value: retrieve_arg(5)?, + }, + retrieve_arg(6).ok(), + use_default_hasher(1)? && use_default_hasher(3)?, + ), + StorageKind::NMap => { + let keygen = retrieve_arg(1)?; + let keys = collect_keys(&keygen)?; + ( + None, + Metadata::NMap { keys, keygen, value: retrieve_arg(2)? }, + retrieve_arg(3).ok(), + false, + ) + }, + StorageKind::CountedNMap => { + let keygen = retrieve_arg(1)?; + let keys = collect_keys(&keygen)?; + ( + None, + Metadata::CountedNMap { keys, keygen, value: retrieve_arg(2)? }, + retrieve_arg(3).ok(), + false, + ) + }, + }; + + Ok(res) +} + +/// Returns `(named generics, metadata, query kind, use_default_hasher)` +fn process_generics( + segment: &syn::PathSegment, + dev_mode: bool, +) -> syn::Result<(Option, Metadata, Option, bool)> { + let storage_kind = match &*segment.ident.to_string() { + "StorageValue" => StorageKind::Value, + "StorageMap" => StorageKind::Map, + "CountedStorageMap" => StorageKind::CountedMap, + "StorageDoubleMap" => StorageKind::DoubleMap, + "StorageNMap" => StorageKind::NMap, + "CountedStorageNMap" => StorageKind::CountedNMap, + found => { + let msg = format!( + "Invalid pallet::storage, expected ident: `StorageValue` or \ + `StorageMap` or `CountedStorageMap` or `StorageDoubleMap` or `StorageNMap` or `CountedStorageNMap` \ + in order to expand metadata, found `{}`.", + found, + ); + return Err(syn::Error::new(segment.ident.span(), msg)) + }, + }; + + let args_span = segment.arguments.span(); + + let args = match &segment.arguments { + syn::PathArguments::AngleBracketed(args) if !args.args.is_empty() => args, + _ => { + let msg = "Invalid pallet::storage, invalid number of generic generic arguments, \ + expect more that 0 generic arguments."; + return Err(syn::Error::new(segment.span(), msg)) + }, + }; + + if args.args.iter().all(|gen| matches!(gen, syn::GenericArgument::Type(_))) { + let args = args + .args + .iter() + .map(|gen| match gen { + syn::GenericArgument::Type(gen) => gen.clone(), + _ => unreachable!("It is asserted above that all generics are types"), + }) + .collect::>(); + process_unnamed_generics(&storage_kind, args_span, &args, dev_mode) + } else if args.args.iter().all(|gen| matches!(gen, syn::GenericArgument::AssocType(_))) { + let args = args + .args + .iter() + .map(|gen| match gen { + syn::GenericArgument::AssocType(gen) => gen.clone(), + _ => unreachable!("It is asserted above that all generics are bindings"), + }) + .collect::>(); + process_named_generics(&storage_kind, args_span, &args, dev_mode) + } else { + let msg = "Invalid pallet::storage, invalid generic declaration for storage. Expect only \ + type generics or binding generics, e.g. `` or \ + ``."; + Err(syn::Error::new(segment.span(), msg)) + } +} + +/// Parse the 2nd type argument to `StorageNMap` and return its keys. +fn collect_keys(keygen: &syn::Type) -> syn::Result> { + if let syn::Type::Tuple(tup) = keygen { + tup.elems.iter().map(extract_key).collect::>>() + } else { + Ok(vec![extract_key(keygen)?]) + } +} + +/// In `Key`, extract K and return it. +fn extract_key(ty: &syn::Type) -> syn::Result { + let typ = if let syn::Type::Path(typ) = ty { + typ + } else { + let msg = "Invalid pallet::storage, expected type path"; + return Err(syn::Error::new(ty.span(), msg)) + }; + + let key_struct = typ.path.segments.last().ok_or_else(|| { + let msg = "Invalid pallet::storage, expected type path with at least one segment"; + syn::Error::new(typ.path.span(), msg) + })?; + if key_struct.ident != "Key" && key_struct.ident != "NMapKey" { + let msg = "Invalid pallet::storage, expected Key or NMapKey struct"; + return Err(syn::Error::new(key_struct.ident.span(), msg)) + } + + let ty_params = if let syn::PathArguments::AngleBracketed(args) = &key_struct.arguments { + args + } else { + let msg = "Invalid pallet::storage, expected angle bracketed arguments"; + return Err(syn::Error::new(key_struct.arguments.span(), msg)) + }; + + if ty_params.args.len() != 2 { + let msg = format!( + "Invalid pallet::storage, unexpected number of generic arguments \ + for Key struct, expected 2 args, found {}", + ty_params.args.len() + ); + return Err(syn::Error::new(ty_params.span(), msg)) + } + + let key = match &ty_params.args[1] { + syn::GenericArgument::Type(key_ty) => key_ty.clone(), + _ => { + let msg = "Invalid pallet::storage, expected type"; + return Err(syn::Error::new(ty_params.args[1].span(), msg)) + }, + }; + + Ok(key) +} + +impl StorageDef { + /// Return the storage prefix for this storage item + pub fn prefix(&self) -> String { + self.rename_as + .as_ref() + .map(syn::LitStr::value) + .unwrap_or_else(|| self.ident.to_string()) + } + + /// Return either the span of the ident or the span of the literal in the + /// #[storage_prefix] attribute + pub fn prefix_span(&self) -> proc_macro2::Span { + self.rename_as + .as_ref() + .map(syn::LitStr::span) + .unwrap_or_else(|| self.ident.span()) + } + + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + dev_mode: bool, + ) -> syn::Result { + let item = if let syn::Item::Type(item) = item { + item + } else { + return Err(syn::Error::new(item.span(), "Invalid pallet::storage, expect item type.")) + }; + + let attrs: Vec = helper::take_item_pallet_attrs(&mut item.attrs)?; + let PalletStorageAttrInfo { getter, rename_as, mut unbounded, whitelisted, try_decode } = + PalletStorageAttrInfo::from_attrs(attrs)?; + + // set all storages to be unbounded if dev_mode is enabled + unbounded |= dev_mode; + let cfg_attrs = helper::get_item_cfg_attrs(&item.attrs); + + let instances = vec![helper::check_type_def_gen(&item.generics, item.ident.span())?]; + + let where_clause = item.generics.where_clause.clone(); + let docs = get_doc_literals(&item.attrs); + + let typ = if let syn::Type::Path(typ) = &*item.ty { + typ + } else { + let msg = "Invalid pallet::storage, expected type path"; + return Err(syn::Error::new(item.ty.span(), msg)) + }; + + if typ.path.segments.len() != 1 { + let msg = "Invalid pallet::storage, expected type path with one segment"; + return Err(syn::Error::new(item.ty.span(), msg)) + } + + let (named_generics, metadata, query_kind, use_default_hasher) = + process_generics(&typ.path.segments[0], dev_mode)?; + + let query_kind = query_kind + .map(|query_kind| { + use syn::{ + AngleBracketedGenericArguments, GenericArgument, Path, PathArguments, Type, + TypePath, + }; + + let result_query = match query_kind { + Type::Path(path) + if path + .path + .segments + .last() + .map_or(false, |s| s.ident == "OptionQuery") => + return Ok(Some(QueryKind::OptionQuery)), + Type::Path(TypePath { path: Path { segments, .. }, .. }) + if segments.last().map_or(false, |s| s.ident == "ResultQuery") => + segments + .last() + .expect("segments is checked to have the last value; qed") + .clone(), + Type::Path(path) + if path.path.segments.last().map_or(false, |s| s.ident == "ValueQuery") => + return Ok(Some(QueryKind::ValueQuery)), + _ => return Ok(None), + }; + + let error_type = match result_query.arguments { + PathArguments::AngleBracketed(AngleBracketedGenericArguments { + args, .. + }) => { + if args.len() != 1 { + let msg = format!( + "Invalid pallet::storage, unexpected number of generic arguments \ + for ResultQuery, expected 1 type argument, found {}", + args.len(), + ); + return Err(syn::Error::new(args.span(), msg)) + } + + args[0].clone() + }, + args => { + let msg = format!( + "Invalid pallet::storage, unexpected generic args for ResultQuery, \ + expected angle-bracketed arguments, found `{}`", + args.to_token_stream().to_string() + ); + return Err(syn::Error::new(args.span(), msg)) + }, + }; + + match error_type { + GenericArgument::Type(Type::Path(TypePath { + path: Path { segments: err_variant, leading_colon }, + .. + })) => { + if err_variant.len() < 2 { + let msg = format!( + "Invalid pallet::storage, unexpected number of path segments for \ + the generics in ResultQuery, expected a path with at least 2 \ + segments, found {}", + err_variant.len(), + ); + return Err(syn::Error::new(err_variant.span(), msg)) + } + let mut error = err_variant.clone(); + let err_variant = error + .pop() + .expect("Checked to have at least 2; qed") + .into_value() + .ident; + + // Necessary here to eliminate the last double colon + let last = + error.pop().expect("Checked to have at least 2; qed").into_value(); + error.push_value(last); + + Ok(Some(QueryKind::ResultQuery( + syn::Path { leading_colon, segments: error }, + err_variant, + ))) + }, + gen_arg => { + let msg = format!( + "Invalid pallet::storage, unexpected generic argument kind, expected a \ + type path to a `PalletError` enum variant, found `{}`", + gen_arg.to_token_stream().to_string(), + ); + Err(syn::Error::new(gen_arg.span(), msg)) + }, + } + }) + .transpose()? + .unwrap_or(Some(QueryKind::OptionQuery)); + + if let (None, Some(getter)) = (query_kind.as_ref(), getter.as_ref()) { + let msg = "Invalid pallet::storage, cannot generate getter because QueryKind is not \ + identifiable. QueryKind must be `OptionQuery`, `ResultQuery`, `ValueQuery`, or default \ + one to be identifiable."; + return Err(syn::Error::new(getter.span(), msg)) + } + + Ok(StorageDef { + attr_span, + index, + vis: item.vis.clone(), + ident: item.ident.clone(), + instances, + metadata, + docs, + getter, + rename_as, + query_kind, + where_clause, + cfg_attrs, + named_generics, + unbounded, + whitelisted, + try_decode, + use_default_hasher, + }) + } +} diff --git a/support/procedural-fork/src/pallet/parse/tasks.rs b/support/procedural-fork/src/pallet/parse/tasks.rs new file mode 100644 index 000000000..6405bb415 --- /dev/null +++ b/support/procedural-fork/src/pallet/parse/tasks.rs @@ -0,0 +1,968 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Home of the parsing code for the Tasks API + +use std::collections::HashSet; + +#[cfg(test)] +use crate::assert_parse_error_matches; + +#[cfg(test)] +use crate::pallet::parse::tests::simulate_manifest_dir; + +use derive_syn_parse::Parse; +use frame_support_procedural_tools::generate_access_from_frame_or_crate; +use proc_macro2::TokenStream as TokenStream2; +use quote::{quote, ToTokens}; +use syn::{ + parse::ParseStream, + parse2, + spanned::Spanned, + token::{Bracket, Paren, PathSep, Pound}, + Attribute, Error, Expr, Ident, ImplItem, ImplItemFn, ItemEnum, ItemImpl, LitInt, Path, + PathArguments, Result, TypePath, +}; + +pub mod keywords { + use syn::custom_keyword; + + custom_keyword!(tasks_experimental); + custom_keyword!(task_enum); + custom_keyword!(task_list); + custom_keyword!(task_condition); + custom_keyword!(task_index); + custom_keyword!(task_weight); + custom_keyword!(pallet); +} + +/// Represents the `#[pallet::tasks_experimental]` attribute and its attached item. Also includes +/// metadata about the linked [`TaskEnumDef`] if applicable. +#[derive(Clone, Debug)] +pub struct TasksDef { + pub tasks_attr: Option, + pub tasks: Vec, + pub item_impl: ItemImpl, + /// Path to `frame_support` + pub scrate: Path, + pub enum_ident: Ident, + pub enum_arguments: PathArguments, +} + +impl syn::parse::Parse for TasksDef { + fn parse(input: ParseStream) -> Result { + let item_impl: ItemImpl = input.parse()?; + let (tasks_attrs, normal_attrs) = partition_tasks_attrs(&item_impl); + let tasks_attr = match tasks_attrs.first() { + Some(attr) => Some(parse2::(attr.to_token_stream())?), + None => None, + }; + if let Some(extra_tasks_attr) = tasks_attrs.get(1) { + return Err(Error::new( + extra_tasks_attr.span(), + "unexpected extra `#[pallet::tasks_experimental]` attribute", + )) + } + let tasks: Vec = if tasks_attr.is_some() { + item_impl + .items + .clone() + .into_iter() + .filter(|impl_item| matches!(impl_item, ImplItem::Fn(_))) + .map(|item| parse2::(item.to_token_stream())) + .collect::>()? + } else { + Vec::new() + }; + let mut task_indices = HashSet::::new(); + for task in tasks.iter() { + let task_index = &task.index_attr.meta.index; + if !task_indices.insert(task_index.clone()) { + return Err(Error::new( + task_index.span(), + format!("duplicate task index `{}`", task_index), + )) + } + } + let mut item_impl = item_impl; + item_impl.attrs = normal_attrs; + + // we require the path on the impl to be a TypePath + let enum_path = parse2::(item_impl.self_ty.to_token_stream())?; + let segments = enum_path.path.segments.iter().collect::>(); + let (Some(last_seg), None) = (segments.get(0), segments.get(1)) else { + return Err(Error::new( + enum_path.span(), + "if specified manually, the task enum must be defined locally in this \ + pallet and cannot be a re-export", + )) + }; + let enum_ident = last_seg.ident.clone(); + let enum_arguments = last_seg.arguments.clone(); + + // We do this here because it would be improper to do something fallible like this at + // the expansion phase. Fallible stuff should happen during parsing. + let scrate = generate_access_from_frame_or_crate("frame-support")?; + + Ok(TasksDef { tasks_attr, item_impl, tasks, scrate, enum_ident, enum_arguments }) + } +} + +/// Parsing for a `#[pallet::tasks_experimental]` attr. +pub type PalletTasksAttr = PalletTaskAttr; + +/// Parsing for any of the attributes that can be used within a `#[pallet::tasks_experimental]` +/// [`ItemImpl`]. +pub type TaskAttr = PalletTaskAttr; + +/// Parsing for a `#[pallet::task_index]` attr. +pub type TaskIndexAttr = PalletTaskAttr; + +/// Parsing for a `#[pallet::task_condition]` attr. +pub type TaskConditionAttr = PalletTaskAttr; + +/// Parsing for a `#[pallet::task_list]` attr. +pub type TaskListAttr = PalletTaskAttr; + +/// Parsing for a `#[pallet::task_weight]` attr. +pub type TaskWeightAttr = PalletTaskAttr; + +/// Parsing for a `#[pallet:task_enum]` attr. +pub type PalletTaskEnumAttr = PalletTaskAttr; + +/// Parsing for a manually-specified (or auto-generated) task enum, optionally including the +/// attached `#[pallet::task_enum]` attribute. +#[derive(Clone, Debug)] +pub struct TaskEnumDef { + pub attr: Option, + pub item_enum: ItemEnum, + pub scrate: Path, + pub type_use_generics: TokenStream2, +} + +impl syn::parse::Parse for TaskEnumDef { + fn parse(input: ParseStream) -> Result { + let mut item_enum = input.parse::()?; + let attr = extract_pallet_attr(&mut item_enum)?; + let attr = match attr { + Some(attr) => Some(parse2(attr)?), + None => None, + }; + + // We do this here because it would be improper to do something fallible like this at + // the expansion phase. Fallible stuff should happen during parsing. + let scrate = generate_access_from_frame_or_crate("frame-support")?; + + let type_use_generics = quote!(T); + + Ok(TaskEnumDef { attr, item_enum, scrate, type_use_generics }) + } +} + +/// Represents an individual tasks within a [`TasksDef`]. +#[derive(Debug, Clone)] +pub struct TaskDef { + pub index_attr: TaskIndexAttr, + pub condition_attr: TaskConditionAttr, + pub list_attr: TaskListAttr, + pub weight_attr: TaskWeightAttr, + pub normal_attrs: Vec, + pub item: ImplItemFn, + pub arg_names: Vec, +} + +impl syn::parse::Parse for TaskDef { + fn parse(input: ParseStream) -> Result { + let item = input.parse::()?; + // we only want to activate TaskAttrType parsing errors for tasks-related attributes, + // so we filter them here + let (task_attrs, normal_attrs) = partition_task_attrs(&item); + + let task_attrs: Vec = task_attrs + .into_iter() + .map(|attr| parse2(attr.to_token_stream())) + .collect::>()?; + + let Some(index_attr) = task_attrs + .iter() + .find(|attr| matches!(attr.meta, TaskAttrMeta::TaskIndex(_))) + .cloned() + else { + return Err(Error::new( + item.sig.ident.span(), + "missing `#[pallet::task_index(..)]` attribute", + )) + }; + + let Some(condition_attr) = task_attrs + .iter() + .find(|attr| matches!(attr.meta, TaskAttrMeta::TaskCondition(_))) + .cloned() + else { + return Err(Error::new( + item.sig.ident.span(), + "missing `#[pallet::task_condition(..)]` attribute", + )) + }; + + let Some(list_attr) = task_attrs + .iter() + .find(|attr| matches!(attr.meta, TaskAttrMeta::TaskList(_))) + .cloned() + else { + return Err(Error::new( + item.sig.ident.span(), + "missing `#[pallet::task_list(..)]` attribute", + )) + }; + + let Some(weight_attr) = task_attrs + .iter() + .find(|attr| matches!(attr.meta, TaskAttrMeta::TaskWeight(_))) + .cloned() + else { + return Err(Error::new( + item.sig.ident.span(), + "missing `#[pallet::task_weight(..)]` attribute", + )) + }; + + if let Some(duplicate) = task_attrs + .iter() + .filter(|attr| matches!(attr.meta, TaskAttrMeta::TaskCondition(_))) + .collect::>() + .get(1) + { + return Err(Error::new( + duplicate.span(), + "unexpected extra `#[pallet::task_condition(..)]` attribute", + )) + } + + if let Some(duplicate) = task_attrs + .iter() + .filter(|attr| matches!(attr.meta, TaskAttrMeta::TaskList(_))) + .collect::>() + .get(1) + { + return Err(Error::new( + duplicate.span(), + "unexpected extra `#[pallet::task_list(..)]` attribute", + )) + } + + if let Some(duplicate) = task_attrs + .iter() + .filter(|attr| matches!(attr.meta, TaskAttrMeta::TaskIndex(_))) + .collect::>() + .get(1) + { + return Err(Error::new( + duplicate.span(), + "unexpected extra `#[pallet::task_index(..)]` attribute", + )) + } + + let mut arg_names = vec![]; + for input in item.sig.inputs.iter() { + match input { + syn::FnArg::Typed(pat_type) => match &*pat_type.pat { + syn::Pat::Ident(ident) => arg_names.push(ident.ident.clone()), + _ => return Err(Error::new(input.span(), "unexpected pattern type")), + }, + _ => return Err(Error::new(input.span(), "unexpected function argument type")), + } + } + + let index_attr = index_attr.try_into().expect("we check the type above; QED"); + let condition_attr = condition_attr.try_into().expect("we check the type above; QED"); + let list_attr = list_attr.try_into().expect("we check the type above; QED"); + let weight_attr = weight_attr.try_into().expect("we check the type above; QED"); + + Ok(TaskDef { + index_attr, + condition_attr, + list_attr, + weight_attr, + normal_attrs, + item, + arg_names, + }) + } +} + +/// The contents of a [`TasksDef`]-related attribute. +#[derive(Parse, Debug, Clone)] +pub enum TaskAttrMeta { + #[peek(keywords::task_list, name = "#[pallet::task_list(..)]")] + TaskList(TaskListAttrMeta), + #[peek(keywords::task_index, name = "#[pallet::task_index(..)")] + TaskIndex(TaskIndexAttrMeta), + #[peek(keywords::task_condition, name = "#[pallet::task_condition(..)")] + TaskCondition(TaskConditionAttrMeta), + #[peek(keywords::task_weight, name = "#[pallet::task_weight(..)")] + TaskWeight(TaskWeightAttrMeta), +} + +/// The contents of a `#[pallet::task_list]` attribute. +#[derive(Parse, Debug, Clone)] +pub struct TaskListAttrMeta { + pub task_list: keywords::task_list, + #[paren] + _paren: Paren, + #[inside(_paren)] + pub expr: Expr, +} + +/// The contents of a `#[pallet::task_index]` attribute. +#[derive(Parse, Debug, Clone)] +pub struct TaskIndexAttrMeta { + pub task_index: keywords::task_index, + #[paren] + _paren: Paren, + #[inside(_paren)] + pub index: LitInt, +} + +/// The contents of a `#[pallet::task_condition]` attribute. +#[derive(Parse, Debug, Clone)] +pub struct TaskConditionAttrMeta { + pub task_condition: keywords::task_condition, + #[paren] + _paren: Paren, + #[inside(_paren)] + pub expr: Expr, +} + +/// The contents of a `#[pallet::task_weight]` attribute. +#[derive(Parse, Debug, Clone)] +pub struct TaskWeightAttrMeta { + pub task_weight: keywords::task_weight, + #[paren] + _paren: Paren, + #[inside(_paren)] + pub expr: Expr, +} + +/// The contents of a `#[pallet::task]` attribute. +#[derive(Parse, Debug, Clone)] +pub struct PalletTaskAttr { + pub pound: Pound, + #[bracket] + _bracket: Bracket, + #[inside(_bracket)] + pub pallet: keywords::pallet, + #[inside(_bracket)] + pub colons: PathSep, + #[inside(_bracket)] + pub meta: T, +} + +impl ToTokens for TaskListAttrMeta { + fn to_tokens(&self, tokens: &mut TokenStream2) { + let task_list = self.task_list; + let expr = &self.expr; + tokens.extend(quote!(#task_list(#expr))); + } +} + +impl ToTokens for TaskConditionAttrMeta { + fn to_tokens(&self, tokens: &mut TokenStream2) { + let task_condition = self.task_condition; + let expr = &self.expr; + tokens.extend(quote!(#task_condition(#expr))); + } +} + +impl ToTokens for TaskWeightAttrMeta { + fn to_tokens(&self, tokens: &mut TokenStream2) { + let task_weight = self.task_weight; + let expr = &self.expr; + tokens.extend(quote!(#task_weight(#expr))); + } +} + +impl ToTokens for TaskIndexAttrMeta { + fn to_tokens(&self, tokens: &mut TokenStream2) { + let task_index = self.task_index; + let index = &self.index; + tokens.extend(quote!(#task_index(#index))) + } +} + +impl ToTokens for TaskAttrMeta { + fn to_tokens(&self, tokens: &mut TokenStream2) { + match self { + TaskAttrMeta::TaskList(list) => tokens.extend(list.to_token_stream()), + TaskAttrMeta::TaskIndex(index) => tokens.extend(index.to_token_stream()), + TaskAttrMeta::TaskCondition(condition) => tokens.extend(condition.to_token_stream()), + TaskAttrMeta::TaskWeight(weight) => tokens.extend(weight.to_token_stream()), + } + } +} + +impl ToTokens for PalletTaskAttr { + fn to_tokens(&self, tokens: &mut TokenStream2) { + let pound = self.pound; + let pallet = self.pallet; + let colons = self.colons; + let meta = &self.meta; + tokens.extend(quote!(#pound[#pallet #colons #meta])); + } +} + +impl TryFrom> for TaskIndexAttr { + type Error = syn::Error; + + fn try_from(value: PalletTaskAttr) -> Result { + let pound = value.pound; + let pallet = value.pallet; + let colons = value.colons; + match value.meta { + TaskAttrMeta::TaskIndex(meta) => parse2(quote!(#pound[#pallet #colons #meta])), + _ => + return Err(Error::new( + value.span(), + format!("`{:?}` cannot be converted to a `TaskIndexAttr`", value.meta), + )), + } + } +} + +impl TryFrom> for TaskConditionAttr { + type Error = syn::Error; + + fn try_from(value: PalletTaskAttr) -> Result { + let pound = value.pound; + let pallet = value.pallet; + let colons = value.colons; + match value.meta { + TaskAttrMeta::TaskCondition(meta) => parse2(quote!(#pound[#pallet #colons #meta])), + _ => + return Err(Error::new( + value.span(), + format!("`{:?}` cannot be converted to a `TaskConditionAttr`", value.meta), + )), + } + } +} + +impl TryFrom> for TaskWeightAttr { + type Error = syn::Error; + + fn try_from(value: PalletTaskAttr) -> Result { + let pound = value.pound; + let pallet = value.pallet; + let colons = value.colons; + match value.meta { + TaskAttrMeta::TaskWeight(meta) => parse2(quote!(#pound[#pallet #colons #meta])), + _ => + return Err(Error::new( + value.span(), + format!("`{:?}` cannot be converted to a `TaskWeightAttr`", value.meta), + )), + } + } +} + +impl TryFrom> for TaskListAttr { + type Error = syn::Error; + + fn try_from(value: PalletTaskAttr) -> Result { + let pound = value.pound; + let pallet = value.pallet; + let colons = value.colons; + match value.meta { + TaskAttrMeta::TaskList(meta) => parse2(quote!(#pound[#pallet #colons #meta])), + _ => + return Err(Error::new( + value.span(), + format!("`{:?}` cannot be converted to a `TaskListAttr`", value.meta), + )), + } + } +} + +fn extract_pallet_attr(item_enum: &mut ItemEnum) -> Result> { + let mut duplicate = None; + let mut attr = None; + item_enum.attrs = item_enum + .attrs + .iter() + .filter(|found_attr| { + let segs = found_attr + .path() + .segments + .iter() + .map(|seg| seg.ident.clone()) + .collect::>(); + let (Some(seg1), Some(_), None) = (segs.get(0), segs.get(1), segs.get(2)) else { + return true + }; + if seg1 != "pallet" { + return true + } + if attr.is_some() { + duplicate = Some(found_attr.span()); + } + attr = Some(found_attr.to_token_stream()); + false + }) + .cloned() + .collect(); + if let Some(span) = duplicate { + return Err(Error::new(span, "only one `#[pallet::_]` attribute is supported on this item")) + } + Ok(attr) +} + +fn partition_tasks_attrs(item_impl: &ItemImpl) -> (Vec, Vec) { + item_impl.attrs.clone().into_iter().partition(|attr| { + let mut path_segs = attr.path().segments.iter(); + let (Some(prefix), Some(suffix), None) = + (path_segs.next(), path_segs.next(), path_segs.next()) + else { + return false + }; + prefix.ident == "pallet" && suffix.ident == "tasks_experimental" + }) +} + +fn partition_task_attrs(item: &ImplItemFn) -> (Vec, Vec) { + item.attrs.clone().into_iter().partition(|attr| { + let mut path_segs = attr.path().segments.iter(); + let (Some(prefix), Some(suffix)) = (path_segs.next(), path_segs.next()) else { + return false + }; + // N.B: the `PartialEq` impl between `Ident` and `&str` is more efficient than + // parsing and makes no stack or heap allocations + prefix.ident == "pallet" && + (suffix.ident == "tasks_experimental" || + suffix.ident == "task_list" || + suffix.ident == "task_condition" || + suffix.ident == "task_weight" || + suffix.ident == "task_index") + }) +} + +#[test] +fn test_parse_task_list_() { + parse2::(quote!(#[pallet::task_list(Something::iter())])).unwrap(); + parse2::(quote!(#[pallet::task_list(Numbers::::iter_keys())])).unwrap(); + parse2::(quote!(#[pallet::task_list(iter())])).unwrap(); + assert_parse_error_matches!( + parse2::(quote!(#[pallet::task_list()])), + "expected an expression" + ); + assert_parse_error_matches!( + parse2::(quote!(#[pallet::task_list])), + "expected parentheses" + ); +} + +#[test] +fn test_parse_task_index() { + parse2::(quote!(#[pallet::task_index(3)])).unwrap(); + parse2::(quote!(#[pallet::task_index(0)])).unwrap(); + parse2::(quote!(#[pallet::task_index(17)])).unwrap(); + assert_parse_error_matches!( + parse2::(quote!(#[pallet::task_index])), + "expected parentheses" + ); + assert_parse_error_matches!( + parse2::(quote!(#[pallet::task_index("hey")])), + "expected integer literal" + ); + assert_parse_error_matches!( + parse2::(quote!(#[pallet::task_index(0.3)])), + "expected integer literal" + ); +} + +#[test] +fn test_parse_task_condition() { + parse2::(quote!(#[pallet::task_condition(|x| x.is_some())])).unwrap(); + parse2::(quote!(#[pallet::task_condition(|_x| some_expr())])).unwrap(); + parse2::(quote!(#[pallet::task_condition(|| some_expr())])).unwrap(); + parse2::(quote!(#[pallet::task_condition(some_expr())])).unwrap(); +} + +#[test] +fn test_parse_tasks_attr() { + parse2::(quote!(#[pallet::tasks_experimental])).unwrap(); + assert_parse_error_matches!( + parse2::(quote!(#[pallet::taskss])), + "expected `tasks_experimental`" + ); + assert_parse_error_matches!( + parse2::(quote!(#[pallet::tasks_])), + "expected `tasks_experimental`" + ); + assert_parse_error_matches!( + parse2::(quote!(#[pal::tasks])), + "expected `pallet`" + ); + assert_parse_error_matches!( + parse2::(quote!(#[pallet::tasks_experimental()])), + "unexpected token" + ); +} + +#[test] +fn test_parse_tasks_def_basic() { + simulate_manifest_dir("../../examples/basic", || { + let parsed = parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + /// Add a pair of numbers into the totals and remove them. + #[pallet::task_list(Numbers::::iter_keys())] + #[pallet::task_condition(|i| Numbers::::contains_key(i))] + #[pallet::task_index(0)] + #[pallet::task_weight(0)] + pub fn add_number_into_total(i: u32) -> DispatchResult { + let v = Numbers::::take(i).ok_or(Error::::NotFound)?; + Total::::mutate(|(total_keys, total_values)| { + *total_keys += i; + *total_values += v; + }); + Ok(()) + } + } + }) + .unwrap(); + assert_eq!(parsed.tasks.len(), 1); + }); +} + +#[test] +fn test_parse_tasks_def_basic_increment_decrement() { + simulate_manifest_dir("../../examples/basic", || { + let parsed = parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + /// Get the value and check if it can be incremented + #[pallet::task_index(0)] + #[pallet::task_condition(|| { + let value = Value::::get().unwrap(); + value < 255 + })] + #[pallet::task_list(Vec::>::new())] + #[pallet::task_weight(0)] + fn increment() -> DispatchResult { + let value = Value::::get().unwrap_or_default(); + if value >= 255 { + Err(Error::::ValueOverflow.into()) + } else { + let new_val = value.checked_add(1).ok_or(Error::::ValueOverflow)?; + Value::::put(new_val); + Pallet::::deposit_event(Event::Incremented { new_val }); + Ok(()) + } + } + + // Get the value and check if it can be decremented + #[pallet::task_index(1)] + #[pallet::task_condition(|| { + let value = Value::::get().unwrap(); + value > 0 + })] + #[pallet::task_list(Vec::>::new())] + #[pallet::task_weight(0)] + fn decrement() -> DispatchResult { + let value = Value::::get().unwrap_or_default(); + if value == 0 { + Err(Error::::ValueUnderflow.into()) + } else { + let new_val = value.checked_sub(1).ok_or(Error::::ValueUnderflow)?; + Value::::put(new_val); + Pallet::::deposit_event(Event::Decremented { new_val }); + Ok(()) + } + } + } + }) + .unwrap(); + assert_eq!(parsed.tasks.len(), 2); + }); +} + +#[test] +fn test_parse_tasks_def_duplicate_index() { + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + #[pallet::task_list(Something::iter())] + #[pallet::task_condition(|i| i % 2 == 0)] + #[pallet::task_index(0)] + #[pallet::task_weight(0)] + pub fn foo(i: u32) -> DispatchResult { + Ok(()) + } + + #[pallet::task_list(Numbers::::iter_keys())] + #[pallet::task_condition(|i| Numbers::::contains_key(i))] + #[pallet::task_index(0)] + #[pallet::task_weight(0)] + pub fn bar(i: u32) -> DispatchResult { + Ok(()) + } + } + }), + "duplicate task index `0`" + ); + }); +} + +#[test] +fn test_parse_tasks_def_missing_task_list() { + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + #[pallet::task_condition(|i| i % 2 == 0)] + #[pallet::task_index(0)] + pub fn foo(i: u32) -> DispatchResult { + Ok(()) + } + } + }), + r"missing `#\[pallet::task_list\(\.\.\)\]`" + ); + }); +} + +#[test] +fn test_parse_tasks_def_missing_task_condition() { + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + #[pallet::task_list(Something::iter())] + #[pallet::task_index(0)] + pub fn foo(i: u32) -> DispatchResult { + Ok(()) + } + } + }), + r"missing `#\[pallet::task_condition\(\.\.\)\]`" + ); + }); +} + +#[test] +fn test_parse_tasks_def_missing_task_index() { + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + #[pallet::task_condition(|i| i % 2 == 0)] + #[pallet::task_list(Something::iter())] + pub fn foo(i: u32) -> DispatchResult { + Ok(()) + } + } + }), + r"missing `#\[pallet::task_index\(\.\.\)\]`" + ); + }); +} + +#[test] +fn test_parse_tasks_def_missing_task_weight() { + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + #[pallet::task_condition(|i| i % 2 == 0)] + #[pallet::task_list(Something::iter())] + #[pallet::task_index(0)] + pub fn foo(i: u32) -> DispatchResult { + Ok(()) + } + } + }), + r"missing `#\[pallet::task_weight\(\.\.\)\]`" + ); + }); +} + +#[test] +fn test_parse_tasks_def_unexpected_extra_task_list_attr() { + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + #[pallet::task_condition(|i| i % 2 == 0)] + #[pallet::task_index(0)] + #[pallet::task_weight(0)] + #[pallet::task_list(Something::iter())] + #[pallet::task_list(SomethingElse::iter())] + pub fn foo(i: u32) -> DispatchResult { + Ok(()) + } + } + }), + r"unexpected extra `#\[pallet::task_list\(\.\.\)\]`" + ); + }); +} + +#[test] +fn test_parse_tasks_def_unexpected_extra_task_condition_attr() { + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + #[pallet::task_condition(|i| i % 2 == 0)] + #[pallet::task_condition(|i| i % 4 == 0)] + #[pallet::task_index(0)] + #[pallet::task_list(Something::iter())] + #[pallet::task_weight(0)] + pub fn foo(i: u32) -> DispatchResult { + Ok(()) + } + } + }), + r"unexpected extra `#\[pallet::task_condition\(\.\.\)\]`" + ); + }); +} + +#[test] +fn test_parse_tasks_def_unexpected_extra_task_index_attr() { + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + #[pallet::task_condition(|i| i % 2 == 0)] + #[pallet::task_index(0)] + #[pallet::task_index(0)] + #[pallet::task_list(Something::iter())] + #[pallet::task_weight(0)] + pub fn foo(i: u32) -> DispatchResult { + Ok(()) + } + } + }), + r"unexpected extra `#\[pallet::task_index\(\.\.\)\]`" + ); + }); +} + +#[test] +fn test_parse_tasks_def_extra_tasks_attribute() { + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + #[pallet::tasks_experimental] + impl, I: 'static> Pallet {} + }), + r"unexpected extra `#\[pallet::tasks_experimental\]` attribute" + ); + }); +} + +#[test] +fn test_parse_task_enum_def_basic() { + simulate_manifest_dir("../../examples/basic", || { + parse2::(quote! { + #[pallet::task_enum] + pub enum Task { + Increment, + Decrement, + } + }) + .unwrap(); + }); +} + +#[test] +fn test_parse_task_enum_def_non_task_name() { + simulate_manifest_dir("../../examples/basic", || { + parse2::(quote! { + #[pallet::task_enum] + pub enum Something { + Foo + } + }) + .unwrap(); + }); +} + +#[test] +fn test_parse_task_enum_def_missing_attr_allowed() { + simulate_manifest_dir("../../examples/basic", || { + parse2::(quote! { + pub enum Task { + Increment, + Decrement, + } + }) + .unwrap(); + }); +} + +#[test] +fn test_parse_task_enum_def_missing_attr_alternate_name_allowed() { + simulate_manifest_dir("../../examples/basic", || { + parse2::(quote! { + pub enum Foo { + Red, + } + }) + .unwrap(); + }); +} + +#[test] +fn test_parse_task_enum_def_wrong_attr() { + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::something] + pub enum Task { + Increment, + Decrement, + } + }), + "expected `task_enum`" + ); + }); +} + +#[test] +fn test_parse_task_enum_def_wrong_item() { + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::task_enum] + pub struct Something; + }), + "expected `enum`" + ); + }); +} diff --git a/support/procedural-fork/src/pallet/parse/tests/mod.rs b/support/procedural-fork/src/pallet/parse/tests/mod.rs new file mode 100644 index 000000000..a3661f307 --- /dev/null +++ b/support/procedural-fork/src/pallet/parse/tests/mod.rs @@ -0,0 +1,264 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::{panic, sync::Mutex}; +use syn::parse_quote; + +#[doc(hidden)] +pub mod __private { + pub use regex; +} + +/// Allows you to assert that the input expression resolves to an error whose string +/// representation matches the specified regex literal. +/// +/// ## Example: +/// +/// ``` +/// use super::tasks::*; +/// +/// assert_parse_error_matches!( +/// parse2::(quote! { +/// #[pallet::task_enum] +/// pub struct Something; +/// }), +/// "expected `enum`" +/// ); +/// ``` +/// +/// More complex regular expressions are also possible (anything that could pass as a regex for +/// use with the [`regex`] crate.): +/// +/// ```ignore +/// assert_parse_error_matches!( +/// parse2::(quote! { +/// #[pallet::tasks_experimental] +/// impl, I: 'static> Pallet { +/// #[pallet::task_condition(|i| i % 2 == 0)] +/// #[pallet::task_index(0)] +/// pub fn foo(i: u32) -> DispatchResult { +/// Ok(()) +/// } +/// } +/// }), +/// r"missing `#\[pallet::task_list\(\.\.\)\]`" +/// ); +/// ``` +/// +/// Although this is primarily intended to be used with parsing errors, this macro is general +/// enough that it will work with any error with a reasonable [`core::fmt::Display`] impl. +#[macro_export] +macro_rules! assert_parse_error_matches { + ($expr:expr, $reg:literal) => { + match $expr { + Ok(_) => panic!("Expected an `Error(..)`, but got Ok(..)"), + Err(e) => { + let error_message = e.to_string(); + let re = $crate::pallet::parse::tests::__private::regex::Regex::new($reg) + .expect("Invalid regex pattern"); + assert!( + re.is_match(&error_message), + "Error message \"{}\" does not match the pattern \"{}\"", + error_message, + $reg + ); + }, + } + }; +} + +/// Allows you to assert that an entire pallet parses successfully. A custom syntax is used for +/// specifying arguments so please pay attention to the docs below. +/// +/// The general syntax is: +/// +/// ```ignore +/// assert_pallet_parses! { +/// #[manifest_dir("../../examples/basic")] +/// #[frame_support::pallet] +/// pub mod pallet { +/// #[pallet::config] +/// pub trait Config: frame_system::Config {} +/// +/// #[pallet::pallet] +/// pub struct Pallet(_); +/// } +/// }; +/// ``` +/// +/// The `#[manifest_dir(..)]` attribute _must_ be specified as the _first_ attribute on the +/// pallet module, and should reference the relative (to your current directory) path of a +/// directory containing containing the `Cargo.toml` of a valid pallet. Typically you will only +/// ever need to use the `examples/basic` pallet, but sometimes it might be advantageous to +/// specify a different one that has additional dependencies. +/// +/// The reason this must be specified is that our underlying parsing of pallets depends on +/// reaching out into the file system to look for particular `Cargo.toml` dependencies via the +/// [`generate_access_from_frame_or_crate`] method, so to simulate this properly in a proc +/// macro crate, we need to temporarily convince this function that we are running from the +/// directory of a valid pallet. +#[macro_export] +macro_rules! assert_pallet_parses { + ( + #[manifest_dir($manifest_dir:literal)] + $($tokens:tt)* + ) => { + { + let mut pallet: Option<$crate::pallet::parse::Def> = None; + $crate::pallet::parse::tests::simulate_manifest_dir($manifest_dir, core::panic::AssertUnwindSafe(|| { + pallet = Some($crate::pallet::parse::Def::try_from(syn::parse_quote! { + $($tokens)* + }, false).unwrap()); + })); + pallet.unwrap() + } + } +} + +/// Similar to [`assert_pallet_parses`], except this instead expects the pallet not to parse, +/// and allows you to specify a regex matching the expected parse error. +/// +/// This is identical syntactically to [`assert_pallet_parses`] in every way except there is a +/// second attribute that must be specified immediately after `#[manifest_dir(..)]` which is +/// `#[error_regex(..)]` which should contain a string/regex literal designed to match what you +/// consider to be the correct parsing error we should see when we try to parse this particular +/// pallet. +/// +/// ## Example: +/// +/// ``` +/// assert_pallet_parse_error! { +/// #[manifest_dir("../../examples/basic")] +/// #[error_regex("Missing `\\#\\[pallet::pallet\\]`")] +/// #[frame_support::pallet] +/// pub mod pallet { +/// #[pallet::config] +/// pub trait Config: frame_system::Config {} +/// } +/// } +/// ``` +#[macro_export] +macro_rules! assert_pallet_parse_error { + ( + #[manifest_dir($manifest_dir:literal)] + #[error_regex($reg:literal)] + $($tokens:tt)* + ) => { + $crate::pallet::parse::tests::simulate_manifest_dir($manifest_dir, || { + $crate::assert_parse_error_matches!( + $crate::pallet::parse::Def::try_from( + parse_quote! { + $($tokens)* + }, + false + ), + $reg + ); + }); + } +} + +/// Safely runs the specified `closure` while simulating an alternative `CARGO_MANIFEST_DIR`, +/// restoring `CARGO_MANIFEST_DIR` to its original value upon completion regardless of whether +/// the closure panics. +/// +/// This is useful in tests of `Def::try_from` and other pallet-related methods that internally +/// make use of [`generate_access_from_frame_or_crate`], which is sensitive to entries in the +/// "current" `Cargo.toml` files. +/// +/// This function uses a [`Mutex`] to avoid a race condition created when multiple tests try to +/// modify and then restore the `CARGO_MANIFEST_DIR` ENV var in an overlapping way. +pub fn simulate_manifest_dir, F: FnOnce() + std::panic::UnwindSafe>( + path: P, + closure: F, +) { + use std::{env::*, path::*}; + + /// Ensures that only one thread can modify/restore the `CARGO_MANIFEST_DIR` ENV var at a time, + /// avoiding a race condition because `cargo test` runs tests in parallel. + /// + /// Although this forces all tests that use [`simulate_manifest_dir`] to run sequentially with + /// respect to each other, this is still several orders of magnitude faster than using UI + /// tests, even if they are run in parallel. + static MANIFEST_DIR_LOCK: Mutex<()> = Mutex::new(()); + + // avoid race condition when swapping out `CARGO_MANIFEST_DIR` + let guard = MANIFEST_DIR_LOCK.lock().unwrap(); + + // obtain the current/original `CARGO_MANIFEST_DIR` + let orig = PathBuf::from( + var("CARGO_MANIFEST_DIR").expect("failed to read ENV var `CARGO_MANIFEST_DIR`"), + ); + + // set `CARGO_MANIFEST_DIR` to the provided path, relative to current working dir + set_var("CARGO_MANIFEST_DIR", orig.join(path.as_ref())); + + // safely run closure catching any panics + let result = panic::catch_unwind(closure); + + // restore original `CARGO_MANIFEST_DIR` before unwinding + set_var("CARGO_MANIFEST_DIR", &orig); + + // unlock the mutex so we don't poison it if there is a panic + drop(guard); + + // unwind any panics originally encountered when running closure + result.unwrap(); +} + +mod tasks; + +#[test] +fn test_parse_minimal_pallet() { + assert_pallet_parses! { + #[manifest_dir("../../examples/basic")] + #[frame_support::pallet] + pub mod pallet { + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + }; +} + +#[test] +fn test_parse_pallet_missing_pallet() { + assert_pallet_parse_error! { + #[manifest_dir("../../examples/basic")] + #[error_regex("Missing `\\#\\[pallet::pallet\\]`")] + #[frame_support::pallet] + pub mod pallet { + #[pallet::config] + pub trait Config: frame_system::Config {} + } + } +} + +#[test] +fn test_parse_pallet_missing_config() { + assert_pallet_parse_error! { + #[manifest_dir("../../examples/basic")] + #[error_regex("Missing `\\#\\[pallet::config\\]`")] + #[frame_support::pallet] + pub mod pallet { + #[pallet::pallet] + pub struct Pallet(_); + } + } +} diff --git a/support/procedural-fork/src/pallet/parse/tests/tasks.rs b/support/procedural-fork/src/pallet/parse/tests/tasks.rs new file mode 100644 index 000000000..9f1436284 --- /dev/null +++ b/support/procedural-fork/src/pallet/parse/tests/tasks.rs @@ -0,0 +1,240 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use syn::parse_quote; + +#[test] +fn test_parse_pallet_with_task_enum_missing_impl() { + assert_pallet_parse_error! { + #[manifest_dir("../../examples/basic")] + #[error_regex("Missing `\\#\\[pallet::tasks_experimental\\]` impl")] + #[frame_support::pallet] + pub mod pallet { + #[pallet::task_enum] + pub enum Task { + Something, + } + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + } +} + +#[test] +fn test_parse_pallet_with_task_enum_wrong_attribute() { + assert_pallet_parse_error! { + #[manifest_dir("../../examples/basic")] + #[error_regex("expected one of")] + #[frame_support::pallet] + pub mod pallet { + #[pallet::wrong_attribute] + pub enum Task { + Something, + } + + #[pallet::task_list] + impl frame_support::traits::Task for Task + where + T: TypeInfo, + {} + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + } +} + +#[test] +fn test_parse_pallet_missing_task_enum() { + assert_pallet_parses! { + #[manifest_dir("../../examples/basic")] + #[frame_support::pallet] + pub mod pallet { + #[pallet::tasks_experimental] + #[cfg(test)] // aha, this means it's being eaten + impl frame_support::traits::Task for Task + where + T: TypeInfo, + {} + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + }; +} + +#[test] +fn test_parse_pallet_task_list_in_wrong_place() { + assert_pallet_parse_error! { + #[manifest_dir("../../examples/basic")] + #[error_regex("can only be used on items within an `impl` statement.")] + #[frame_support::pallet] + pub mod pallet { + pub enum MyCustomTaskEnum { + Something, + } + + #[pallet::task_list] + pub fn something() { + println!("hey"); + } + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + } +} + +#[test] +fn test_parse_pallet_manual_tasks_impl_without_manual_tasks_enum() { + assert_pallet_parse_error! { + #[manifest_dir("../../examples/basic")] + #[error_regex(".*attribute must be attached to your.*")] + #[frame_support::pallet] + pub mod pallet { + + impl frame_support::traits::Task for Task + where + T: TypeInfo, + { + type Enumeration = sp_std::vec::IntoIter>; + + fn iter() -> Self::Enumeration { + sp_std::vec![Task::increment, Task::decrement].into_iter() + } + } + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + } +} + +#[test] +fn test_parse_pallet_manual_task_enum_non_manual_impl() { + assert_pallet_parses! { + #[manifest_dir("../../examples/basic")] + #[frame_support::pallet] + pub mod pallet { + pub enum MyCustomTaskEnum { + Something, + } + + #[pallet::tasks_experimental] + impl frame_support::traits::Task for MyCustomTaskEnum + where + T: TypeInfo, + {} + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + }; +} + +#[test] +fn test_parse_pallet_non_manual_task_enum_manual_impl() { + assert_pallet_parses! { + #[manifest_dir("../../examples/basic")] + #[frame_support::pallet] + pub mod pallet { + #[pallet::task_enum] + pub enum MyCustomTaskEnum { + Something, + } + + impl frame_support::traits::Task for MyCustomTaskEnum + where + T: TypeInfo, + {} + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + }; +} + +#[test] +fn test_parse_pallet_manual_task_enum_manual_impl() { + assert_pallet_parses! { + #[manifest_dir("../../examples/basic")] + #[frame_support::pallet] + pub mod pallet { + pub enum MyCustomTaskEnum { + Something, + } + + impl frame_support::traits::Task for MyCustomTaskEnum + where + T: TypeInfo, + {} + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + }; +} + +#[test] +fn test_parse_pallet_manual_task_enum_mismatch_ident() { + assert_pallet_parses! { + #[manifest_dir("../../examples/basic")] + #[frame_support::pallet] + pub mod pallet { + pub enum WrongIdent { + Something, + } + + #[pallet::tasks_experimental] + impl frame_support::traits::Task for MyCustomTaskEnum + where + T: TypeInfo, + {} + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + }; +} diff --git a/support/procedural-fork/src/pallet/parse/type_value.rs b/support/procedural-fork/src/pallet/parse/type_value.rs new file mode 100644 index 000000000..4d9db30b3 --- /dev/null +++ b/support/procedural-fork/src/pallet/parse/type_value.rs @@ -0,0 +1,123 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::helper; +use syn::spanned::Spanned; + +/// Definition of type value. Just a function which is expanded to a struct implementing `Get`. +pub struct TypeValueDef { + /// The index of error item in pallet module. + pub index: usize, + /// Visibility of the struct to generate. + pub vis: syn::Visibility, + /// Ident of the struct to generate. + pub ident: syn::Ident, + /// The type return by Get. + pub type_: Box, + /// The block returning the value to get + pub block: Box, + /// If type value is generic over `T` (or `T` and `I` for instantiable pallet) + pub is_generic: bool, + /// A set of usage of instance, must be check for consistency with config. + pub instances: Vec, + /// The where clause of the function. + pub where_clause: Option, + /// The span of the pallet::type_value attribute. + pub attr_span: proc_macro2::Span, + /// Docs on the item. + pub docs: Vec, +} + +impl TypeValueDef { + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + ) -> syn::Result { + let item = if let syn::Item::Fn(item) = item { + item + } else { + let msg = "Invalid pallet::type_value, expected item fn"; + return Err(syn::Error::new(item.span(), msg)) + }; + + let mut docs = vec![]; + for attr in &item.attrs { + if let syn::Meta::NameValue(meta) = &attr.meta { + if meta.path.get_ident().map_or(false, |ident| ident == "doc") { + docs.push(meta.value.clone()); + continue + } + } + + let msg = "Invalid pallet::type_value, unexpected attribute, only doc attribute are \ + allowed"; + return Err(syn::Error::new(attr.span(), msg)) + } + + if let Some(span) = item + .sig + .constness + .as_ref() + .map(|t| t.span()) + .or_else(|| item.sig.asyncness.as_ref().map(|t| t.span())) + .or_else(|| item.sig.unsafety.as_ref().map(|t| t.span())) + .or_else(|| item.sig.abi.as_ref().map(|t| t.span())) + .or_else(|| item.sig.variadic.as_ref().map(|t| t.span())) + { + let msg = "Invalid pallet::type_value, unexpected token"; + return Err(syn::Error::new(span, msg)) + } + + if !item.sig.inputs.is_empty() { + let msg = "Invalid pallet::type_value, unexpected argument"; + return Err(syn::Error::new(item.sig.inputs[0].span(), msg)) + } + + let vis = item.vis.clone(); + let ident = item.sig.ident.clone(); + let block = item.block.clone(); + let type_ = match item.sig.output.clone() { + syn::ReturnType::Type(_, type_) => type_, + syn::ReturnType::Default => { + let msg = "Invalid pallet::type_value, expected return type"; + return Err(syn::Error::new(item.sig.span(), msg)) + }, + }; + + let mut instances = vec![]; + if let Some(usage) = helper::check_type_value_gen(&item.sig.generics, item.sig.span())? { + instances.push(usage); + } + + let is_generic = item.sig.generics.type_params().count() > 0; + let where_clause = item.sig.generics.where_clause.clone(); + + Ok(TypeValueDef { + attr_span, + index, + is_generic, + vis, + ident, + block, + type_, + instances, + where_clause, + docs, + }) + } +} diff --git a/support/procedural-fork/src/pallet/parse/validate_unsigned.rs b/support/procedural-fork/src/pallet/parse/validate_unsigned.rs new file mode 100644 index 000000000..2bf0a1b6c --- /dev/null +++ b/support/procedural-fork/src/pallet/parse/validate_unsigned.rs @@ -0,0 +1,62 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::helper; +use syn::spanned::Spanned; + +/// The definition of the pallet validate unsigned implementation. +pub struct ValidateUnsignedDef { + /// The index of validate unsigned item in pallet module. + pub index: usize, + /// A set of usage of instance, must be check for consistency with config. + pub instances: Vec, +} + +impl ValidateUnsignedDef { + pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { + let item = if let syn::Item::Impl(item) = item { + item + } else { + let msg = "Invalid pallet::validate_unsigned, expected item impl"; + return Err(syn::Error::new(item.span(), msg)) + }; + + if item.trait_.is_none() { + let msg = "Invalid pallet::validate_unsigned, expected impl<..> ValidateUnsigned for \ + Pallet<..>"; + return Err(syn::Error::new(item.span(), msg)) + } + + if let Some(last) = item.trait_.as_ref().unwrap().1.segments.last() { + if last.ident != "ValidateUnsigned" { + let msg = "Invalid pallet::validate_unsigned, expected trait ValidateUnsigned"; + return Err(syn::Error::new(last.span(), msg)) + } + } else { + let msg = "Invalid pallet::validate_unsigned, expected impl<..> ValidateUnsigned for \ + Pallet<..>"; + return Err(syn::Error::new(item.span(), msg)) + } + + let instances = vec![ + helper::check_pallet_struct_usage(&item.self_ty)?, + helper::check_impl_gen(&item.generics, item.impl_token.span())?, + ]; + + Ok(ValidateUnsignedDef { index, instances }) + } +} diff --git a/support/procedural-fork/src/pallet_error.rs b/support/procedural-fork/src/pallet_error.rs new file mode 100644 index 000000000..693a1e982 --- /dev/null +++ b/support/procedural-fork/src/pallet_error.rs @@ -0,0 +1,178 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use frame_support_procedural_tools::generate_access_from_frame_or_crate; +use quote::ToTokens; + +// Derive `PalletError` +pub fn derive_pallet_error(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let syn::DeriveInput { ident: name, generics, data, .. } = match syn::parse(input) { + Ok(input) => input, + Err(e) => return e.to_compile_error().into(), + }; + + let frame_support = match generate_access_from_frame_or_crate("frame-support") { + Ok(c) => c, + Err(e) => return e.into_compile_error().into(), + }; + let frame_support = &frame_support; + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + let max_encoded_size = match data { + syn::Data::Struct(syn::DataStruct { fields, .. }) => match fields { + syn::Fields::Named(syn::FieldsNamed { named: fields, .. }) | + syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed: fields, .. }) => { + let maybe_field_tys = fields + .iter() + .map(|f| generate_field_types(f, &frame_support)) + .collect::>>(); + let field_tys = match maybe_field_tys { + Ok(tys) => tys.into_iter().flatten(), + Err(e) => return e.into_compile_error().into(), + }; + quote::quote! { + 0_usize + #( + .saturating_add(< + #field_tys as #frame_support::traits::PalletError + >::MAX_ENCODED_SIZE) + )* + } + }, + syn::Fields::Unit => quote::quote!(0), + }, + syn::Data::Enum(syn::DataEnum { variants, .. }) => { + let field_tys = variants + .iter() + .map(|variant| generate_variant_field_types(variant, &frame_support)) + .collect::>>, syn::Error>>(); + + let field_tys = match field_tys { + Ok(tys) => tys.into_iter().flatten().collect::>(), + Err(e) => return e.to_compile_error().into(), + }; + + // We start with `1`, because the discriminant of an enum is stored as u8 + if field_tys.is_empty() { + quote::quote!(1) + } else { + let variant_sizes = field_tys.into_iter().map(|variant_field_tys| { + quote::quote! { + 1_usize + #(.saturating_add(< + #variant_field_tys as #frame_support::traits::PalletError + >::MAX_ENCODED_SIZE))* + } + }); + + quote::quote! {{ + let mut size = 1_usize; + let mut tmp = 0_usize; + #( + tmp = #variant_sizes; + size = if tmp > size { tmp } else { size }; + tmp = 0_usize; + )* + size + }} + } + }, + syn::Data::Union(syn::DataUnion { union_token, .. }) => { + let msg = "Cannot derive `PalletError` for union; please implement it directly"; + return syn::Error::new(union_token.span, msg).into_compile_error().into() + }, + }; + + quote::quote!( + const _: () = { + impl #impl_generics #frame_support::traits::PalletError + for #name #ty_generics #where_clause + { + const MAX_ENCODED_SIZE: usize = #max_encoded_size; + } + }; + ) + .into() +} + +fn generate_field_types( + field: &syn::Field, + scrate: &syn::Path, +) -> syn::Result> { + let attrs = &field.attrs; + + for attr in attrs { + if attr.path().is_ident("codec") { + let mut res = None; + + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("skip") { + res = Some(None); + } else if meta.path.is_ident("compact") { + let field_ty = &field.ty; + res = Some(Some(quote::quote!(#scrate::__private::codec::Compact<#field_ty>))); + } else if meta.path.is_ident("compact") { + res = Some(Some(meta.value()?.parse()?)); + } + + Ok(()) + })?; + + if let Some(v) = res { + return Ok(v) + } + } + } + + Ok(Some(field.ty.to_token_stream())) +} + +fn generate_variant_field_types( + variant: &syn::Variant, + scrate: &syn::Path, +) -> syn::Result>> { + let attrs = &variant.attrs; + + for attr in attrs { + if attr.path().is_ident("codec") { + let mut skip = false; + + // We ignore the error intentionally as this isn't `codec(skip)` when + // `parse_nested_meta` fails. + let _ = attr.parse_nested_meta(|meta| { + skip = meta.path.is_ident("skip"); + Ok(()) + }); + + if skip { + return Ok(None) + } + } + } + + match &variant.fields { + syn::Fields::Named(syn::FieldsNamed { named: fields, .. }) | + syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed: fields, .. }) => { + let field_tys = fields + .iter() + .map(|field| generate_field_types(field, scrate)) + .collect::>>()?; + Ok(Some(field_tys.into_iter().flatten().collect())) + }, + syn::Fields::Unit => Ok(None), + } +} diff --git a/support/procedural-fork/src/runtime/expand/mod.rs b/support/procedural-fork/src/runtime/expand/mod.rs new file mode 100644 index 000000000..93c88fce9 --- /dev/null +++ b/support/procedural-fork/src/runtime/expand/mod.rs @@ -0,0 +1,320 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::parse::runtime_types::RuntimeType; +use crate::{ + construct_runtime::{ + check_pallet_number, decl_all_pallets, decl_integrity_test, decl_pallet_runtime_setup, + decl_static_assertions, expand, + }, + runtime::{ + parse::{ + AllPalletsDeclaration, ExplicitAllPalletsDeclaration, ImplicitAllPalletsDeclaration, + }, + Def, + }, +}; +use cfg_expr::Predicate; +use frame_support_procedural_tools::{ + generate_access_from_frame_or_crate, generate_crate_access, generate_hidden_includes, +}; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; +use std::collections::HashSet; +use syn::{Ident, Result}; + +/// The fixed name of the system pallet. +const SYSTEM_PALLET_NAME: &str = "System"; + +pub fn expand(def: Def, legacy_ordering: bool) -> TokenStream2 { + let input = def.input; + + let (check_pallet_number_res, res) = match def.pallets { + AllPalletsDeclaration::Implicit(ref decl) => ( + check_pallet_number(input.clone(), decl.pallet_count), + construct_runtime_implicit_to_explicit(input.into(), decl.clone(), legacy_ordering), + ), + AllPalletsDeclaration::Explicit(ref decl) => ( + check_pallet_number(input, decl.pallets.len()), + construct_runtime_final_expansion( + def.runtime_struct.ident.clone(), + decl.clone(), + def.runtime_types.clone(), + legacy_ordering, + ), + ), + }; + + let res = res.unwrap_or_else(|e| e.to_compile_error()); + + // We want to provide better error messages to the user and thus, handle the error here + // separately. If there is an error, we print the error and still generate all of the code to + // get in overall less errors for the user. + let res = if let Err(error) = check_pallet_number_res { + let error = error.to_compile_error(); + + quote! { + #error + + #res + } + } else { + res + }; + + let res = expander::Expander::new("construct_runtime") + .dry(std::env::var("FRAME_EXPAND").is_err()) + .verbose(true) + .write_to_out_dir(res) + .expect("Does not fail because of IO in OUT_DIR; qed"); + + res.into() +} + +fn construct_runtime_implicit_to_explicit( + input: TokenStream2, + definition: ImplicitAllPalletsDeclaration, + legacy_ordering: bool, +) -> Result { + let frame_support = generate_access_from_frame_or_crate("frame-support")?; + let attr = if legacy_ordering { quote!((legacy_ordering)) } else { quote!() }; + let mut expansion = quote::quote!( + #[frame_support::runtime #attr] + #input + ); + for pallet in definition.pallet_decls.iter() { + let pallet_path = &pallet.path; + let pallet_name = &pallet.name; + let pallet_instance = pallet.instance.as_ref().map(|instance| quote::quote!(<#instance>)); + expansion = quote::quote!( + #frame_support::__private::tt_call! { + macro = [{ #pallet_path::tt_default_parts_v2 }] + frame_support = [{ #frame_support }] + ~~> #frame_support::match_and_insert! { + target = [{ #expansion }] + pattern = [{ #pallet_name = #pallet_path #pallet_instance }] + } + } + ); + } + + Ok(expansion) +} + +fn construct_runtime_final_expansion( + name: Ident, + definition: ExplicitAllPalletsDeclaration, + runtime_types: Vec, + legacy_ordering: bool, +) -> Result { + let ExplicitAllPalletsDeclaration { mut pallets, name: pallets_name } = definition; + + if !legacy_ordering { + // Ensure that order of hooks is based on the pallet index + pallets.sort_by_key(|p| p.index); + } + + let system_pallet = + pallets.iter().find(|decl| decl.name == SYSTEM_PALLET_NAME).ok_or_else(|| { + syn::Error::new( + pallets_name.span(), + "`System` pallet declaration is missing. \ + Please add this line: `pub type System = frame_system;`", + ) + })?; + if !system_pallet.cfg_pattern.is_empty() { + return Err(syn::Error::new( + system_pallet.name.span(), + "`System` pallet declaration is feature gated, please remove any `#[cfg]` attributes", + )) + } + + let features = pallets + .iter() + .filter_map(|decl| { + (!decl.cfg_pattern.is_empty()).then(|| { + decl.cfg_pattern.iter().flat_map(|attr| { + attr.predicates().filter_map(|pred| match pred { + Predicate::Feature(feat) => Some(feat), + Predicate::Test => Some("test"), + _ => None, + }) + }) + }) + }) + .flatten() + .collect::>(); + + 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 frame_system = generate_access_from_frame_or_crate("frame-system")?; + let block = quote!(<#name as #frame_system::Config>::Block); + let unchecked_extrinsic = quote!(<#block as #scrate::sp_runtime::traits::Block>::Extrinsic); + + let mut dispatch = None; + let mut outer_event = None; + let mut outer_error = None; + let mut outer_origin = None; + let mut freeze_reason = None; + let mut hold_reason = None; + let mut slash_reason = None; + let mut lock_id = None; + let mut task = None; + + for runtime_type in runtime_types.iter() { + match runtime_type { + RuntimeType::RuntimeCall(_) => { + dispatch = + Some(expand::expand_outer_dispatch(&name, system_pallet, &pallets, &scrate)); + }, + RuntimeType::RuntimeEvent(_) => { + outer_event = Some(expand::expand_outer_enum( + &name, + &pallets, + &scrate, + expand::OuterEnumType::Event, + )?); + }, + RuntimeType::RuntimeError(_) => { + outer_error = Some(expand::expand_outer_enum( + &name, + &pallets, + &scrate, + expand::OuterEnumType::Error, + )?); + }, + RuntimeType::RuntimeOrigin(_) => { + outer_origin = + Some(expand::expand_outer_origin(&name, system_pallet, &pallets, &scrate)?); + }, + RuntimeType::RuntimeFreezeReason(_) => { + freeze_reason = Some(expand::expand_outer_freeze_reason(&pallets, &scrate)); + }, + RuntimeType::RuntimeHoldReason(_) => { + hold_reason = Some(expand::expand_outer_hold_reason(&pallets, &scrate)); + }, + RuntimeType::RuntimeSlashReason(_) => { + slash_reason = Some(expand::expand_outer_slash_reason(&pallets, &scrate)); + }, + RuntimeType::RuntimeLockId(_) => { + lock_id = Some(expand::expand_outer_lock_id(&pallets, &scrate)); + }, + RuntimeType::RuntimeTask(_) => { + task = Some(expand::expand_outer_task(&name, &pallets, &scrate)); + }, + } + } + + let all_pallets = decl_all_pallets(&name, pallets.iter(), &features); + let pallet_to_index = decl_pallet_runtime_setup(&name, &pallets, &scrate); + + let metadata = expand::expand_runtime_metadata( + &name, + &pallets, + &scrate, + &unchecked_extrinsic, + &system_pallet.path, + ); + let outer_config = expand::expand_outer_config(&name, &pallets, &scrate); + let inherent = + expand::expand_outer_inherent(&name, &block, &unchecked_extrinsic, &pallets, &scrate); + let validate_unsigned = expand::expand_outer_validate_unsigned(&name, &pallets, &scrate); + let integrity_test = decl_integrity_test(&scrate); + let static_assertions = decl_static_assertions(&name, &pallets, &scrate); + + let res = quote!( + #scrate_decl + + // Prevent UncheckedExtrinsic to print unused warning. + const _: () = { + #[allow(unused)] + type __hidden_use_of_unchecked_extrinsic = #unchecked_extrinsic; + }; + + #[derive( + Clone, Copy, PartialEq, Eq, #scrate::sp_runtime::RuntimeDebug, + #scrate::__private::scale_info::TypeInfo + )] + pub struct #name; + impl #scrate::sp_runtime::traits::GetRuntimeBlockType for #name { + type RuntimeBlock = #block; + } + + // Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata. + // The function is implemented by calling `impl_runtime_apis!`. + // + // However, the `runtime` may be used without calling `impl_runtime_apis!`. + // Rely on the `Deref` trait to differentiate between a runtime that implements + // APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro runtime). + // + // Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function. + // `InternalConstructRuntime` is implemented by the `runtime` for Runtime references (`& Runtime`), + // while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`). + // + // Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!` + // when both macros are called; and will resolve an empty `runtime_metadata` when only the `runtime` + // is used. + + #[doc(hidden)] + trait InternalConstructRuntime { + #[inline(always)] + fn runtime_metadata(&self) -> #scrate::__private::sp_std::vec::Vec<#scrate::__private::metadata_ir::RuntimeApiMetadataIR> { + Default::default() + } + } + #[doc(hidden)] + impl InternalConstructRuntime for &#name {} + + #outer_event + + #outer_error + + #outer_origin + + #all_pallets + + #pallet_to_index + + #dispatch + + #task + + #metadata + + #outer_config + + #inherent + + #validate_unsigned + + #freeze_reason + + #hold_reason + + #lock_id + + #slash_reason + + #integrity_test + + #static_assertions + ); + + Ok(res) +} diff --git a/support/procedural-fork/src/runtime/mod.rs b/support/procedural-fork/src/runtime/mod.rs new file mode 100644 index 000000000..aaae579eb --- /dev/null +++ b/support/procedural-fork/src/runtime/mod.rs @@ -0,0 +1,236 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Implementation of `runtime`. +//! +//! `runtime` implementation is recursive and can generate code which will call itself +//! in order to get all the pallet parts for each pallet. +//! +//! Pallets can define their parts: +//! - Implicitly: `pub type System = frame_system;` +//! - Explicitly: `pub type System = frame_system + Pallet + Call;` +//! +//! The `runtime` transitions from the implicit definition to the explicit one. +//! From the explicit state, Substrate expands the pallets with additional information +//! that is to be included in the runtime metadata. +//! +//! Pallets must provide the `tt_default_parts_v2` macro for these transitions. +//! These are automatically implemented by the `#[pallet::pallet]` macro. +//! +//! This macro also generates the following enums for ease of decoding if the respective type +//! is defined inside `#[runtime::derive]`: +//! - `enum RuntimeCall`: This type contains the information needed to decode extrinsics. +//! - `enum RuntimeEvent`: This type contains the information needed to decode events. +//! - `enum RuntimeError`: While this cannot be used directly to decode `sp_runtime::DispatchError` +//! from the chain, it contains the information needed to decode the +//! `sp_runtime::DispatchError::Module`. +//! +//! # State Transitions +//! +//! ```ignore +//! +----------+ +//! | Implicit | +//! +----------+ +//! | +//! v +//! +----------+ +//! | Explicit | +//! +----------+ +//! ``` +//! +//! The `runtime` macro transforms the implicit declaration of each pallet +//! `System: frame_system` to an explicit one `System: frame_system + Pallet + Call` using the +//! `tt_default_parts_v2` macro. +//! +//! The `tt_default_parts_v2` macro exposes a plus separated list of pallet parts. For example, the +//! `Event` part is exposed only if the pallet implements an event via `#[pallet::event]` macro. +//! The tokens generated by this macro are `+ Pallet + Call` for our example. +//! +//! The `match_and_insert` macro takes in 3 arguments: +//! - target: This is the `TokenStream` that contains the `runtime` macro. +//! - pattern: The pattern to match against in the target stream. +//! - tokens: The tokens to added after the pattern match. +//! +//! The `runtime` macro uses the `tt_call` to get the default pallet parts via +//! the `tt_default_parts_v2` macro defined by each pallet. The pallet parts are then returned as +//! input to the `match_and_replace` macro. +//! The `match_and_replace` then will modify the `runtime` to expand the implicit +//! definition to the explicit one. +//! +//! For example, +//! +//! ```ignore +//! #[frame_support::runtime] +//! mod runtime { +//! //... +//! +//! #[runtime::pallet_index(0)] +//! pub type System = frame_system; // Implicit definition of parts +//! +//! #[runtime::pallet_index(1)] +//! pub type Balances = pallet_balances; // Implicit definition of parts +//! } +//! ``` +//! This call has some implicit pallet parts, thus it will expand to: +//! ```ignore +//! frame_support::__private::tt_call! { +//! macro = [{ pallet_balances::tt_default_parts_v2 }] +//! ~~> frame_support::match_and_insert! { +//! target = [{ +//! frame_support::__private::tt_call! { +//! macro = [{ frame_system::tt_default_parts_v2 }] +//! ~~> frame_support::match_and_insert! { +//! target = [{ +//! #[frame_support::runtime] +//! mod runtime { +//! //... +//! +//! #[runtime::pallet_index(0)] +//! pub type System = frame_system; +//! +//! #[runtime::pallet_index(1)] +//! pub type Balances = pallet_balances; +//! } +//! }] +//! pattern = [{ System = frame_system }] +//! } +//! } +//! }] +//! pattern = [{ Balances = pallet_balances }] +//! } +//! } +//! ``` +//! `tt_default_parts_v2` must be defined. It returns the pallet parts inside some tokens, and +//! then `tt_call` will pipe the returned pallet parts into the input of `match_and_insert`. +//! Thus `match_and_insert` will initially receive the following inputs: +//! ```ignore +//! frame_support::match_and_insert! { +//! target = [{ +//! frame_support::match_and_insert! { +//! target = [{ +//! #[frame_support::runtime] +//! mod runtime { +//! //... +//! +//! #[runtime::pallet_index(0)] +//! pub type System = frame_system; +//! +//! #[runtime::pallet_index(1)] +//! pub type Balances = pallet_balances; +//! } +//! }] +//! pattern = [{ System = frame_system }] +//! tokens = [{ ::{+ Pallet + Call} }] +//! } +//! }] +//! pattern = [{ Balances = pallet_balances }] +//! tokens = [{ ::{+ Pallet + Call} }] +//! } +//! ``` +//! After dealing with `pallet_balances`, the inner `match_and_insert` will expand to: +//! ```ignore +//! frame_support::match_and_insert! { +//! target = [{ +//! #[frame_support::runtime] +//! mod runtime { +//! //... +//! +//! #[runtime::pallet_index(0)] +//! pub type System = frame_system; // Implicit definition of parts +//! +//! #[runtime::pallet_index(1)] +//! pub type Balances = pallet_balances + Pallet + Call; // Explicit definition of parts +//! } +//! }] +//! pattern = [{ System = frame_system }] +//! tokens = [{ ::{+ Pallet + Call} }] +//! } +//! ``` +//! +//! Which will then finally expand to the following: +//! ```ignore +//! #[frame_support::runtime] +//! mod runtime { +//! //... +//! +//! #[runtime::pallet_index(0)] +//! pub type System = frame_system + Pallet + Call; +//! +//! #[runtime::pallet_index(1)] +//! pub type Balances = pallet_balances + Pallet + Call; +//! } +//! ``` +//! +//! This call has no implicit pallet parts, thus it will expand to the runtime construction: +//! ```ignore +//! pub struct Runtime { ... } +//! pub struct Call { ... } +//! impl Call ... +//! pub enum Origin { ... } +//! ... +//! ``` +//! +//! Visualizing the entire flow of `#[frame_support::runtime]`, it would look like the following: +//! +//! ```ignore +//! +----------------------+ +------------------------+ +-------------------+ +//! | | | (defined in pallet) | | | +//! | runtime | --> | tt_default_parts_v2! | --> | match_and_insert! | +//! | w/ no pallet parts | | | | | +//! +----------------------+ +------------------------+ +-------------------+ +//! +//! +----------------------+ +//! | | +//! --> | runtime | +//! | w/ pallet parts | +//! +----------------------+ +//! ``` + +#![cfg(feature = "experimental")] + +pub use parse::Def; +use proc_macro::TokenStream; +use syn::spanned::Spanned; + +mod expand; +mod parse; + +mod keyword { + syn::custom_keyword!(legacy_ordering); +} + +pub fn runtime(attr: TokenStream, tokens: TokenStream) -> TokenStream { + let mut legacy_ordering = false; + if !attr.is_empty() { + if let Ok(_) = syn::parse::(attr.clone()) { + legacy_ordering = true; + } else { + let msg = "Invalid runtime macro call: unexpected attribute. Macro call must be \ + bare, such as `#[frame_support::runtime]` or `#[runtime]`, or must specify the \ + `legacy_ordering` attribute, such as `#[frame_support::runtime(legacy_ordering)]` or \ + #[runtime(legacy_ordering)]."; + let span = proc_macro2::TokenStream::from(attr).span(); + return syn::Error::new(span, msg).to_compile_error().into() + } + } + + let item = syn::parse_macro_input!(tokens as syn::ItemMod); + match parse::Def::try_from(item) { + Ok(def) => expand::expand(def, legacy_ordering).into(), + Err(e) => e.to_compile_error().into(), + } +} diff --git a/support/procedural-fork/src/runtime/parse/helper.rs b/support/procedural-fork/src/runtime/parse/helper.rs new file mode 100644 index 000000000..f05395f9b --- /dev/null +++ b/support/procedural-fork/src/runtime/parse/helper.rs @@ -0,0 +1,37 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::pallet::parse::helper::MutItemAttrs; +use quote::ToTokens; + +pub(crate) fn take_first_item_runtime_attr( + item: &mut impl MutItemAttrs, +) -> syn::Result> +where + Attr: syn::parse::Parse, +{ + let attrs = if let Some(attrs) = item.mut_item_attrs() { attrs } else { return Ok(None) }; + + if let Some(index) = attrs.iter().position(|attr| { + attr.path().segments.first().map_or(false, |segment| segment.ident == "runtime") + }) { + let runtime_attr = attrs.remove(index); + Ok(Some(syn::parse2(runtime_attr.into_token_stream())?)) + } else { + Ok(None) + } +} diff --git a/support/procedural-fork/src/runtime/parse/mod.rs b/support/procedural-fork/src/runtime/parse/mod.rs new file mode 100644 index 000000000..893cb4726 --- /dev/null +++ b/support/procedural-fork/src/runtime/parse/mod.rs @@ -0,0 +1,266 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pub mod helper; +pub mod pallet; +pub mod pallet_decl; +pub mod runtime_struct; +pub mod runtime_types; + +use crate::construct_runtime::parse::Pallet; +use pallet_decl::PalletDeclaration; +use proc_macro2::TokenStream as TokenStream2; +use quote::ToTokens; +use std::collections::HashMap; +use syn::{spanned::Spanned, Ident, Token}; + +use frame_support_procedural_tools::syn_ext as ext; +use runtime_types::RuntimeType; + +mod keyword { + use syn::custom_keyword; + + custom_keyword!(runtime); + custom_keyword!(derive); + custom_keyword!(pallet_index); + custom_keyword!(disable_call); + custom_keyword!(disable_unsigned); +} + +enum RuntimeAttr { + Runtime(proc_macro2::Span), + Derive(proc_macro2::Span, Vec), + PalletIndex(proc_macro2::Span, u8), + DisableCall(proc_macro2::Span), + DisableUnsigned(proc_macro2::Span), +} + +impl RuntimeAttr { + fn span(&self) -> proc_macro2::Span { + match self { + Self::Runtime(span) => *span, + Self::Derive(span, _) => *span, + Self::PalletIndex(span, _) => *span, + Self::DisableCall(span) => *span, + Self::DisableUnsigned(span) => *span, + } + } +} + +impl syn::parse::Parse for RuntimeAttr { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + let lookahead = content.lookahead1(); + if lookahead.peek(keyword::runtime) { + Ok(RuntimeAttr::Runtime(content.parse::()?.span())) + } else if lookahead.peek(keyword::derive) { + let _ = content.parse::(); + let derive_content; + syn::parenthesized!(derive_content in content); + let runtime_types = + derive_content.parse::>()?; + let runtime_types = runtime_types.inner.into_iter().collect(); + Ok(RuntimeAttr::Derive(derive_content.span(), runtime_types)) + } else if lookahead.peek(keyword::pallet_index) { + let _ = content.parse::(); + let pallet_index_content; + syn::parenthesized!(pallet_index_content in content); + let pallet_index = pallet_index_content.parse::()?; + if !pallet_index.suffix().is_empty() { + let msg = "Number literal must not have a suffix"; + return Err(syn::Error::new(pallet_index.span(), msg)) + } + Ok(RuntimeAttr::PalletIndex(pallet_index.span(), pallet_index.base10_parse()?)) + } else if lookahead.peek(keyword::disable_call) { + Ok(RuntimeAttr::DisableCall(content.parse::()?.span())) + } else if lookahead.peek(keyword::disable_unsigned) { + Ok(RuntimeAttr::DisableUnsigned(content.parse::()?.span())) + } else { + Err(lookahead.error()) + } + } +} + +#[derive(Debug, Clone)] +pub enum AllPalletsDeclaration { + Implicit(ImplicitAllPalletsDeclaration), + Explicit(ExplicitAllPalletsDeclaration), +} + +/// Declaration of a runtime with some pallet with implicit declaration of parts. +#[derive(Debug, Clone)] +pub struct ImplicitAllPalletsDeclaration { + pub name: Ident, + pub pallet_decls: Vec, + pub pallet_count: usize, +} + +/// Declaration of a runtime with all pallet having explicit declaration of parts. +#[derive(Debug, Clone)] +pub struct ExplicitAllPalletsDeclaration { + pub name: Ident, + pub pallets: Vec, +} + +pub struct Def { + pub input: TokenStream2, + pub item: syn::ItemMod, + pub runtime_struct: runtime_struct::RuntimeStructDef, + pub pallets: AllPalletsDeclaration, + pub runtime_types: Vec, +} + +impl Def { + pub fn try_from(mut item: syn::ItemMod) -> syn::Result { + let input: TokenStream2 = item.to_token_stream().into(); + let item_span = item.span(); + let items = &mut item + .content + .as_mut() + .ok_or_else(|| { + let msg = "Invalid runtime definition, expected mod to be inlined."; + syn::Error::new(item_span, msg) + })? + .1; + + let mut runtime_struct = None; + let mut runtime_types = None; + + let mut indices = HashMap::new(); + let mut names = HashMap::new(); + + let mut pallet_decls = vec![]; + let mut pallets = vec![]; + + for item in items.iter_mut() { + let mut pallet_item = None; + let mut pallet_index = 0; + + let mut disable_call = false; + let mut disable_unsigned = false; + + while let Some(runtime_attr) = + helper::take_first_item_runtime_attr::(item)? + { + match runtime_attr { + RuntimeAttr::Runtime(span) if runtime_struct.is_none() => { + let p = runtime_struct::RuntimeStructDef::try_from(span, item)?; + runtime_struct = Some(p); + }, + RuntimeAttr::Derive(_, types) if runtime_types.is_none() => { + runtime_types = Some(types); + }, + RuntimeAttr::PalletIndex(span, index) => { + pallet_index = index; + pallet_item = if let syn::Item::Type(item) = item { + Some(item.clone()) + } else { + let msg = "Invalid runtime::pallet_index, expected type definition"; + return Err(syn::Error::new(span, msg)) + }; + }, + RuntimeAttr::DisableCall(_) => disable_call = true, + RuntimeAttr::DisableUnsigned(_) => disable_unsigned = true, + attr => { + let msg = "Invalid duplicated attribute"; + return Err(syn::Error::new(attr.span(), msg)) + }, + } + } + + if let Some(pallet_item) = pallet_item { + match *pallet_item.ty.clone() { + syn::Type::Path(ref path) => { + let pallet_decl = + PalletDeclaration::try_from(item.span(), &pallet_item, path)?; + + if let Some(used_pallet) = + names.insert(pallet_decl.name.clone(), pallet_decl.name.span()) + { + let msg = "Two pallets with the same name!"; + + let mut err = syn::Error::new(used_pallet, &msg); + err.combine(syn::Error::new(pallet_decl.name.span(), &msg)); + return Err(err) + } + + pallet_decls.push(pallet_decl); + }, + syn::Type::TraitObject(syn::TypeTraitObject { bounds, .. }) => { + let pallet = Pallet::try_from( + item.span(), + &pallet_item, + pallet_index, + disable_call, + disable_unsigned, + &bounds, + )?; + + if let Some(used_pallet) = indices.insert(pallet.index, pallet.name.clone()) + { + let msg = format!( + "Pallet indices are conflicting: Both pallets {} and {} are at index {}", + used_pallet, pallet.name, pallet.index, + ); + let mut err = syn::Error::new(used_pallet.span(), &msg); + err.combine(syn::Error::new(pallet.name.span(), msg)); + return Err(err) + } + + pallets.push(pallet); + }, + _ => continue, + } + } + } + + let name = item.ident.clone(); + let decl_count = pallet_decls.len(); + let pallets = if decl_count > 0 { + AllPalletsDeclaration::Implicit(ImplicitAllPalletsDeclaration { + name, + pallet_decls, + pallet_count: decl_count.saturating_add(pallets.len()), + }) + } else { + AllPalletsDeclaration::Explicit(ExplicitAllPalletsDeclaration { name, pallets }) + }; + + let def = Def { + input, + item, + runtime_struct: runtime_struct.ok_or_else(|| { + syn::Error::new(item_span, + "Missing Runtime. Please add a struct inside the module and annotate it with `#[runtime::runtime]`" + ) + })?, + pallets, + runtime_types: runtime_types.ok_or_else(|| { + syn::Error::new(item_span, + "Missing Runtime Types. Please annotate the runtime struct with `#[runtime::derive]`" + ) + })?, + }; + + Ok(def) + } +} diff --git a/support/procedural-fork/src/runtime/parse/pallet.rs b/support/procedural-fork/src/runtime/parse/pallet.rs new file mode 100644 index 000000000..d2f1857fb --- /dev/null +++ b/support/procedural-fork/src/runtime/parse/pallet.rs @@ -0,0 +1,99 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::construct_runtime::parse::{Pallet, PalletPart, PalletPartKeyword, PalletPath}; +use quote::ToTokens; +use syn::{punctuated::Punctuated, spanned::Spanned, token, Error, Ident, PathArguments}; + +impl Pallet { + pub fn try_from( + attr_span: proc_macro2::Span, + item: &syn::ItemType, + pallet_index: u8, + disable_call: bool, + disable_unsigned: bool, + bounds: &Punctuated, + ) -> syn::Result { + let name = item.ident.clone(); + + let mut pallet_path = None; + let mut pallet_parts = vec![]; + + for (index, bound) in bounds.into_iter().enumerate() { + if let syn::TypeParamBound::Trait(syn::TraitBound { path, .. }) = bound { + if index == 0 { + pallet_path = Some(PalletPath { inner: path.clone() }); + } else { + let pallet_part = syn::parse2::(bound.into_token_stream())?; + pallet_parts.push(pallet_part); + } + } else { + return Err(Error::new( + attr_span, + "Invalid pallet declaration, expected a path or a trait object", + )) + }; + } + + let mut path = pallet_path.ok_or(Error::new( + attr_span, + "Invalid pallet declaration, expected a path or a trait object", + ))?; + + let mut instance = None; + if let Some(segment) = path.inner.segments.iter_mut().find(|seg| !seg.arguments.is_empty()) + { + if let PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { + args, .. + }) = segment.arguments.clone() + { + if let Some(syn::GenericArgument::Type(syn::Type::Path(arg_path))) = args.first() { + instance = + Some(Ident::new(&arg_path.to_token_stream().to_string(), arg_path.span())); + segment.arguments = PathArguments::None; + } + } + } + + pallet_parts = pallet_parts + .into_iter() + .filter(|part| { + if let (true, &PalletPartKeyword::Call(_)) = (disable_call, &part.keyword) { + false + } else if let (true, &PalletPartKeyword::ValidateUnsigned(_)) = + (disable_unsigned, &part.keyword) + { + false + } else { + true + } + }) + .collect(); + + let cfg_pattern = vec![]; + + Ok(Pallet { + is_expanded: true, + name, + index: pallet_index, + path, + instance, + cfg_pattern, + pallet_parts, + }) + } +} diff --git a/support/procedural-fork/src/runtime/parse/pallet_decl.rs b/support/procedural-fork/src/runtime/parse/pallet_decl.rs new file mode 100644 index 000000000..437a163cf --- /dev/null +++ b/support/procedural-fork/src/runtime/parse/pallet_decl.rs @@ -0,0 +1,60 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use quote::ToTokens; +use syn::{spanned::Spanned, Attribute, Ident, PathArguments}; + +/// The declaration of a pallet. +#[derive(Debug, Clone)] +pub struct PalletDeclaration { + /// The name of the pallet, e.g.`System` in `System: frame_system`. + pub name: Ident, + /// Optional attributes tagged right above a pallet declaration. + pub attrs: Vec, + /// The path of the pallet, e.g. `frame_system` in `System: frame_system`. + pub path: syn::Path, + /// The instance of the pallet, e.g. `Instance1` in `Council: pallet_collective::`. + pub instance: Option, +} + +impl PalletDeclaration { + pub fn try_from( + _attr_span: proc_macro2::Span, + item: &syn::ItemType, + path: &syn::TypePath, + ) -> syn::Result { + let name = item.ident.clone(); + + let mut path = path.path.clone(); + + let mut instance = None; + if let Some(segment) = path.segments.iter_mut().find(|seg| !seg.arguments.is_empty()) { + if let PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { + args, .. + }) = segment.arguments.clone() + { + if let Some(syn::GenericArgument::Type(syn::Type::Path(arg_path))) = args.first() { + instance = + Some(Ident::new(&arg_path.to_token_stream().to_string(), arg_path.span())); + segment.arguments = PathArguments::None; + } + } + } + + Ok(Self { name, path, instance, attrs: item.attrs.clone() }) + } +} diff --git a/support/procedural-fork/src/runtime/parse/runtime_struct.rs b/support/procedural-fork/src/runtime/parse/runtime_struct.rs new file mode 100644 index 000000000..8fa746ee8 --- /dev/null +++ b/support/procedural-fork/src/runtime/parse/runtime_struct.rs @@ -0,0 +1,35 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use syn::spanned::Spanned; +pub struct RuntimeStructDef { + pub ident: syn::Ident, + pub attr_span: proc_macro2::Span, +} + +impl RuntimeStructDef { + pub fn try_from(attr_span: proc_macro2::Span, item: &mut syn::Item) -> syn::Result { + let item = if let syn::Item::Struct(item) = item { + item + } else { + let msg = "Invalid runtime::runtime, expected struct definition"; + return Err(syn::Error::new(item.span(), msg)) + }; + + Ok(Self { ident: item.ident.clone(), attr_span }) + } +} diff --git a/support/procedural-fork/src/runtime/parse/runtime_types.rs b/support/procedural-fork/src/runtime/parse/runtime_types.rs new file mode 100644 index 000000000..a4480e2a1 --- /dev/null +++ b/support/procedural-fork/src/runtime/parse/runtime_types.rs @@ -0,0 +1,76 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use syn::{ + parse::{Parse, ParseStream}, + Result, +}; + +mod keyword { + use syn::custom_keyword; + + custom_keyword!(RuntimeCall); + custom_keyword!(RuntimeEvent); + custom_keyword!(RuntimeError); + custom_keyword!(RuntimeOrigin); + custom_keyword!(RuntimeFreezeReason); + custom_keyword!(RuntimeHoldReason); + custom_keyword!(RuntimeSlashReason); + custom_keyword!(RuntimeLockId); + custom_keyword!(RuntimeTask); +} + +#[derive(Debug, Clone, PartialEq)] +pub enum RuntimeType { + RuntimeCall(keyword::RuntimeCall), + RuntimeEvent(keyword::RuntimeEvent), + RuntimeError(keyword::RuntimeError), + RuntimeOrigin(keyword::RuntimeOrigin), + RuntimeFreezeReason(keyword::RuntimeFreezeReason), + RuntimeHoldReason(keyword::RuntimeHoldReason), + RuntimeSlashReason(keyword::RuntimeSlashReason), + RuntimeLockId(keyword::RuntimeLockId), + RuntimeTask(keyword::RuntimeTask), +} + +impl Parse for RuntimeType { + fn parse(input: ParseStream) -> Result { + let lookahead = input.lookahead1(); + + if lookahead.peek(keyword::RuntimeCall) { + Ok(Self::RuntimeCall(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeEvent) { + Ok(Self::RuntimeEvent(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeError) { + Ok(Self::RuntimeError(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeOrigin) { + Ok(Self::RuntimeOrigin(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeFreezeReason) { + Ok(Self::RuntimeFreezeReason(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeHoldReason) { + Ok(Self::RuntimeHoldReason(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeSlashReason) { + Ok(Self::RuntimeSlashReason(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeLockId) { + Ok(Self::RuntimeLockId(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeTask) { + Ok(Self::RuntimeTask(input.parse()?)) + } else { + Err(lookahead.error()) + } + } +} diff --git a/support/procedural-fork/src/storage_alias.rs b/support/procedural-fork/src/storage_alias.rs new file mode 100644 index 000000000..06f62768f --- /dev/null +++ b/support/procedural-fork/src/storage_alias.rs @@ -0,0 +1,676 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Implementation of the `storage_alias` attribute macro. + +use crate::{counter_prefix, pallet::parse::helper}; +use frame_support_procedural_tools::generate_access_from_frame_or_crate; +use proc_macro2::{Span, TokenStream}; +use quote::{quote, ToTokens}; +use syn::{ + parenthesized, + parse::{Parse, ParseStream}, + punctuated::Punctuated, + spanned::Spanned, + token, + visit::Visit, + Attribute, Error, Ident, Result, Token, Type, TypeParam, Visibility, WhereClause, +}; + +/// Extension trait for [`Type`]. +trait TypeExt { + fn get_ident(&self) -> Option<&Ident>; + fn contains_ident(&self, ident: &Ident) -> bool; +} + +impl TypeExt for Type { + fn get_ident(&self) -> Option<&Ident> { + match self { + Type::Path(p) => match &p.qself { + Some(qself) => qself.ty.get_ident(), + None => p.path.get_ident(), + }, + _ => None, + } + } + + fn contains_ident(&self, ident: &Ident) -> bool { + struct ContainsIdent<'a> { + ident: &'a Ident, + found: bool, + } + impl<'a, 'ast> Visit<'ast> for ContainsIdent<'a> { + fn visit_ident(&mut self, i: &'ast Ident) { + if i == self.ident { + self.found = true; + } + } + } + + let mut visitor = ContainsIdent { ident, found: false }; + syn::visit::visit_type(&mut visitor, self); + visitor.found + } +} + +/// Represents generics which only support [`TypeParam`] separated by commas. +struct SimpleGenerics { + lt_token: Token![<], + params: Punctuated, + gt_token: Token![>], +} + +impl SimpleGenerics { + /// Returns the generics for types declarations etc. + fn type_generics(&self) -> impl Iterator { + self.params.iter().map(|p| &p.ident) + } + + /// Returns the generics for the `impl` block. + fn impl_generics(&self) -> impl Iterator { + self.params.iter() + } +} + +impl Parse for SimpleGenerics { + fn parse(input: ParseStream<'_>) -> Result { + Ok(Self { + lt_token: input.parse()?, + params: Punctuated::parse_separated_nonempty(input)?, + gt_token: input.parse()?, + }) + } +} + +impl ToTokens for SimpleGenerics { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.lt_token.to_tokens(tokens); + self.params.to_tokens(tokens); + self.gt_token.to_tokens(tokens); + } +} + +mod storage_types { + syn::custom_keyword!(StorageValue); + syn::custom_keyword!(StorageMap); + syn::custom_keyword!(CountedStorageMap); + syn::custom_keyword!(StorageDoubleMap); + syn::custom_keyword!(StorageNMap); +} + +/// The types of prefixes the storage alias macro supports. +mod prefix_types { + // Use the verbatim/unmodified input name as the prefix. + syn::custom_keyword!(verbatim); + // The input type is a pallet and its pallet name should be used as the prefix. + syn::custom_keyword!(pallet_name); + // The input type implements `Get<'static str>` and this `str` should be used as the prefix. + syn::custom_keyword!(dynamic); +} + +/// The supported storage types +enum StorageType { + Value { + _kw: storage_types::StorageValue, + _lt_token: Token![<], + prefix: Type, + _value_comma: Token![,], + value_ty: Type, + query_type: Option<(Token![,], Type)>, + _trailing_comma: Option, + _gt_token: Token![>], + }, + Map { + _kw: storage_types::StorageMap, + _lt_token: Token![<], + prefix: Type, + _hasher_comma: Token![,], + hasher_ty: Type, + _key_comma: Token![,], + key_ty: Type, + _value_comma: Token![,], + value_ty: Type, + query_type: Option<(Token![,], Type)>, + _trailing_comma: Option, + _gt_token: Token![>], + }, + CountedMap { + _kw: storage_types::CountedStorageMap, + _lt_token: Token![<], + prefix: Type, + _hasher_comma: Token![,], + hasher_ty: Type, + _key_comma: Token![,], + key_ty: Type, + _value_comma: Token![,], + value_ty: Type, + query_type: Option<(Token![,], Type)>, + _trailing_comma: Option, + _gt_token: Token![>], + }, + DoubleMap { + _kw: storage_types::StorageDoubleMap, + _lt_token: Token![<], + prefix: Type, + _hasher1_comma: Token![,], + hasher1_ty: Type, + _key1_comma: Token![,], + key1_ty: Type, + _hasher2_comma: Token![,], + hasher2_ty: Type, + _key2_comma: Token![,], + key2_ty: Type, + _value_comma: Token![,], + value_ty: Type, + query_type: Option<(Token![,], Type)>, + _trailing_comma: Option, + _gt_token: Token![>], + }, + NMap { + _kw: storage_types::StorageNMap, + _lt_token: Token![<], + prefix: Type, + _paren_comma: Token![,], + _paren_token: token::Paren, + key_types: Punctuated, + _value_comma: Token![,], + value_ty: Type, + query_type: Option<(Token![,], Type)>, + _trailing_comma: Option, + _gt_token: Token![>], + }, +} + +impl StorageType { + /// Generate the actual type declaration. + fn generate_type_declaration( + &self, + crate_: &syn::Path, + storage_instance: &StorageInstance, + storage_name: &Ident, + storage_generics: Option<&SimpleGenerics>, + visibility: &Visibility, + attributes: &[Attribute], + ) -> TokenStream { + let storage_instance_generics = &storage_instance.generics; + let storage_instance = &storage_instance.name; + let attributes = attributes.iter(); + let storage_generics = storage_generics.map(|g| { + let generics = g.type_generics(); + + quote!( < #( #generics ),* > ) + }); + + match self { + Self::Value { value_ty, query_type, .. } => { + let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); + + quote! { + #( #attributes )* + #visibility type #storage_name #storage_generics = #crate_::storage::types::StorageValue< + #storage_instance #storage_instance_generics, + #value_ty + #query_type + >; + } + }, + Self::CountedMap { value_ty, query_type, hasher_ty, key_ty, .. } | + Self::Map { value_ty, query_type, hasher_ty, key_ty, .. } => { + let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); + let map_type = Ident::new( + match self { + Self::Map { .. } => "StorageMap", + _ => "CountedStorageMap", + }, + Span::call_site(), + ); + + quote! { + #( #attributes )* + #visibility type #storage_name #storage_generics = #crate_::storage::types::#map_type< + #storage_instance #storage_instance_generics, + #hasher_ty, + #key_ty, + #value_ty + #query_type + >; + } + }, + Self::DoubleMap { + value_ty, + query_type, + hasher1_ty, + key1_ty, + hasher2_ty, + key2_ty, + .. + } => { + let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); + + quote! { + #( #attributes )* + #visibility type #storage_name #storage_generics = #crate_::storage::types::StorageDoubleMap< + #storage_instance #storage_instance_generics, + #hasher1_ty, + #key1_ty, + #hasher2_ty, + #key2_ty, + #value_ty + #query_type + >; + } + }, + Self::NMap { value_ty, query_type, key_types, .. } => { + let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); + let key_types = key_types.iter(); + + quote! { + #( #attributes )* + #visibility type #storage_name #storage_generics = #crate_::storage::types::StorageNMap< + #storage_instance #storage_instance_generics, + ( #( #key_types ),* ), + #value_ty + #query_type + >; + } + }, + } + } + + /// The prefix for this storage type. + fn prefix(&self) -> &Type { + match self { + Self::Value { prefix, .. } | + Self::Map { prefix, .. } | + Self::CountedMap { prefix, .. } | + Self::NMap { prefix, .. } | + Self::DoubleMap { prefix, .. } => prefix, + } + } +} + +impl Parse for StorageType { + fn parse(input: ParseStream<'_>) -> Result { + let lookahead = input.lookahead1(); + + let parse_query_type = |input: ParseStream<'_>| -> Result> { + if input.peek(Token![,]) && !input.peek2(Token![>]) { + Ok(Some((input.parse()?, input.parse()?))) + } else { + Ok(None) + } + }; + + if lookahead.peek(storage_types::StorageValue) { + Ok(Self::Value { + _kw: input.parse()?, + _lt_token: input.parse()?, + prefix: input.parse()?, + _value_comma: input.parse()?, + value_ty: input.parse()?, + query_type: parse_query_type(input)?, + _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, + _gt_token: input.parse()?, + }) + } else if lookahead.peek(storage_types::StorageMap) { + Ok(Self::Map { + _kw: input.parse()?, + _lt_token: input.parse()?, + prefix: input.parse()?, + _hasher_comma: input.parse()?, + hasher_ty: input.parse()?, + _key_comma: input.parse()?, + key_ty: input.parse()?, + _value_comma: input.parse()?, + value_ty: input.parse()?, + query_type: parse_query_type(input)?, + _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, + _gt_token: input.parse()?, + }) + } else if lookahead.peek(storage_types::CountedStorageMap) { + Ok(Self::CountedMap { + _kw: input.parse()?, + _lt_token: input.parse()?, + prefix: input.parse()?, + _hasher_comma: input.parse()?, + hasher_ty: input.parse()?, + _key_comma: input.parse()?, + key_ty: input.parse()?, + _value_comma: input.parse()?, + value_ty: input.parse()?, + query_type: parse_query_type(input)?, + _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, + _gt_token: input.parse()?, + }) + } else if lookahead.peek(storage_types::StorageDoubleMap) { + Ok(Self::DoubleMap { + _kw: input.parse()?, + _lt_token: input.parse()?, + prefix: input.parse()?, + _hasher1_comma: input.parse()?, + hasher1_ty: input.parse()?, + _key1_comma: input.parse()?, + key1_ty: input.parse()?, + _hasher2_comma: input.parse()?, + hasher2_ty: input.parse()?, + _key2_comma: input.parse()?, + key2_ty: input.parse()?, + _value_comma: input.parse()?, + value_ty: input.parse()?, + query_type: parse_query_type(input)?, + _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, + _gt_token: input.parse()?, + }) + } else if lookahead.peek(storage_types::StorageNMap) { + let content; + Ok(Self::NMap { + _kw: input.parse()?, + _lt_token: input.parse()?, + prefix: input.parse()?, + _paren_comma: input.parse()?, + _paren_token: parenthesized!(content in input), + key_types: Punctuated::parse_terminated(&content)?, + _value_comma: input.parse()?, + value_ty: input.parse()?, + query_type: parse_query_type(input)?, + _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, + _gt_token: input.parse()?, + }) + } else { + Err(lookahead.error()) + } + } +} + +/// The input expected by this macro. +struct Input { + attributes: Vec, + visibility: Visibility, + _type: Token![type], + storage_name: Ident, + storage_generics: Option, + where_clause: Option, + _equal: Token![=], + storage_type: StorageType, + _semicolon: Token![;], +} + +impl Parse for Input { + fn parse(input: ParseStream<'_>) -> Result { + let attributes = input.call(Attribute::parse_outer)?; + let visibility = input.parse()?; + let _type = input.parse()?; + let storage_name = input.parse()?; + + let lookahead = input.lookahead1(); + let storage_generics = if lookahead.peek(Token![<]) { + Some(input.parse()?) + } else if lookahead.peek(Token![=]) { + None + } else { + return Err(lookahead.error()) + }; + + let lookahead = input.lookahead1(); + let where_clause = if lookahead.peek(Token![where]) { + Some(input.parse()?) + } else if lookahead.peek(Token![=]) { + None + } else { + return Err(lookahead.error()) + }; + + let _equal = input.parse()?; + + let storage_type = input.parse()?; + + let _semicolon = input.parse()?; + + Ok(Self { + attributes, + visibility, + _type, + storage_name, + storage_generics, + _equal, + storage_type, + where_clause, + _semicolon, + }) + } +} + +/// Defines which type of prefix the storage alias is using. +#[derive(Clone, Copy)] +enum PrefixType { + /// An appropriate prefix will be determined automatically. + /// + /// If generics are passed, this is assumed to be a pallet and the pallet name should be used. + /// Otherwise use the verbatim passed name as prefix. + Compatibility, + /// The provided ident/name will be used as the prefix. + Verbatim, + /// The provided type will be used to determine the prefix. This type must + /// implement `PalletInfoAccess` which specifies the proper name. This + /// name is then used as the prefix. + PalletName, + /// Uses the provided type implementing `Get<'static str>` to determine the prefix. + Dynamic, +} + +/// Implementation of the `storage_alias` attribute macro. +pub fn storage_alias(attributes: TokenStream, input: TokenStream) -> Result { + let input = syn::parse2::(input)?; + let crate_ = generate_access_from_frame_or_crate("frame-support")?; + + let prefix_type = if attributes.is_empty() { + PrefixType::Compatibility + } else if syn::parse2::(attributes.clone()).is_ok() { + PrefixType::Verbatim + } else if syn::parse2::(attributes.clone()).is_ok() { + PrefixType::PalletName + } else if syn::parse2::(attributes.clone()).is_ok() { + PrefixType::Dynamic + } else { + return Err(Error::new(attributes.span(), "Unknown attributes")) + }; + + let storage_instance = generate_storage_instance( + &crate_, + &input.storage_name, + input.storage_generics.as_ref(), + input.where_clause.as_ref(), + input.storage_type.prefix(), + &input.visibility, + matches!(input.storage_type, StorageType::CountedMap { .. }), + prefix_type, + )?; + + let definition = input.storage_type.generate_type_declaration( + &crate_, + &storage_instance, + &input.storage_name, + input.storage_generics.as_ref(), + &input.visibility, + &input.attributes, + ); + + let storage_instance_code = storage_instance.code; + + Ok(quote! { + #storage_instance_code + + #definition + }) +} + +/// The storage instance to use for the storage alias. +struct StorageInstance { + name: Ident, + generics: TokenStream, + code: TokenStream, +} + +/// Generate the [`StorageInstance`] for the storage alias. +fn generate_storage_instance( + crate_: &syn::Path, + storage_name: &Ident, + storage_generics: Option<&SimpleGenerics>, + storage_where_clause: Option<&WhereClause>, + prefix: &Type, + visibility: &Visibility, + is_counted_map: bool, + prefix_type: PrefixType, +) -> Result { + if let Type::Infer(_) = prefix { + return Err(Error::new(prefix.span(), "`_` is not allowed as prefix by `storage_alias`.")) + } + + let impl_generics_used_by_prefix = storage_generics + .as_ref() + .map(|g| { + g.impl_generics() + .filter(|g| prefix.contains_ident(&g.ident)) + .collect::>() + }) + .unwrap_or_default(); + + let (pallet_prefix, impl_generics, type_generics) = match prefix_type { + PrefixType::Compatibility => + if !impl_generics_used_by_prefix.is_empty() { + let type_generics = impl_generics_used_by_prefix.iter().map(|g| &g.ident); + let impl_generics = impl_generics_used_by_prefix.iter(); + + ( + quote! { + < #prefix as #crate_::traits::PalletInfoAccess>::name() + }, + quote!( #( #impl_generics ),* ), + quote!( #( #type_generics ),* ), + ) + } else if let Some(prefix) = prefix.get_ident() { + let prefix_str = prefix.to_string(); + + (quote!(#prefix_str), quote!(), quote!()) + } else { + return Err(Error::new_spanned( + prefix, + "If there are no generics, the prefix is only allowed to be an identifier.", + )) + }, + PrefixType::Verbatim => { + let prefix_str = match prefix.get_ident() { + Some(p) => p.to_string(), + None => + return Err(Error::new_spanned( + prefix, + "Prefix type `verbatim` requires that the prefix is an ident.", + )), + }; + + (quote!(#prefix_str), quote!(), quote!()) + }, + PrefixType::PalletName => { + let type_generics = impl_generics_used_by_prefix.iter().map(|g| &g.ident); + let impl_generics = impl_generics_used_by_prefix.iter(); + + ( + quote! { + <#prefix as #crate_::traits::PalletInfoAccess>::name() + }, + quote!( #( #impl_generics ),* ), + quote!( #( #type_generics ),* ), + ) + }, + PrefixType::Dynamic => { + let type_generics = impl_generics_used_by_prefix.iter().map(|g| &g.ident); + let impl_generics = impl_generics_used_by_prefix.iter(); + + ( + quote! { + <#prefix as #crate_::traits::Get<_>>::get() + }, + quote!( #( #impl_generics ),* ), + quote!( #( #type_generics ),* ), + ) + }, + }; + + let where_clause = storage_where_clause.map(|w| quote!(#w)).unwrap_or_default(); + + let name_str = format!("{}_Storage_Instance", storage_name); + let name = Ident::new(&name_str, Span::call_site()); + let storage_name_str = storage_name.to_string(); + + let counter_code = is_counted_map.then(|| { + let counter_name = Ident::new(&counter_prefix(&name_str), Span::call_site()); + let counter_storage_name_str = counter_prefix(&storage_name_str); + let storage_prefix_hash = helper::two128_str(&counter_storage_name_str); + + quote! { + #visibility struct #counter_name< #impl_generics >( + ::core::marker::PhantomData<(#type_generics)> + ) #where_clause; + + impl<#impl_generics> #crate_::traits::StorageInstance + for #counter_name< #type_generics > #where_clause + { + fn pallet_prefix() -> &'static str { + #pallet_prefix + } + + const STORAGE_PREFIX: &'static str = #counter_storage_name_str; + fn storage_prefix_hash() -> [u8; 16] { + #storage_prefix_hash + } + } + + impl<#impl_generics> #crate_::storage::types::CountedStorageMapInstance + for #name< #type_generics > #where_clause + { + type CounterPrefix = #counter_name < #type_generics >; + } + } + }); + + let storage_prefix_hash = helper::two128_str(&storage_name_str); + + // Implement `StorageInstance` trait. + let code = quote! { + #[allow(non_camel_case_types)] + #visibility struct #name< #impl_generics >( + ::core::marker::PhantomData<(#type_generics)> + ) #where_clause; + + impl<#impl_generics> #crate_::traits::StorageInstance + for #name< #type_generics > #where_clause + { + fn pallet_prefix() -> &'static str { + #pallet_prefix + } + + const STORAGE_PREFIX: &'static str = #storage_name_str; + fn storage_prefix_hash() -> [u8; 16] { + #storage_prefix_hash + } + } + + #counter_code + }; + + Ok(StorageInstance { name, code, generics: quote!( < #type_generics > ) }) +} diff --git a/support/procedural-fork/src/transactional.rs b/support/procedural-fork/src/transactional.rs new file mode 100644 index 000000000..e9d4f84b7 --- /dev/null +++ b/support/procedural-fork/src/transactional.rs @@ -0,0 +1,60 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use frame_support_procedural_tools::generate_access_from_frame_or_crate; +use proc_macro::TokenStream; +use quote::quote; +use syn::{ItemFn, Result}; + +pub fn transactional(_attr: TokenStream, input: TokenStream) -> Result { + let ItemFn { attrs, vis, sig, block } = syn::parse(input)?; + + let crate_ = generate_access_from_frame_or_crate("frame-support")?; + let output = quote! { + #(#attrs)* + #vis #sig { + use #crate_::storage::{with_transaction, TransactionOutcome}; + with_transaction(|| { + let r = (|| { #block })(); + if r.is_ok() { + TransactionOutcome::Commit(r) + } else { + TransactionOutcome::Rollback(r) + } + }) + } + }; + + Ok(output.into()) +} + +pub fn require_transactional(_attr: TokenStream, input: TokenStream) -> Result { + let ItemFn { attrs, vis, sig, block } = syn::parse(input)?; + + let crate_ = generate_access_from_frame_or_crate("frame-support")?; + let output = quote! { + #(#attrs)* + #vis #sig { + if !#crate_::storage::transactional::is_transactional() { + return Err(#crate_::sp_runtime::TransactionalError::NoLayer.into()); + } + #block + } + }; + + Ok(output.into()) +} diff --git a/support/procedural-fork/src/tt_macro.rs b/support/procedural-fork/src/tt_macro.rs new file mode 100644 index 000000000..d37127421 --- /dev/null +++ b/support/procedural-fork/src/tt_macro.rs @@ -0,0 +1,105 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Implementation of the `create_tt_return_macro` macro + +use crate::COUNTER; +use proc_macro2::{Ident, TokenStream}; +use quote::format_ident; + +struct CreateTtReturnMacroDef { + name: Ident, + args: Vec<(Ident, TokenStream)>, +} + +impl syn::parse::Parse for CreateTtReturnMacroDef { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let name = input.parse()?; + let _ = input.parse::()?; + + let mut args = Vec::new(); + while !input.is_empty() { + let mut value; + let key: Ident = input.parse()?; + let _ = input.parse::()?; + let _: syn::token::Bracket = syn::bracketed!(value in input); + let _: syn::token::Brace = syn::braced!(value in value); + let value: TokenStream = value.parse()?; + + args.push((key, value)) + } + + Ok(Self { name, args }) + } +} + +/// A proc macro that accepts a name and any number of key-value pairs, to be used to create a +/// declarative macro that follows tt-call conventions and simply calls +/// [`tt_call::tt_return`], accepting an optional `frame-support` argument and returning +/// the key-value pairs that were supplied to the proc macro. +/// +/// # Example +/// ```ignore +/// __create_tt_macro! { +/// my_tt_macro, +/// foo = [{ bar }] +/// } +/// +/// // Creates the following declarative macro: +/// +/// macro_rules! my_tt_macro { +/// { +/// $caller:tt +/// $(your_tt_return = [{ $my_tt_return:path }])? +/// } => { +/// $my_tt_return! { +/// $caller +/// foo = [{ bar }] +/// } +/// } +/// } +/// ``` +pub fn create_tt_return_macro(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let CreateTtReturnMacroDef { name, args } = + syn::parse_macro_input!(input as CreateTtReturnMacroDef); + + let (keys, values): (Vec<_>, Vec<_>) = args.into_iter().unzip(); + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + let unique_name = format_ident!("{}_{}", name, count); + + let decl_macro = quote::quote! { + #[macro_export] + #[doc(hidden)] + macro_rules! #unique_name { + { + $caller:tt + $(your_tt_return = [{ $my_tt_macro:path }])? + } => { + $my_tt_return! { + $caller + #( + #keys = [{ #values }] + )* + } + } + } + + pub use #unique_name as #name; + }; + + decl_macro.into() +} From dd5c84b4e6b0a7757115cef8cafbbc91cc296df6 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 18 Sep 2024 16:02:43 -0400 Subject: [PATCH 14/53] fix warnings --- support/procedural-fork/src/lib.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/support/procedural-fork/src/lib.rs b/support/procedural-fork/src/lib.rs index 08ce0a73c..efc3ee6a7 100644 --- a/support/procedural-fork/src/lib.rs +++ b/support/procedural-fork/src/lib.rs @@ -19,12 +19,7 @@ mod storage_alias; mod transactional; mod tt_macro; -use frame_support_procedural_tools::generate_access_from_frame_or_crate; -use macro_magic::{import_tokens_attr, import_tokens_attr_verbatim}; -use proc_macro::TokenStream; -use quote::{quote, ToTokens}; use std::{cell::RefCell, str::FromStr}; -use syn::{parse_macro_input, Error, ItemImpl, ItemMod, TraitItemType}; pub(crate) const INHERENT_INSTANCE_NAME: &str = "__InherentHiddenInstance"; From 15fedec861fc0ee1a1b330b4120498f743e2e194 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 18 Sep 2024 16:19:44 -0400 Subject: [PATCH 15/53] publicly export everything --- support/procedural-fork/src/lib.rs | 92 ++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/support/procedural-fork/src/lib.rs b/support/procedural-fork/src/lib.rs index efc3ee6a7..5292c7834 100644 --- a/support/procedural-fork/src/lib.rs +++ b/support/procedural-fork/src/lib.rs @@ -60,3 +60,95 @@ fn get_cargo_env_var(version_env: &str) -> std::result::Result String { format!("CounterFor{}", prefix) } + +pub mod exports { + pub mod benchmark { + pub use crate::benchmark::*; + } + + pub mod crate_version { + pub use crate::crate_version::*; + } + + pub mod derive_impl { + pub use crate::derive_impl::*; + } + + pub mod dummy_part_checker { + pub use crate::dummy_part_checker::*; + } + + pub mod dynamic_params { + pub use crate::dynamic_params::*; + } + + pub mod key_prefix { + pub use crate::key_prefix::*; + } + + pub mod match_and_insert { + pub use crate::match_and_insert::*; + } + + pub mod pallet_error { + pub use crate::pallet_error::*; + } + + pub mod storage_alias { + pub use crate::storage_alias::*; + } + + pub mod transactional { + pub use crate::transactional::*; + } + + pub mod tt_macro { + pub use crate::tt_macro::*; + } + + pub mod construct_runtime { + pub use crate::construct_runtime::*; + + pub mod parse { + pub use crate::construct_runtime::parse::*; + } + + pub mod expand { + pub use crate::construct_runtime::expand::*; + } + } + + pub mod no_bound { + pub mod clone { + pub use crate::no_bound::clone::*; + } + + pub mod debug { + pub use crate::no_bound::debug::*; + } + + pub mod default { + pub use crate::no_bound::default::*; + } + + pub mod ord { + pub use crate::no_bound::ord::*; + } + + pub mod partial_eq { + pub use crate::no_bound::partial_eq::*; + } + + pub mod partial_ord { + pub use crate::no_bound::partial_ord::*; + } + } + + pub mod pallet { + pub use crate::pallet::*; + + pub mod parse { + pub use crate::pallet::parse::*; + } + } +} From 472337733ddc930ed004cedb2b76ff10ff489f30 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 18 Sep 2024 16:27:26 -0400 Subject: [PATCH 16/53] add docs --- support/procedural-fork/src/lib.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/support/procedural-fork/src/lib.rs b/support/procedural-fork/src/lib.rs index 5292c7834..cce0c65fa 100644 --- a/support/procedural-fork/src/lib.rs +++ b/support/procedural-fork/src/lib.rs @@ -1,3 +1,13 @@ +//! This crate is a fork of the `frame-support-procedural` crate from +//! `substrate/frame/support/procedural` in `polkadot-sdk`. The purpose of this fork is to +//! re-export all parsing code from the original crate to make it accessible to other crates, +//! since the original crate is a `proc-macro` crate and therefore cannot have any non-macro +//! public exports. If Parity ever decides to move the parsing code to a separate crate, this +//! fork will no longer need to exist. +//! +//! Tags will be created for each major version of `polkadot-sdk` that `subtensor` relies on, +//! on an as-needed, ad-hoc basis, and versions will matched the corresponding `polkadot-sdk` +//! version/tag name. #![recursion_limit = "512"] #![deny(rustdoc::broken_intra_doc_links)] From aa57e9c32e221439b3f1c709dca662a84ec012dc Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 18 Sep 2024 16:29:01 -0400 Subject: [PATCH 17/53] tweak --- support/procedural-fork/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/support/procedural-fork/src/lib.rs b/support/procedural-fork/src/lib.rs index cce0c65fa..2ac076636 100644 --- a/support/procedural-fork/src/lib.rs +++ b/support/procedural-fork/src/lib.rs @@ -3,7 +3,8 @@ //! re-export all parsing code from the original crate to make it accessible to other crates, //! since the original crate is a `proc-macro` crate and therefore cannot have any non-macro //! public exports. If Parity ever decides to move the parsing code to a separate crate, this -//! fork will no longer need to exist. +//! fork will no longer need to exist, but right now this is the only reliable way to get +//! access to the core parsing logic of substrate. //! //! Tags will be created for each major version of `polkadot-sdk` that `subtensor` relies on, //! on an as-needed, ad-hoc basis, and versions will matched the corresponding `polkadot-sdk` From 6122efa8e968bcea7452b425a6ac9bde86b885b4 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 18 Sep 2024 16:42:39 -0400 Subject: [PATCH 18/53] new lint using procedural-fork --- support/linting/src/pallet_index.rs | 209 ++++++---------------------- 1 file changed, 44 insertions(+), 165 deletions(-) diff --git a/support/linting/src/pallet_index.rs b/support/linting/src/pallet_index.rs index b373a04b2..80e5e234d 100644 --- a/support/linting/src/pallet_index.rs +++ b/support/linting/src/pallet_index.rs @@ -1,12 +1,6 @@ use super::*; -use quote::ToTokens; -use syn::braced; -use syn::parse::{Parse, ParseStream}; -use syn::punctuated::Punctuated; -use syn::spanned::Spanned; -use syn::token::Colon; -use syn::visit::Visit; -use syn::{File, Ident, ItemMacro, Path, Token, Visibility}; +use procedural_fork::exports::construct_runtime::parse::RuntimeDeclaration; +use syn::{visit::Visit, File}; pub struct RequireExplicitPalletIndex; @@ -29,133 +23,42 @@ struct ConstructRuntimeVisitor { errors: Vec, } -impl<'ast> Visit<'ast> for ConstructRuntimeVisitor { - fn visit_item_macro(&mut self, node: &'ast ItemMacro) { +impl<'ast> syn::visit::Visit<'ast> for ConstructRuntimeVisitor { + fn visit_item_macro(&mut self, node: &'ast syn::ItemMacro) { if node.mac.path.is_ident("construct_runtime") { - // Token stream parsing logic let tokens = node.mac.tokens.clone(); - println!("Parsing construct_runtime! tokens: {}", tokens.to_string()); - let result = syn::parse2::(tokens); - if let Ok(runtime_entries) = result { - for entry in runtime_entries.entries { - // Check if the entry is missing an explicit index - if entry.index.is_none() { - self.errors.push(syn::Error::new( - entry.pallet_name.span(), - format!( - "Pallet `{}` does not have an explicit index in construct_runtime!", - entry.pallet_name.to_token_stream().to_string().trim() - ), - )); + // Attempt to parse the construct_runtime invocation. + match syn::parse2::(tokens) { + Ok(runtime_decl) => { + if let RuntimeDeclaration::Explicit(runtime) = runtime_decl { + for pallet in runtime.pallets { + if pallet.index.is_none() { + self.errors.push(syn::Error::new( + pallet.name.span(), + format!( + "Pallet `{}` does not have an explicit index in construct_runtime!", + pallet.name.to_token_stream() + ), + )); + } + } } } - } else { - // Log error - println!("Failed to parse construct_runtime! block: {:?}", result); - self.errors.push(result.unwrap_err()); + Err(e) => self.errors.push(e), } } - // Continue visiting the rest of the file syn::visit::visit_item_macro(self, node); } } -#[derive(Debug)] -struct ConstructRuntimeEntries { - entries: Punctuated, -} - -impl Parse for ConstructRuntimeEntries { - fn parse(input: ParseStream) -> syn::Result { - let entries = input.parse_terminated(PalletEntry::parse, Token![,])?; - Ok(ConstructRuntimeEntries { entries }) - } -} - -#[derive(Debug)] -struct PalletEntry { - visibility: Option, - pallet_name: Path, - components: Option, - index: Option, -} - -impl Parse for PalletEntry { - fn parse(input: ParseStream) -> syn::Result { - // Optionally parse visibility (e.g., `pub`) - let visibility: Option = input.parse().ok(); - - // Parse the pallet name (handling complex paths with generics and nested components) - let pallet_name = parse_complex_pallet_path(input)?; - - // Optionally parse the components in `{ Pallet, Call, Storage }` - let components = if input.peek(syn::token::Brace) { - let content; - braced!(content in input); - Some(content.parse::()?) - } else { - None - }; - - // Optionally parse the index if it's present - let index = if input.peek(Colon) { - input.parse::()?; - Some(input.parse::()?) - } else { - None - }; - - Ok(PalletEntry { - visibility, - pallet_name, - components, - index, - }) - } -} - -fn parse_complex_pallet_path(input: ParseStream) -> syn::Result { - // Parse the base path (e.g., `pallet_collective`) - let path = input.parse::()?; - - // If there are generics like `::`, handle them - if input.peek(syn::token::Lt) { - let _generics: syn::AngleBracketedGenericArguments = input.parse()?; - } - - // Now handle nested components like `{ Pallet, Call, Storage }` - if input.peek(syn::token::Brace) { - let content; - braced!(content in input); - let components: Punctuated = - content.parse_terminated(Ident::parse, Token![,])?; - println!("Parsed components: {:?}", components); - } - - Ok(path) -} - -#[derive(Debug)] -struct PalletComponents { - components: Punctuated, -} - -impl Parse for PalletComponents { - fn parse(input: ParseStream) -> syn::Result { - Ok(PalletComponents { - components: input.parse_terminated(Ident::parse, Token![,])?, - }) - } -} - #[cfg(test)] mod tests { use super::*; - fn lint_macro(input: &str) -> Result { - let item_macro: ItemMacro = syn::parse_str(input).expect("should only use on a macro"); + fn lint_macro(input: &str) -> Result<()> { + let item_macro: syn::ItemMacro = syn::parse_str(input).expect("should only use on a macro"); let mut visitor = ConstructRuntimeVisitor::default(); visitor.visit_item_macro(&item_macro); if !visitor.errors.is_empty() { @@ -208,52 +111,6 @@ mod tests { lint_macro(input).unwrap(); } - #[test] - fn test_with_generic_and_index() { - let input = r#" - construct_runtime!( - PalletA, - pallet_collective::::{ Pallet, Call, Storage }: 1 - ); - "#; - lint_macro(input).unwrap(); - } - - #[test] - fn test_with_nested_and_missing_index() { - let input = r#" - construct_runtime!( - PalletA, - pallet_collective::::{ Pallet, Call, Storage } - ); - "#; - lint_macro(input).unwrap_err(); - } - - #[test] - fn test_complex_construct_runtime_enum_should_fail() { - // This test should fail because there are no explicit indices for the pallets - let input = r#" - construct_runtime! { - pub enum Test { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Call, Config, Storage, Event}, - Triumvirate: pallet_collective::::{Pallet, Call, Storage, Origin, Event, Config}, - TriumvirateMembers: pallet_membership::::{Pallet, Call, Storage, Event, Config}, - Senate: pallet_collective::::{Pallet, Call, Storage, Origin, Event, Config}, - SenateMembers: pallet_membership::::{Pallet, Call, Storage, Event, Config}, - SubtensorModule: pallet_subtensor::{Pallet, Call, Storage, Event}, - Utility: pallet_utility::{Pallet, Call, Storage, Event}, - Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event}, - Preimage: pallet_preimage::{Pallet, Call, Storage, Event}, - } - } - "#; - - // This should fail because there are no explicit indices - lint_macro(input).unwrap_err(); - } - #[test] fn test_complex_construct_runtime_struct() { let input = r#" @@ -286,4 +143,26 @@ mod tests { lint_macro(input).unwrap(); } + + #[test] + fn test_complex_construct_runtime_enum_should_fail() { + let input = r#" + construct_runtime! { + pub enum Test { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Config, Storage, Event}, + Triumvirate: pallet_collective::::{Pallet, Call, Storage, Origin, Event, Config}, + TriumvirateMembers: pallet_membership::::{Pallet, Call, Storage, Event, Config}, + Senate: pallet_collective::::{Pallet, Call, Storage, Origin, Event, Config}, + SenateMembers: pallet_membership::::{Pallet, Call, Storage, Event, Config}, + SubtensorModule: pallet_subtensor::{Pallet, Call, Storage, Event}, + Utility: pallet_utility::{Pallet, Call, Storage, Event}, + Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event}, + Preimage: pallet_preimage::{Pallet, Call, Storage, Event}, + } + } + "#; + + lint_macro(input).unwrap_err(); + } } From 523d469eb143355677e98b5055cecd42d83e87f1 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 18 Sep 2024 16:56:15 -0400 Subject: [PATCH 19/53] passing :tada: :tada: :boom: !!! --- support/linting/src/pallet_index.rs | 58 +++++++++++++---------------- 1 file changed, 26 insertions(+), 32 deletions(-) diff --git a/support/linting/src/pallet_index.rs b/support/linting/src/pallet_index.rs index 80e5e234d..fe84aaea2 100644 --- a/support/linting/src/pallet_index.rs +++ b/support/linting/src/pallet_index.rs @@ -1,5 +1,6 @@ use super::*; use procedural_fork::exports::construct_runtime::parse::RuntimeDeclaration; +use quote::ToTokens; use syn::{visit::Visit, File}; pub struct RequireExplicitPalletIndex; @@ -33,7 +34,7 @@ impl<'ast> syn::visit::Visit<'ast> for ConstructRuntimeVisitor { Ok(runtime_decl) => { if let RuntimeDeclaration::Explicit(runtime) = runtime_decl { for pallet in runtime.pallets { - if pallet.index.is_none() { + if pallet.index == 0 { self.errors.push(syn::Error::new( pallet.name.span(), format!( @@ -57,7 +58,7 @@ impl<'ast> syn::visit::Visit<'ast> for ConstructRuntimeVisitor { mod tests { use super::*; - fn lint_macro(input: &str) -> Result<()> { + fn lint_macro(input: &str) -> Result { let item_macro: syn::ItemMacro = syn::parse_str(input).expect("should only use on a macro"); let mut visitor = ConstructRuntimeVisitor::default(); visitor.visit_item_macro(&item_macro); @@ -67,50 +68,43 @@ mod tests { Ok(()) } + // Corrected test cases + #[test] fn test_no_pallet_index() { + // Updated with valid `construct_runtime!` syntax let input = r#" - construct_runtime!( - PalletA, - PalletB - ); + construct_runtime! { + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + PalletA, + PalletB + } + } "#; lint_macro(input).unwrap_err(); } - #[test] - fn test_with_pallet_index() { - let input = r#" - construct_runtime!( - PalletA: 0, - PalletB: 1 - ); - "#; - lint_macro(input).unwrap(); - } - #[test] fn test_mixed_pallet_index() { let input = r#" - construct_runtime!( - PalletA, - PalletB: 1 - ); + construct_runtime! { + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + PalletA, + PalletB: 1 + } + } "#; lint_macro(input).unwrap_err(); } - #[test] - fn test_with_visibility_and_index() { - let input = r#" - construct_runtime!( - pub PalletA: 0, - PalletB: 1 - ); - "#; - lint_macro(input).unwrap(); - } - #[test] fn test_complex_construct_runtime_struct() { let input = r#" From 160c7b21c8dc6e9b47132aed95fcb30162da4bcb Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 18 Sep 2024 16:57:21 -0400 Subject: [PATCH 20/53] more test examples --- support/linting/src/pallet_index.rs | 85 ++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/support/linting/src/pallet_index.rs b/support/linting/src/pallet_index.rs index fe84aaea2..084f3be6c 100644 --- a/support/linting/src/pallet_index.rs +++ b/support/linting/src/pallet_index.rs @@ -72,7 +72,6 @@ mod tests { #[test] fn test_no_pallet_index() { - // Updated with valid `construct_runtime!` syntax let input = r#" construct_runtime! { pub enum Test where @@ -159,4 +158,88 @@ mod tests { lint_macro(input).unwrap_err(); } + + #[test] + fn test_with_multiple_instances() { + let input = r#" + construct_runtime! { + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + Instance1: pallet_collective::::{Pallet, Call, Storage} = 1, + Instance2: pallet_collective::::{Pallet, Call, Storage} = 2, + Balances: pallet_balances = 3 + } + } + "#; + lint_macro(input).unwrap(); + } + + #[test] + fn test_missing_pallet_parts() { + let input = r#" + construct_runtime! { + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + PalletA = 0, + PalletB + } + } + "#; + lint_macro(input).unwrap_err(); + } + + #[test] + fn test_with_expanded_pallet() { + let input = r#" + construct_runtime! { + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + ExpandedPallet: pallet_balances expanded::{Pallet, Call, Storage} = 1, + RegularPallet: pallet_sudo = 2 + } + } + "#; + lint_macro(input).unwrap(); + } + + #[test] + fn test_with_no_pallets() { + let input = r#" + construct_runtime! { + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + } + } + "#; + lint_macro(input).unwrap(); + } + + #[test] + fn test_with_pallet_alias() { + let input = r#" + construct_runtime! { + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + MyAlias: pallet_balances = 1, + OtherAlias: pallet_timestamp = 2 + } + } + "#; + lint_macro(input).unwrap(); + } } From 6b46371525a80fe47c74be7efd33b7ad9e6460bc Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 18 Sep 2024 16:59:19 -0400 Subject: [PATCH 21/53] refactor to use quote! directly --- support/linting/src/pallet_index.rs | 141 ++++++++++++++-------------- 1 file changed, 69 insertions(+), 72 deletions(-) diff --git a/support/linting/src/pallet_index.rs b/support/linting/src/pallet_index.rs index 084f3be6c..31e9c0af3 100644 --- a/support/linting/src/pallet_index.rs +++ b/support/linting/src/pallet_index.rs @@ -57,9 +57,10 @@ impl<'ast> syn::visit::Visit<'ast> for ConstructRuntimeVisitor { #[cfg(test)] mod tests { use super::*; + use quote::quote; - fn lint_macro(input: &str) -> Result { - let item_macro: syn::ItemMacro = syn::parse_str(input).expect("should only use on a macro"); + fn lint_macro(tokens: proc_macro2::TokenStream) -> Result { + let item_macro: syn::ItemMacro = syn::parse2(tokens).expect("should only use on a macro"); let mut visitor = ConstructRuntimeVisitor::default(); visitor.visit_item_macro(&item_macro); if !visitor.errors.is_empty() { @@ -68,11 +69,9 @@ mod tests { Ok(()) } - // Corrected test cases - #[test] fn test_no_pallet_index() { - let input = r#" + let tokens = quote! { construct_runtime! { pub enum Test where Block = Block, @@ -83,13 +82,13 @@ mod tests { PalletB } } - "#; - lint_macro(input).unwrap_err(); + }; + lint_macro(tokens).unwrap_err(); } #[test] fn test_mixed_pallet_index() { - let input = r#" + let tokens = quote! { construct_runtime! { pub enum Test where Block = Block, @@ -100,68 +99,66 @@ mod tests { PalletB: 1 } } - "#; - lint_macro(input).unwrap_err(); + }; + lint_macro(tokens).unwrap_err(); } #[test] fn test_complex_construct_runtime_struct() { - let input = r#" - construct_runtime! { - pub struct Runtime { - System : frame_system = 0, - RandomnessCollectiveFlip : pallet_insecure_randomness_collective_flip = 1, - Timestamp : pallet_timestamp = 2, - Aura : pallet_aura = 3, - Grandpa : pallet_grandpa = 4, - Balances : pallet_balances = 5, - TransactionPayment : pallet_transaction_payment = 6, - SubtensorModule : pallet_subtensor = 7, - Triumvirate : pallet_collective::::{ Pallet, Call, Storage, Origin, Event, Config } = 8, - TriumvirateMembers : pallet_membership::::{ Pallet, Call, Storage, Event, Config } = 9, - SenateMembers : pallet_membership::::{ Pallet, Call, Storage, Event, Config } = 10, - Utility : pallet_utility = 11, - Sudo : pallet_sudo = 12, - Multisig : pallet_multisig = 13, - Preimage : pallet_preimage = 14, - Scheduler : pallet_scheduler = 15, - Proxy : pallet_proxy = 16, - Registry : pallet_registry = 17, - Commitments : pallet_commitments = 18, - AdminUtils : pallet_admin_utils = 19, - SafeMode : pallet_safe_mode = 20 - } - } - "#; - - lint_macro(input).unwrap(); + let tokens = quote! { + construct_runtime! { + pub struct Runtime { + System : frame_system = 0, + RandomnessCollectiveFlip : pallet_insecure_randomness_collective_flip = 1, + Timestamp : pallet_timestamp = 2, + Aura : pallet_aura = 3, + Grandpa : pallet_grandpa = 4, + Balances : pallet_balances = 5, + TransactionPayment : pallet_transaction_payment = 6, + SubtensorModule : pallet_subtensor = 7, + Triumvirate : pallet_collective::::{ Pallet, Call, Storage, Origin, Event, Config } = 8, + TriumvirateMembers : pallet_membership::::{ Pallet, Call, Storage, Event, Config } = 9, + SenateMembers : pallet_membership::::{ Pallet, Call, Storage, Event, Config } = 10, + Utility : pallet_utility = 11, + Sudo : pallet_sudo = 12, + Multisig : pallet_multisig = 13, + Preimage : pallet_preimage = 14, + Scheduler : pallet_scheduler = 15, + Proxy : pallet_proxy = 16, + Registry : pallet_registry = 17, + Commitments : pallet_commitments = 18, + AdminUtils : pallet_admin_utils = 19, + SafeMode : pallet_safe_mode = 20 + } + } + }; + lint_macro(tokens).unwrap(); } #[test] fn test_complex_construct_runtime_enum_should_fail() { - let input = r#" - construct_runtime! { - pub enum Test { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Call, Config, Storage, Event}, - Triumvirate: pallet_collective::::{Pallet, Call, Storage, Origin, Event, Config}, - TriumvirateMembers: pallet_membership::::{Pallet, Call, Storage, Event, Config}, - Senate: pallet_collective::::{Pallet, Call, Storage, Origin, Event, Config}, - SenateMembers: pallet_membership::::{Pallet, Call, Storage, Event, Config}, - SubtensorModule: pallet_subtensor::{Pallet, Call, Storage, Event}, - Utility: pallet_utility::{Pallet, Call, Storage, Event}, - Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event}, - Preimage: pallet_preimage::{Pallet, Call, Storage, Event}, - } - } - "#; - - lint_macro(input).unwrap_err(); + let tokens = quote! { + construct_runtime! { + pub enum Test { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Config, Storage, Event}, + Triumvirate: pallet_collective::::{Pallet, Call, Storage, Origin, Event, Config}, + TriumvirateMembers: pallet_membership::::{Pallet, Call, Storage, Event, Config}, + Senate: pallet_collective::::{Pallet, Call, Storage, Origin, Event, Config}, + SenateMembers: pallet_membership::::{Pallet, Call, Storage, Event, Config}, + SubtensorModule: pallet_subtensor::{Pallet, Call, Storage, Event}, + Utility: pallet_utility::{Pallet, Call, Storage, Event}, + Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event}, + Preimage: pallet_preimage::{Pallet, Call, Storage, Event}, + } + } + }; + lint_macro(tokens).unwrap_err(); } #[test] fn test_with_multiple_instances() { - let input = r#" + let tokens = quote! { construct_runtime! { pub enum Test where Block = Block, @@ -173,13 +170,13 @@ mod tests { Balances: pallet_balances = 3 } } - "#; - lint_macro(input).unwrap(); + }; + lint_macro(tokens).unwrap(); } #[test] fn test_missing_pallet_parts() { - let input = r#" + let tokens = quote! { construct_runtime! { pub enum Test where Block = Block, @@ -190,13 +187,13 @@ mod tests { PalletB } } - "#; - lint_macro(input).unwrap_err(); + }; + lint_macro(tokens).unwrap_err(); } #[test] fn test_with_expanded_pallet() { - let input = r#" + let tokens = quote! { construct_runtime! { pub enum Test where Block = Block, @@ -207,13 +204,13 @@ mod tests { RegularPallet: pallet_sudo = 2 } } - "#; - lint_macro(input).unwrap(); + }; + lint_macro(tokens).unwrap(); } #[test] fn test_with_no_pallets() { - let input = r#" + let tokens = quote! { construct_runtime! { pub enum Test where Block = Block, @@ -222,13 +219,13 @@ mod tests { { } } - "#; - lint_macro(input).unwrap(); + }; + lint_macro(tokens).unwrap(); } #[test] fn test_with_pallet_alias() { - let input = r#" + let tokens = quote! { construct_runtime! { pub enum Test where Block = Block, @@ -239,7 +236,7 @@ mod tests { OtherAlias: pallet_timestamp = 2 } } - "#; - lint_macro(input).unwrap(); + }; + lint_macro(tokens).unwrap(); } } From ff1cb0de0140616976813bed541115a699ea7ead Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 18 Sep 2024 17:02:08 -0400 Subject: [PATCH 22/53] use explicit unwrap for testing purposes --- support/linting/src/pallet_index.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support/linting/src/pallet_index.rs b/support/linting/src/pallet_index.rs index 31e9c0af3..a97f1a95a 100644 --- a/support/linting/src/pallet_index.rs +++ b/support/linting/src/pallet_index.rs @@ -60,7 +60,7 @@ mod tests { use quote::quote; fn lint_macro(tokens: proc_macro2::TokenStream) -> Result { - let item_macro: syn::ItemMacro = syn::parse2(tokens).expect("should only use on a macro"); + let item_macro: syn::ItemMacro = syn::parse2(tokens).unwrap(); let mut visitor = ConstructRuntimeVisitor::default(); visitor.visit_item_macro(&item_macro); if !visitor.errors.is_empty() { From ed529c7e7e636e030623bb09f344803e09a1dbe0 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 18 Sep 2024 17:14:52 -0400 Subject: [PATCH 23/53] handle implicit --- support/linting/src/pallet_index.rs | 227 +++++++++++----------------- 1 file changed, 92 insertions(+), 135 deletions(-) diff --git a/support/linting/src/pallet_index.rs b/support/linting/src/pallet_index.rs index a97f1a95a..9f9b49094 100644 --- a/support/linting/src/pallet_index.rs +++ b/support/linting/src/pallet_index.rs @@ -8,7 +8,6 @@ pub struct RequireExplicitPalletIndex; impl Lint for RequireExplicitPalletIndex { fn lint(source: &File) -> Result { let mut visitor = ConstructRuntimeVisitor::default(); - visitor.visit_file(source); if !visitor.errors.is_empty() { @@ -32,18 +31,35 @@ impl<'ast> syn::visit::Visit<'ast> for ConstructRuntimeVisitor { // Attempt to parse the construct_runtime invocation. match syn::parse2::(tokens) { Ok(runtime_decl) => { - if let RuntimeDeclaration::Explicit(runtime) = runtime_decl { - for pallet in runtime.pallets { - if pallet.index == 0 { - self.errors.push(syn::Error::new( - pallet.name.span(), - format!( - "Pallet `{}` does not have an explicit index in construct_runtime!", - pallet.name.to_token_stream() - ), - )); + match runtime_decl { + RuntimeDeclaration::Explicit(runtime) => { + for pallet in runtime.pallets { + if pallet.index == 0 { + self.errors.push(syn::Error::new( + pallet.name.span(), + format!( + "Pallet `{}` does not have an explicit index in construct_runtime!", + pallet.name.to_token_stream() + ), + )); + } + } + } + RuntimeDeclaration::Implicit(runtime) => { + for pallet in runtime.pallets { + // Check if the index is missing (implicit declaration) + if pallet.index.is_none() { + self.errors.push(syn::Error::new( + pallet.name.span(), + format!( + "Pallet `{}` does not have an explicit index in the implicit construct_runtime!", + pallet.name.to_token_stream() + ), + )); + } } } + _ => {} } } Err(e) => self.errors.push(e), @@ -59,8 +75,8 @@ mod tests { use super::*; use quote::quote; - fn lint_macro(tokens: proc_macro2::TokenStream) -> Result { - let item_macro: syn::ItemMacro = syn::parse2(tokens).unwrap(); + fn lint_macro(input: proc_macro2::TokenStream) -> Result { + let item_macro: syn::ItemMacro = syn::parse2(input).unwrap(); let mut visitor = ConstructRuntimeVisitor::default(); visitor.visit_item_macro(&item_macro); if !visitor.errors.is_empty() { @@ -69,9 +85,12 @@ mod tests { Ok(()) } + // Corrected test cases + #[test] fn test_no_pallet_index() { - let tokens = quote! { + // Updated with valid `construct_runtime!` syntax + let input = quote! { construct_runtime! { pub enum Test where Block = Block, @@ -83,12 +102,12 @@ mod tests { } } }; - lint_macro(tokens).unwrap_err(); + lint_macro(input).unwrap_err(); } #[test] fn test_mixed_pallet_index() { - let tokens = quote! { + let input = quote! { construct_runtime! { pub enum Test where Block = Block, @@ -100,143 +119,81 @@ mod tests { } } }; - lint_macro(tokens).unwrap_err(); + lint_macro(input).unwrap_err(); } #[test] fn test_complex_construct_runtime_struct() { - let tokens = quote! { - construct_runtime! { - pub struct Runtime { - System : frame_system = 0, - RandomnessCollectiveFlip : pallet_insecure_randomness_collective_flip = 1, - Timestamp : pallet_timestamp = 2, - Aura : pallet_aura = 3, - Grandpa : pallet_grandpa = 4, - Balances : pallet_balances = 5, - TransactionPayment : pallet_transaction_payment = 6, - SubtensorModule : pallet_subtensor = 7, - Triumvirate : pallet_collective::::{ Pallet, Call, Storage, Origin, Event, Config } = 8, - TriumvirateMembers : pallet_membership::::{ Pallet, Call, Storage, Event, Config } = 9, - SenateMembers : pallet_membership::::{ Pallet, Call, Storage, Event, Config } = 10, - Utility : pallet_utility = 11, - Sudo : pallet_sudo = 12, - Multisig : pallet_multisig = 13, - Preimage : pallet_preimage = 14, - Scheduler : pallet_scheduler = 15, - Proxy : pallet_proxy = 16, - Registry : pallet_registry = 17, - Commitments : pallet_commitments = 18, - AdminUtils : pallet_admin_utils = 19, - SafeMode : pallet_safe_mode = 20 - } + let input = quote! { + construct_runtime! { + pub struct Runtime { + System : frame_system = 0, + RandomnessCollectiveFlip : pallet_insecure_randomness_collective_flip = 1, + Timestamp : pallet_timestamp = 2, + Aura : pallet_aura = 3, + Grandpa : pallet_grandpa = 4, + Balances : pallet_balances = 5, + TransactionPayment : pallet_transaction_payment = 6, + SubtensorModule : pallet_subtensor = 7, + Triumvirate : pallet_collective::::{ Pallet, Call, Storage, Origin, Event, Config } = 8, + TriumvirateMembers : pallet_membership::::{ Pallet, Call, Storage, Event, Config } = 9, + SenateMembers : pallet_membership::::{ Pallet, Call, Storage, Event, Config } = 10, + Utility : pallet_utility = 11, + Sudo : pallet_sudo = 12, + Multisig : pallet_multisig = 13, + Preimage : pallet_preimage = 14, + Scheduler : pallet_scheduler = 15, + Proxy : pallet_proxy = 16, + Registry : pallet_registry = 17, + Commitments : pallet_commitments = 18, + AdminUtils : pallet_admin_utils = 19, + SafeMode : pallet_safe_mode = 20 } + } }; - lint_macro(tokens).unwrap(); - } - #[test] - fn test_complex_construct_runtime_enum_should_fail() { - let tokens = quote! { - construct_runtime! { - pub enum Test { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Call, Config, Storage, Event}, - Triumvirate: pallet_collective::::{Pallet, Call, Storage, Origin, Event, Config}, - TriumvirateMembers: pallet_membership::::{Pallet, Call, Storage, Event, Config}, - Senate: pallet_collective::::{Pallet, Call, Storage, Origin, Event, Config}, - SenateMembers: pallet_membership::::{Pallet, Call, Storage, Event, Config}, - SubtensorModule: pallet_subtensor::{Pallet, Call, Storage, Event}, - Utility: pallet_utility::{Pallet, Call, Storage, Event}, - Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event}, - Preimage: pallet_preimage::{Pallet, Call, Storage, Event}, - } - } - }; - lint_macro(tokens).unwrap_err(); + lint_macro(input).unwrap(); } #[test] - fn test_with_multiple_instances() { - let tokens = quote! { - construct_runtime! { - pub enum Test where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic - { - Instance1: pallet_collective::::{Pallet, Call, Storage} = 1, - Instance2: pallet_collective::::{Pallet, Call, Storage} = 2, - Balances: pallet_balances = 3 - } - } - }; - lint_macro(tokens).unwrap(); - } - - #[test] - fn test_missing_pallet_parts() { - let tokens = quote! { - construct_runtime! { - pub enum Test where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic - { - PalletA = 0, - PalletB - } + fn test_complex_construct_runtime_enum_should_fail() { + let input = quote! { + construct_runtime! { + pub enum Test { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Config, Storage, Event}, + Triumvirate: pallet_collective::::{Pallet, Call, Storage, Origin, Event, Config}, + TriumvirateMembers: pallet_membership::::{Pallet, Call, Storage, Event, Config}, + Senate: pallet_collective::::{Pallet, Call, Storage, Origin, Event, Config}, + SenateMembers: pallet_membership::::{Pallet, Call, Storage, Event, Config}, + SubtensorModule: pallet_subtensor::{Pallet, Call, Storage, Event}, + Utility: pallet_utility::{Pallet, Call, Storage, Event}, + Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event}, + Preimage: pallet_preimage::{Pallet, Call, Storage, Event}, } + } }; - lint_macro(tokens).unwrap_err(); - } - #[test] - fn test_with_expanded_pallet() { - let tokens = quote! { - construct_runtime! { - pub enum Test where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic - { - ExpandedPallet: pallet_balances expanded::{Pallet, Call, Storage} = 1, - RegularPallet: pallet_sudo = 2 - } - } - }; - lint_macro(tokens).unwrap(); + lint_macro(input).unwrap_err(); } + // New test for implicit construct_runtime #[test] - fn test_with_no_pallets() { - let tokens = quote! { - construct_runtime! { - pub enum Test where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic - { - } + fn test_implicit_construct_runtime_should_fail() { + let input = quote! { + construct_runtime! { + pub struct Runtime { + System: frame_system = 0, + RandomnessCollectiveFlip: pallet_insecure_randomness_collective_flip = 1, + Timestamp: pallet_timestamp, + Aura: pallet_aura, + Grandpa: pallet_grandpa, + Balances: pallet_balances, + TransactionPayment: pallet_transaction_payment } + } }; - lint_macro(tokens).unwrap(); - } - #[test] - fn test_with_pallet_alias() { - let tokens = quote! { - construct_runtime! { - pub enum Test where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic - { - MyAlias: pallet_balances = 1, - OtherAlias: pallet_timestamp = 2 - } - } - }; - lint_macro(tokens).unwrap(); + lint_macro(input).unwrap_err(); } } From edbaf387e457396c689359ccf6e07d58913f6e4f Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 18 Sep 2024 17:22:27 -0400 Subject: [PATCH 24/53] fully handling all construct_runtime variants --- support/linting/src/pallet_index.rs | 68 ++++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 12 deletions(-) diff --git a/support/linting/src/pallet_index.rs b/support/linting/src/pallet_index.rs index 9f9b49094..00f3c5ea3 100644 --- a/support/linting/src/pallet_index.rs +++ b/support/linting/src/pallet_index.rs @@ -33,17 +33,10 @@ impl<'ast> syn::visit::Visit<'ast> for ConstructRuntimeVisitor { Ok(runtime_decl) => { match runtime_decl { RuntimeDeclaration::Explicit(runtime) => { - for pallet in runtime.pallets { - if pallet.index == 0 { - self.errors.push(syn::Error::new( - pallet.name.span(), - format!( - "Pallet `{}` does not have an explicit index in construct_runtime!", - pallet.name.to_token_stream() - ), - )); - } - } + self.check_pallets_for_index(&runtime.pallets); + } + RuntimeDeclaration::ExplicitExpanded(runtime) => { + self.check_pallets_for_index(&runtime.pallets); } RuntimeDeclaration::Implicit(runtime) => { for pallet in runtime.pallets { @@ -59,7 +52,6 @@ impl<'ast> syn::visit::Visit<'ast> for ConstructRuntimeVisitor { } } } - _ => {} } } Err(e) => self.errors.push(e), @@ -70,6 +62,25 @@ impl<'ast> syn::visit::Visit<'ast> for ConstructRuntimeVisitor { } } +impl ConstructRuntimeVisitor { + fn check_pallets_for_index( + &mut self, + pallets: &[procedural_fork::exports::construct_runtime::parse::Pallet], + ) { + for pallet in pallets { + if pallet.index == 0 { + self.errors.push(syn::Error::new( + pallet.name.span(), + format!( + "Pallet `{}` does not have an explicit index in construct_runtime!", + pallet.name.to_token_stream() + ), + )); + } + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -196,4 +207,37 @@ mod tests { lint_macro(input).unwrap_err(); } + + // Test for explicit expanded case that should pass + #[test] + fn test_explicit_expanded_runtime_with_correct_index_should_pass() { + let input = quote! { + construct_runtime! { + pub struct Runtime { + System : frame_system = 0, + Balances : pallet_balances = 1, + ExpandedPallet: pallet_collective::{ Pallet, Call, Config, Storage, Event } = 2 + } + } + }; + + lint_macro(input).unwrap(); + } + + // Test for explicit expanded case that should fail + #[test] + fn test_explicit_expanded_runtime_with_missing_index_should_fail() { + let input = quote! { + construct_runtime! { + pub struct Runtime { + System : frame_system = 0, + Balances : pallet_balances = 1, + ExpandedPallet: pallet_collective::{ Pallet, Call, Config, Storage, Event }, + FaultyPallet: pallet_sudo + } + } + }; + + lint_macro(input).unwrap_err(); + } } From b19854e159795a4a4b0bba24bbc85fe3cbc8494a Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 18 Sep 2024 17:29:16 -0400 Subject: [PATCH 25/53] fully working, need to fix existing construct_runtime!s now --- support/linting/src/pallet_index.rs | 60 ++++++++++++++++++----------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/support/linting/src/pallet_index.rs b/support/linting/src/pallet_index.rs index 00f3c5ea3..1513bc8bf 100644 --- a/support/linting/src/pallet_index.rs +++ b/support/linting/src/pallet_index.rs @@ -25,35 +25,38 @@ struct ConstructRuntimeVisitor { impl<'ast> syn::visit::Visit<'ast> for ConstructRuntimeVisitor { fn visit_item_macro(&mut self, node: &'ast syn::ItemMacro) { - if node.mac.path.is_ident("construct_runtime") { + let is_construct_runtime = node + .mac + .path + .segments + .last() + .map_or(false, |segment| segment.ident == "construct_runtime"); + + if is_construct_runtime { let tokens = node.mac.tokens.clone(); - // Attempt to parse the construct_runtime invocation. match syn::parse2::(tokens) { - Ok(runtime_decl) => { - match runtime_decl { - RuntimeDeclaration::Explicit(runtime) => { - self.check_pallets_for_index(&runtime.pallets); - } - RuntimeDeclaration::ExplicitExpanded(runtime) => { - self.check_pallets_for_index(&runtime.pallets); - } - RuntimeDeclaration::Implicit(runtime) => { - for pallet in runtime.pallets { - // Check if the index is missing (implicit declaration) - if pallet.index.is_none() { - self.errors.push(syn::Error::new( + Ok(runtime_decl) => match runtime_decl { + RuntimeDeclaration::Explicit(runtime) => { + self.check_pallets_for_index(&runtime.pallets); + } + RuntimeDeclaration::ExplicitExpanded(runtime) => { + self.check_pallets_for_index(&runtime.pallets); + } + RuntimeDeclaration::Implicit(runtime) => { + for pallet in runtime.pallets { + if pallet.index.is_none() { + self.errors.push(syn::Error::new( pallet.name.span(), format!( "Pallet `{}` does not have an explicit index in the implicit construct_runtime!", pallet.name.to_token_stream() ), )); - } } } } - } + }, Err(e) => self.errors.push(e), } } @@ -96,11 +99,8 @@ mod tests { Ok(()) } - // Corrected test cases - #[test] fn test_no_pallet_index() { - // Updated with valid `construct_runtime!` syntax let input = quote! { construct_runtime! { pub enum Test where @@ -188,7 +188,6 @@ mod tests { lint_macro(input).unwrap_err(); } - // New test for implicit construct_runtime #[test] fn test_implicit_construct_runtime_should_fail() { let input = quote! { @@ -208,7 +207,6 @@ mod tests { lint_macro(input).unwrap_err(); } - // Test for explicit expanded case that should pass #[test] fn test_explicit_expanded_runtime_with_correct_index_should_pass() { let input = quote! { @@ -224,7 +222,6 @@ mod tests { lint_macro(input).unwrap(); } - // Test for explicit expanded case that should fail #[test] fn test_explicit_expanded_runtime_with_missing_index_should_fail() { let input = quote! { @@ -240,4 +237,21 @@ mod tests { lint_macro(input).unwrap_err(); } + + #[test] + fn test_fully_qualified_construct_runtime() { + let input = quote! { + frame_support::construct_runtime! { + pub enum Test { + System: frame_system, + Balances: pallet_balances, + AdminUtils: pallet_admin_utils, + SubtensorModule: pallet_subtensor::{Pallet, Call, Storage, Event, Error}, + Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event}, + } + } + }; + + lint_macro(input).unwrap(); + } } From 6dd376e54dc31c96e7ead89887da4e132778ed9d Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 18 Sep 2024 18:03:29 -0400 Subject: [PATCH 26/53] always use explicit pallet indexing :cool: --- pallets/admin-utils/tests/mock.rs | 10 +++++----- pallets/collective/src/tests.rs | 10 +++++----- pallets/commitments/src/mock.rs | 4 ++-- pallets/commitments/src/tests.rs | 6 +++--- pallets/registry/src/mock.rs | 4 ++-- pallets/subtensor/tests/mock.rs | 20 ++++++++++---------- 6 files changed, 27 insertions(+), 27 deletions(-) diff --git a/pallets/admin-utils/tests/mock.rs b/pallets/admin-utils/tests/mock.rs index 342ed01cd..dca08ab72 100644 --- a/pallets/admin-utils/tests/mock.rs +++ b/pallets/admin-utils/tests/mock.rs @@ -23,11 +23,11 @@ type Block = frame_system::mocking::MockBlock; frame_support::construct_runtime!( pub enum Test { - System: frame_system, - Balances: pallet_balances, - AdminUtils: pallet_admin_utils, - SubtensorModule: pallet_subtensor::{Pallet, Call, Storage, Event, Error}, - Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event}, + System: frame_system = 1, + Balances: pallet_balances = 2, + AdminUtils: pallet_admin_utils = 3, + SubtensorModule: pallet_subtensor::{Pallet, Call, Storage, Event, Error} = 4, + Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event} = 5, } ); diff --git a/pallets/collective/src/tests.rs b/pallets/collective/src/tests.rs index 91fca58d4..4cc5f1bad 100644 --- a/pallets/collective/src/tests.rs +++ b/pallets/collective/src/tests.rs @@ -36,11 +36,11 @@ pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic}, - Collective: pallet_collective::::{Pallet, Call, Event, Origin, Config}, - CollectiveMajority: pallet_collective::::{Pallet, Call, Event, Origin, Config}, - DefaultCollective: pallet_collective::{Pallet, Call, Event, Origin, Config}, - Democracy: mock_democracy::{Pallet, Call, Event}, + System: frame_system::{Pallet, Call, Event} = 1, + Collective: pallet_collective::::{Pallet, Call, Event, Origin, Config} = 2, + CollectiveMajority: pallet_collective::::{Pallet, Call, Event, Origin, Config} = 3, + DefaultCollective: pallet_collective::{Pallet, Call, Event, Origin, Config} = 4, + Democracy: mock_democracy::{Pallet, Call, Event} = 5, } ); mod mock_democracy { diff --git a/pallets/commitments/src/mock.rs b/pallets/commitments/src/mock.rs index 47df72d5d..8866e1c0d 100644 --- a/pallets/commitments/src/mock.rs +++ b/pallets/commitments/src/mock.rs @@ -12,8 +12,8 @@ type Block = frame_system::mocking::MockBlock; frame_support::construct_runtime!( pub enum Test { - System: frame_system, - Commitments: pallet_commitments, + System: frame_system = 1, + Commitments: pallet_commitments = 2, } ); diff --git a/pallets/commitments/src/tests.rs b/pallets/commitments/src/tests.rs index 7449003f4..82f6c97a1 100644 --- a/pallets/commitments/src/tests.rs +++ b/pallets/commitments/src/tests.rs @@ -16,9 +16,9 @@ pub type UncheckedExtrinsic = sp_runtime::generic::UncheckedExtrinsic; frame_support::construct_runtime!( pub enum Test { - System: frame_system, - TemplateModule: pallet_template, + System: frame_system = 1, + TemplateModule: pallet_template = 2, } ); diff --git a/pallets/subtensor/tests/mock.rs b/pallets/subtensor/tests/mock.rs index aa93c3531..6f3b44383 100644 --- a/pallets/subtensor/tests/mock.rs +++ b/pallets/subtensor/tests/mock.rs @@ -24,16 +24,16 @@ type Block = frame_system::mocking::MockBlock; frame_support::construct_runtime!( pub enum Test { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Call, Config, Storage, Event}, - Triumvirate: pallet_collective::::{Pallet, Call, Storage, Origin, Event, Config}, - TriumvirateMembers: pallet_membership::::{Pallet, Call, Storage, Event, Config}, - Senate: pallet_collective::::{Pallet, Call, Storage, Origin, Event, Config}, - SenateMembers: pallet_membership::::{Pallet, Call, Storage, Event, Config}, - SubtensorModule: pallet_subtensor::{Pallet, Call, Storage, Event}, - Utility: pallet_utility::{Pallet, Call, Storage, Event}, - Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event}, - Preimage: pallet_preimage::{Pallet, Call, Storage, Event}, + System: frame_system::{Pallet, Call, Config, Storage, Event} = 1, + Balances: pallet_balances::{Pallet, Call, Config, Storage, Event} = 2, + Triumvirate: pallet_collective::::{Pallet, Call, Storage, Origin, Event, Config} = 3, + TriumvirateMembers: pallet_membership::::{Pallet, Call, Storage, Event, Config} = 4, + Senate: pallet_collective::::{Pallet, Call, Storage, Origin, Event, Config} = 5, + SenateMembers: pallet_membership::::{Pallet, Call, Storage, Event, Config} = 6, + SubtensorModule: pallet_subtensor::{Pallet, Call, Storage, Event} = 7, + Utility: pallet_utility::{Pallet, Call, Storage, Event} = 8, + Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event} = 9, + Preimage: pallet_preimage::{Pallet, Call, Storage, Event} = 10, } ); From a1bd6eb8f1b5a88b16f413fc08eb6aca9bbd68a0 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 19 Sep 2024 00:56:02 -0400 Subject: [PATCH 27/53] nearly --- support/linting/src/pallet_index.rs | 47 +++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/support/linting/src/pallet_index.rs b/support/linting/src/pallet_index.rs index 1513bc8bf..06264af6f 100644 --- a/support/linting/src/pallet_index.rs +++ b/support/linting/src/pallet_index.rs @@ -47,12 +47,12 @@ impl<'ast> syn::visit::Visit<'ast> for ConstructRuntimeVisitor { for pallet in runtime.pallets { if pallet.index.is_none() { self.errors.push(syn::Error::new( - pallet.name.span(), - format!( - "Pallet `{}` does not have an explicit index in the implicit construct_runtime!", - pallet.name.to_token_stream() - ), - )); + pallet.name.span(), + format!( + "Pallet `{}` does not have an explicit index in the implicit construct_runtime!", + pallet.name.to_token_stream() + ), + )); } } } @@ -71,6 +71,7 @@ impl ConstructRuntimeVisitor { pallets: &[procedural_fork::exports::construct_runtime::parse::Pallet], ) { for pallet in pallets { + // For explicit and expanded, ensure index is explicitly provided (not zero) if pallet.index == 0 { self.errors.push(syn::Error::new( pallet.name.span(), @@ -239,19 +240,41 @@ mod tests { } #[test] - fn test_fully_qualified_construct_runtime() { + fn test_fully_qualified_construct_runtime_should_pass() { let input = quote! { frame_support::construct_runtime! { pub enum Test { - System: frame_system, - Balances: pallet_balances, - AdminUtils: pallet_admin_utils, - SubtensorModule: pallet_subtensor::{Pallet, Call, Storage, Event, Error}, - Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event}, + System: frame_system = 1, + Balances: pallet_balances = 2, + AdminUtils: pallet_admin_utils = 3, + SubtensorModule: pallet_subtensor::{Pallet, Call, Storage, Event, Error} = 4, + Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event} = 5, } } }; lint_macro(input).unwrap(); } + + #[test] + fn test_mixed_pallets_should_fail() { + let input = quote! { + frame_support::construct_runtime! { + pub enum Test { + System: frame_system::{Pallet, Call, Config, Storage, Event} = 1, + Balances: pallet_balances::{Pallet, Call, Config, Storage, Event}, + Triumvirate: pallet_collective::::{Pallet, Call, Storage, Origin, Event, Config} = 3, + TriumvirateMembers: pallet_membership::::{Pallet, Call, Storage, Event, Config} = 4, + Senate: pallet_collective::::{Pallet, Call, Storage, Origin, Event, Config} = 5, + SenateMembers: pallet_membership::::{Pallet, Call, Storage, Event, Config} = 6, + SubtensorModule: pallet_subtensor::{Pallet, Call, Storage, Event} = 7, + Utility: pallet_utility::{Pallet, Call, Storage, Event} = 8, + Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event} = 9, + Preimage: pallet_preimage::{Pallet, Call, Storage, Event} = 10, + } + } + }; + + lint_macro(input).unwrap_err(); + } } From b39ef20286b64f0927264140fb31956143351690 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 19 Sep 2024 01:01:43 -0400 Subject: [PATCH 28/53] track original source text --- support/linting/src/pallet_index.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/support/linting/src/pallet_index.rs b/support/linting/src/pallet_index.rs index 06264af6f..bd6f829ac 100644 --- a/support/linting/src/pallet_index.rs +++ b/support/linting/src/pallet_index.rs @@ -7,7 +7,10 @@ pub struct RequireExplicitPalletIndex; impl Lint for RequireExplicitPalletIndex { fn lint(source: &File) -> Result { - let mut visitor = ConstructRuntimeVisitor::default(); + let mut visitor = ConstructRuntimeVisitor { + original_tokens: source.to_token_stream().to_string(), + errors: Vec::new(), + }; visitor.visit_file(source); if !visitor.errors.is_empty() { @@ -18,8 +21,8 @@ impl Lint for RequireExplicitPalletIndex { } } -#[derive(Default)] struct ConstructRuntimeVisitor { + original_tokens: String, errors: Vec, } @@ -44,6 +47,7 @@ impl<'ast> syn::visit::Visit<'ast> for ConstructRuntimeVisitor { self.check_pallets_for_index(&runtime.pallets); } RuntimeDeclaration::Implicit(runtime) => { + // Only implicit runtime allows `None` for index for pallet in runtime.pallets { if pallet.index.is_none() { self.errors.push(syn::Error::new( @@ -71,7 +75,7 @@ impl ConstructRuntimeVisitor { pallets: &[procedural_fork::exports::construct_runtime::parse::Pallet], ) { for pallet in pallets { - // For explicit and expanded, ensure index is explicitly provided (not zero) + // Check for explicit index and detect missing indices if pallet.index == 0 { self.errors.push(syn::Error::new( pallet.name.span(), @@ -92,7 +96,10 @@ mod tests { fn lint_macro(input: proc_macro2::TokenStream) -> Result { let item_macro: syn::ItemMacro = syn::parse2(input).unwrap(); - let mut visitor = ConstructRuntimeVisitor::default(); + let mut visitor = ConstructRuntimeVisitor { + original_tokens: item_macro.to_token_stream().to_string(), + errors: Vec::new(), + }; visitor.visit_item_macro(&item_macro); if !visitor.errors.is_empty() { return Err(visitor.errors); From d78c2260ad75080229ceae9b464dccb5fe82ca56 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 19 Sep 2024 01:06:10 -0400 Subject: [PATCH 29/53] 100% working --- support/linting/src/pallet_index.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/support/linting/src/pallet_index.rs b/support/linting/src/pallet_index.rs index bd6f829ac..0d5c4ad8b 100644 --- a/support/linting/src/pallet_index.rs +++ b/support/linting/src/pallet_index.rs @@ -76,7 +76,14 @@ impl ConstructRuntimeVisitor { ) { for pallet in pallets { // Check for explicit index and detect missing indices - if pallet.index == 0 { + if !self + .original_tokens + .contains(format!(" = {}", pallet.index).as_str()) + { + // ^ HACK: FRAME's parsing code does not allow us to differentiate between an + // automatically generated index and an explicitly provided index so we fall + // back to the original source code here. e.g. if index is 1, we will search + // for " = 1" in the original source code to determine if it was explicitly provided. self.errors.push(syn::Error::new( pallet.name.span(), format!( From b98ef6e3ac4bbaa0869dce64ed614f2c9ff5ccc0 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 19 Sep 2024 01:28:08 -0400 Subject: [PATCH 30/53] fixed --- support/linting/src/pallet_index.rs | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/support/linting/src/pallet_index.rs b/support/linting/src/pallet_index.rs index 0d5c4ad8b..b74e5a62c 100644 --- a/support/linting/src/pallet_index.rs +++ b/support/linting/src/pallet_index.rs @@ -1,4 +1,5 @@ use super::*; +use proc_macro2::TokenStream as TokenStream2; use procedural_fork::exports::construct_runtime::parse::RuntimeDeclaration; use quote::ToTokens; use syn::{visit::Visit, File}; @@ -7,10 +8,7 @@ pub struct RequireExplicitPalletIndex; impl Lint for RequireExplicitPalletIndex { fn lint(source: &File) -> Result { - let mut visitor = ConstructRuntimeVisitor { - original_tokens: source.to_token_stream().to_string(), - errors: Vec::new(), - }; + let mut visitor = ConstructRuntimeVisitor::new(source.to_token_stream()); visitor.visit_file(source); if !visitor.errors.is_empty() { @@ -70,6 +68,17 @@ impl<'ast> syn::visit::Visit<'ast> for ConstructRuntimeVisitor { } impl ConstructRuntimeVisitor { + fn new(original_tokens: impl Into) -> Self { + ConstructRuntimeVisitor { + original_tokens: { + let mut st = original_tokens.into().to_string(); + st.retain(|c| !c.is_whitespace()); + st + }, + errors: Vec::new(), + } + } + fn check_pallets_for_index( &mut self, pallets: &[procedural_fork::exports::construct_runtime::parse::Pallet], @@ -78,7 +87,7 @@ impl ConstructRuntimeVisitor { // Check for explicit index and detect missing indices if !self .original_tokens - .contains(format!(" = {}", pallet.index).as_str()) + .contains(format!("={},", pallet.index).as_str()) { // ^ HACK: FRAME's parsing code does not allow us to differentiate between an // automatically generated index and an explicitly provided index so we fall @@ -103,10 +112,7 @@ mod tests { fn lint_macro(input: proc_macro2::TokenStream) -> Result { let item_macro: syn::ItemMacro = syn::parse2(input).unwrap(); - let mut visitor = ConstructRuntimeVisitor { - original_tokens: item_macro.to_token_stream().to_string(), - errors: Vec::new(), - }; + let mut visitor = ConstructRuntimeVisitor::new(item_macro.to_token_stream()); visitor.visit_item_macro(&item_macro); if !visitor.errors.is_empty() { return Err(visitor.errors); From 5f457d8c55419817c646b8d1c201da6c95f5a7e1 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 19 Sep 2024 01:28:35 -0400 Subject: [PATCH 31/53] intentionally fail CI to make sure this is caught automatically --- pallets/subtensor/tests/mock.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/tests/mock.rs b/pallets/subtensor/tests/mock.rs index 6f3b44383..9555833de 100644 --- a/pallets/subtensor/tests/mock.rs +++ b/pallets/subtensor/tests/mock.rs @@ -25,7 +25,7 @@ frame_support::construct_runtime!( pub enum Test { System: frame_system::{Pallet, Call, Config, Storage, Event} = 1, - Balances: pallet_balances::{Pallet, Call, Config, Storage, Event} = 2, + Balances: pallet_balances::{Pallet, Call, Config, Storage, Event}, Triumvirate: pallet_collective::::{Pallet, Call, Storage, Origin, Event, Config} = 3, TriumvirateMembers: pallet_membership::::{Pallet, Call, Storage, Event, Config} = 4, Senate: pallet_collective::::{Pallet, Call, Storage, Origin, Event, Config} = 5, From a05aa8185dd6cbc5a17e6d66a495366f07a742f1 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 19 Sep 2024 01:52:29 -0400 Subject: [PATCH 32/53] fix procedural-fork tests --- support/procedural-fork/src/derive_impl.rs | 386 +++-- .../procedural-fork/src/pallet/parse/tasks.rs | 1489 +++++++++-------- .../src/pallet/parse/tests/mod.rs | 146 +- .../src/pallet/parse/tests/tasks.rs | 372 ++-- 4 files changed, 1222 insertions(+), 1171 deletions(-) diff --git a/support/procedural-fork/src/derive_impl.rs b/support/procedural-fork/src/derive_impl.rs index 54755f116..f064c8521 100644 --- a/support/procedural-fork/src/derive_impl.rs +++ b/support/procedural-fork/src/derive_impl.rs @@ -23,65 +23,67 @@ use proc_macro2::TokenStream as TokenStream2; use quote::{quote, ToTokens}; use std::collections::HashSet; use syn::{ - parse2, parse_quote, spanned::Spanned, token, Ident, ImplItem, ItemImpl, Path, Result, Token, + parse2, parse_quote, spanned::Spanned, token, Ident, ImplItem, ItemImpl, Path, Result, Token, }; mod keyword { - syn::custom_keyword!(inject_runtime_type); - syn::custom_keyword!(no_aggregated_types); + syn::custom_keyword!(inject_runtime_type); + syn::custom_keyword!(no_aggregated_types); } #[derive(derive_syn_parse::Parse, PartialEq, Eq)] pub enum PalletAttrType { - #[peek(keyword::inject_runtime_type, name = "inject_runtime_type")] - RuntimeType(keyword::inject_runtime_type), + #[peek(keyword::inject_runtime_type, name = "inject_runtime_type")] + RuntimeType(keyword::inject_runtime_type), } #[derive(derive_syn_parse::Parse)] pub struct PalletAttr { - _pound: Token![#], - #[bracket] - _bracket: token::Bracket, - #[inside(_bracket)] - typ: PalletAttrType, + _pound: Token![#], + #[bracket] + _bracket: token::Bracket, + #[inside(_bracket)] + typ: PalletAttrType, } fn is_runtime_type(item: &syn::ImplItemType) -> bool { - item.attrs.iter().any(|attr| { - if let Ok(PalletAttr { typ: PalletAttrType::RuntimeType(_), .. }) = - parse2::(attr.into_token_stream()) - { - return true - } - false - }) + item.attrs.iter().any(|attr| { + if let Ok(PalletAttr { + typ: PalletAttrType::RuntimeType(_), + .. + }) = parse2::(attr.into_token_stream()) + { + return true; + } + false + }) } #[derive(Parse, Debug)] pub struct DeriveImplAttrArgs { - pub default_impl_path: Path, - _as: Option, - #[parse_if(_as.is_some())] - pub disambiguation_path: Option, - _comma: Option, - #[parse_if(_comma.is_some())] - pub no_aggregated_types: Option, + pub default_impl_path: Path, + _as: Option, + #[parse_if(_as.is_some())] + pub disambiguation_path: Option, + _comma: Option, + #[parse_if(_comma.is_some())] + pub no_aggregated_types: Option, } impl ForeignPath for DeriveImplAttrArgs { - fn foreign_path(&self) -> &Path { - &self.default_impl_path - } + fn foreign_path(&self) -> &Path { + &self.default_impl_path + } } impl ToTokens for DeriveImplAttrArgs { - fn to_tokens(&self, tokens: &mut TokenStream2) { - tokens.extend(self.default_impl_path.to_token_stream()); - tokens.extend(self._as.to_token_stream()); - tokens.extend(self.disambiguation_path.to_token_stream()); - tokens.extend(self._comma.to_token_stream()); - tokens.extend(self.no_aggregated_types.to_token_stream()); - } + fn to_tokens(&self, tokens: &mut TokenStream2) { + tokens.extend(self.default_impl_path.to_token_stream()); + tokens.extend(self._as.to_token_stream()); + tokens.extend(self.disambiguation_path.to_token_stream()); + tokens.extend(self._comma.to_token_stream()); + tokens.extend(self.no_aggregated_types.to_token_stream()); + } } /// Gets the [`Ident`] representation of the given [`ImplItem`], if one exists. Otherwise @@ -90,13 +92,13 @@ impl ToTokens for DeriveImplAttrArgs { /// Used by [`combine_impls`] to determine whether we can compare [`ImplItem`]s by [`Ident`] /// or not. fn impl_item_ident(impl_item: &ImplItem) -> Option<&Ident> { - match impl_item { - ImplItem::Const(item) => Some(&item.ident), - ImplItem::Fn(item) => Some(&item.sig.ident), - ImplItem::Type(item) => Some(&item.ident), - ImplItem::Macro(item) => item.mac.path.get_ident(), - _ => None, - } + match impl_item { + ImplItem::Const(item) => Some(&item.ident), + ImplItem::Fn(item) => Some(&item.sig.ident), + ImplItem::Type(item) => Some(&item.ident), + ImplItem::Macro(item) => item.mac.path.get_ident(), + _ => None, + } } /// The real meat behind `derive_impl`. Takes in a `local_impl`, which is the impl for which we @@ -112,64 +114,68 @@ fn impl_item_ident(impl_item: &ImplItem) -> Option<&Ident> { /// into `local_impl`. Items that lack an ident and also exist verbatim in `local_impl` are not /// copied over. fn combine_impls( - local_impl: ItemImpl, - foreign_impl: ItemImpl, - default_impl_path: Path, - disambiguation_path: Path, - inject_runtime_types: bool, + local_impl: ItemImpl, + foreign_impl: ItemImpl, + default_impl_path: Path, + disambiguation_path: Path, + inject_runtime_types: bool, ) -> ItemImpl { - let (existing_local_keys, existing_unsupported_items): (HashSet, HashSet) = - local_impl - .items - .iter() - .cloned() - .partition(|impl_item| impl_item_ident(impl_item).is_some()); - let existing_local_keys: HashSet = existing_local_keys - .into_iter() - .filter_map(|item| impl_item_ident(&item).cloned()) - .collect(); - let mut final_impl = local_impl; - let extended_items = foreign_impl.items.into_iter().filter_map(|item| { - if let Some(ident) = impl_item_ident(&item) { - if existing_local_keys.contains(&ident) { - // do not copy colliding items that have an ident - return None - } - if let ImplItem::Type(typ) = item.clone() { - let cfg_attrs = typ - .attrs - .iter() - .filter(|attr| attr.path().get_ident().map_or(false, |ident| ident == "cfg")) - .map(|attr| attr.to_token_stream()); - if is_runtime_type(&typ) { - let item: ImplItem = if inject_runtime_types { - parse_quote! { - #( #cfg_attrs )* - type #ident = #ident; - } - } else { - item - }; - return Some(item) - } - // modify and insert uncolliding type items - let modified_item: ImplItem = parse_quote! { - #( #cfg_attrs )* - type #ident = <#default_impl_path as #disambiguation_path>::#ident; - }; - return Some(modified_item) - } - // copy uncolliding non-type items that have an ident - Some(item) - } else { - // do not copy colliding items that lack an ident - (!existing_unsupported_items.contains(&item)) - // copy uncolliding items without an ident verbatim - .then_some(item) - } - }); - final_impl.items.extend(extended_items); - final_impl + let (existing_local_keys, existing_unsupported_items): (HashSet, HashSet) = + local_impl + .items + .iter() + .cloned() + .partition(|impl_item| impl_item_ident(impl_item).is_some()); + let existing_local_keys: HashSet = existing_local_keys + .into_iter() + .filter_map(|item| impl_item_ident(&item).cloned()) + .collect(); + let mut final_impl = local_impl; + let extended_items = foreign_impl.items.into_iter().filter_map(|item| { + if let Some(ident) = impl_item_ident(&item) { + if existing_local_keys.contains(&ident) { + // do not copy colliding items that have an ident + return None; + } + if let ImplItem::Type(typ) = item.clone() { + let cfg_attrs = typ + .attrs + .iter() + .filter(|attr| { + attr.path() + .get_ident() + .map_or(false, |ident| ident == "cfg") + }) + .map(|attr| attr.to_token_stream()); + if is_runtime_type(&typ) { + let item: ImplItem = if inject_runtime_types { + parse_quote! { + #( #cfg_attrs )* + type #ident = #ident; + } + } else { + item + }; + return Some(item); + } + // modify and insert uncolliding type items + let modified_item: ImplItem = parse_quote! { + #( #cfg_attrs )* + type #ident = <#default_impl_path as #disambiguation_path>::#ident; + }; + return Some(modified_item); + } + // copy uncolliding non-type items that have an ident + Some(item) + } else { + // do not copy colliding items that lack an ident + (!existing_unsupported_items.contains(&item)) + // copy uncolliding items without an ident verbatim + .then_some(item) + } + }); + final_impl.items.extend(extended_items); + final_impl } /// Computes the disambiguation path for the `derive_impl` attribute macro. @@ -178,25 +184,26 @@ fn combine_impls( /// disambiguation is used as is. If not, we infer the disambiguation path from the /// `foreign_impl_path` and the computed scope. fn compute_disambiguation_path( - disambiguation_path: Option, - foreign_impl: ItemImpl, - default_impl_path: Path, + disambiguation_path: Option, + foreign_impl: ItemImpl, + default_impl_path: Path, ) -> Result { - match (disambiguation_path, foreign_impl.clone().trait_) { - (Some(disambiguation_path), _) => Ok(disambiguation_path), - (None, Some((_, foreign_impl_path, _))) => - if default_impl_path.segments.len() > 1 { - let scope = default_impl_path.segments.first(); - Ok(parse_quote!(#scope :: #foreign_impl_path)) - } else { - Ok(foreign_impl_path) - }, - _ => Err(syn::Error::new( - default_impl_path.span(), - "Impl statement must have a defined type being implemented \ + match (disambiguation_path, foreign_impl.clone().trait_) { + (Some(disambiguation_path), _) => Ok(disambiguation_path), + (None, Some((_, foreign_impl_path, _))) => { + if default_impl_path.segments.len() > 1 { + let scope = default_impl_path.segments.first(); + Ok(parse_quote!(#scope :: #foreign_impl_path)) + } else { + Ok(foreign_impl_path) + } + } + _ => Err(syn::Error::new( + default_impl_path.span(), + "Impl statement must have a defined type being implemented \ for a defined type such as `impl A for B`", - )), - } + )), + } } /// Internal implementation behind [`#[derive_impl(..)]`](`macro@crate::derive_impl`). @@ -211,93 +218,100 @@ fn compute_disambiguation_path( /// `disambiguation_path`: the module path of the external trait we will use to qualify /// defaults imported from the external `impl` statement pub fn derive_impl( - default_impl_path: TokenStream2, - foreign_tokens: TokenStream2, - local_tokens: TokenStream2, - disambiguation_path: Option, - no_aggregated_types: Option, + default_impl_path: TokenStream2, + foreign_tokens: TokenStream2, + local_tokens: TokenStream2, + disambiguation_path: Option, + no_aggregated_types: Option, ) -> Result { - let local_impl = parse2::(local_tokens)?; - let foreign_impl = parse2::(foreign_tokens)?; - let default_impl_path = parse2::(default_impl_path)?; + let local_impl = parse2::(local_tokens)?; + let foreign_impl = parse2::(foreign_tokens)?; + let default_impl_path = parse2::(default_impl_path)?; - let disambiguation_path = compute_disambiguation_path( - disambiguation_path, - foreign_impl.clone(), - default_impl_path.clone(), - )?; + let disambiguation_path = compute_disambiguation_path( + disambiguation_path, + foreign_impl.clone(), + default_impl_path.clone(), + )?; - // generate the combined impl - let combined_impl = combine_impls( - local_impl, - foreign_impl, - default_impl_path, - disambiguation_path, - no_aggregated_types.is_none(), - ); + // generate the combined impl + let combined_impl = combine_impls( + local_impl, + foreign_impl, + default_impl_path, + disambiguation_path, + no_aggregated_types.is_none(), + ); - Ok(quote!(#combined_impl)) + Ok(quote!(#combined_impl)) } #[test] fn test_derive_impl_attr_args_parsing() { - parse2::(quote!( - some::path::TestDefaultConfig as some::path::DefaultConfig - )) - .unwrap(); - parse2::(quote!( - frame_system::prelude::testing::TestDefaultConfig as DefaultConfig - )) - .unwrap(); - parse2::(quote!(Something as some::path::DefaultConfig)).unwrap(); - parse2::(quote!(Something as DefaultConfig)).unwrap(); - parse2::(quote!(DefaultConfig)).unwrap(); - assert!(parse2::(quote!()).is_err()); - assert!(parse2::(quote!(Config Config)).is_err()); + parse2::(quote!( + some::path::TestDefaultConfig as some::path::DefaultConfig + )) + .unwrap(); + parse2::(quote!( + frame_system::prelude::testing::TestDefaultConfig as DefaultConfig + )) + .unwrap(); + parse2::(quote!(Something as some::path::DefaultConfig)).unwrap(); + parse2::(quote!(Something as DefaultConfig)).unwrap(); + parse2::(quote!(DefaultConfig)).unwrap(); + assert!(parse2::(quote!()).is_err()); + assert!(parse2::(quote!(Config Config)).is_err()); } #[test] fn test_runtime_type_with_doc() { - trait TestTrait { - type Test; - } - #[allow(unused)] - struct TestStruct; - let p = parse2::(quote!( - impl TestTrait for TestStruct { - /// Some doc - #[inject_runtime_type] - type Test = u32; - } - )) - .unwrap(); - for item in p.items { - if let ImplItem::Type(typ) = item { - assert_eq!(is_runtime_type(&typ), true); - } - } + #[allow(unused)] + trait TestTrait { + type Test; + } + #[allow(unused)] + struct TestStruct; + let p = parse2::(quote!( + impl TestTrait for TestStruct { + /// Some doc + #[inject_runtime_type] + type Test = u32; + } + )) + .unwrap(); + for item in p.items { + if let ImplItem::Type(typ) = item { + assert_eq!(is_runtime_type(&typ), true); + } + } } #[test] fn test_disambiguation_path() { - let foreign_impl: ItemImpl = parse_quote!(impl SomeTrait for SomeType {}); - let default_impl_path: Path = parse_quote!(SomeScope::SomeType); + let foreign_impl: ItemImpl = parse_quote!(impl SomeTrait for SomeType {}); + let default_impl_path: Path = parse_quote!(SomeScope::SomeType); - // disambiguation path is specified - let disambiguation_path = compute_disambiguation_path( - Some(parse_quote!(SomeScope::SomePath)), - foreign_impl.clone(), - default_impl_path.clone(), - ); - assert_eq!(disambiguation_path.unwrap(), parse_quote!(SomeScope::SomePath)); + // disambiguation path is specified + let disambiguation_path = compute_disambiguation_path( + Some(parse_quote!(SomeScope::SomePath)), + foreign_impl.clone(), + default_impl_path.clone(), + ); + assert_eq!( + disambiguation_path.unwrap(), + parse_quote!(SomeScope::SomePath) + ); - // disambiguation path is not specified and the default_impl_path has more than one segment - let disambiguation_path = - compute_disambiguation_path(None, foreign_impl.clone(), default_impl_path.clone()); - assert_eq!(disambiguation_path.unwrap(), parse_quote!(SomeScope::SomeTrait)); + // disambiguation path is not specified and the default_impl_path has more than one segment + let disambiguation_path = + compute_disambiguation_path(None, foreign_impl.clone(), default_impl_path.clone()); + assert_eq!( + disambiguation_path.unwrap(), + parse_quote!(SomeScope::SomeTrait) + ); - // disambiguation path is not specified and the default_impl_path has only one segment - let disambiguation_path = - compute_disambiguation_path(None, foreign_impl.clone(), parse_quote!(SomeType)); - assert_eq!(disambiguation_path.unwrap(), parse_quote!(SomeTrait)); + // disambiguation path is not specified and the default_impl_path has only one segment + let disambiguation_path = + compute_disambiguation_path(None, foreign_impl.clone(), parse_quote!(SomeType)); + assert_eq!(disambiguation_path.unwrap(), parse_quote!(SomeTrait)); } diff --git a/support/procedural-fork/src/pallet/parse/tasks.rs b/support/procedural-fork/src/pallet/parse/tasks.rs index 6405bb415..f1728f824 100644 --- a/support/procedural-fork/src/pallet/parse/tasks.rs +++ b/support/procedural-fork/src/pallet/parse/tasks.rs @@ -30,96 +30,103 @@ use frame_support_procedural_tools::generate_access_from_frame_or_crate; use proc_macro2::TokenStream as TokenStream2; use quote::{quote, ToTokens}; use syn::{ - parse::ParseStream, - parse2, - spanned::Spanned, - token::{Bracket, Paren, PathSep, Pound}, - Attribute, Error, Expr, Ident, ImplItem, ImplItemFn, ItemEnum, ItemImpl, LitInt, Path, - PathArguments, Result, TypePath, + parse::ParseStream, + parse2, + spanned::Spanned, + token::{Bracket, Paren, PathSep, Pound}, + Attribute, Error, Expr, Ident, ImplItem, ImplItemFn, ItemEnum, ItemImpl, LitInt, Path, + PathArguments, Result, TypePath, }; pub mod keywords { - use syn::custom_keyword; + use syn::custom_keyword; - custom_keyword!(tasks_experimental); - custom_keyword!(task_enum); - custom_keyword!(task_list); - custom_keyword!(task_condition); - custom_keyword!(task_index); - custom_keyword!(task_weight); - custom_keyword!(pallet); + custom_keyword!(tasks_experimental); + custom_keyword!(task_enum); + custom_keyword!(task_list); + custom_keyword!(task_condition); + custom_keyword!(task_index); + custom_keyword!(task_weight); + custom_keyword!(pallet); } /// Represents the `#[pallet::tasks_experimental]` attribute and its attached item. Also includes /// metadata about the linked [`TaskEnumDef`] if applicable. #[derive(Clone, Debug)] pub struct TasksDef { - pub tasks_attr: Option, - pub tasks: Vec, - pub item_impl: ItemImpl, - /// Path to `frame_support` - pub scrate: Path, - pub enum_ident: Ident, - pub enum_arguments: PathArguments, + pub tasks_attr: Option, + pub tasks: Vec, + pub item_impl: ItemImpl, + /// Path to `frame_support` + pub scrate: Path, + pub enum_ident: Ident, + pub enum_arguments: PathArguments, } impl syn::parse::Parse for TasksDef { - fn parse(input: ParseStream) -> Result { - let item_impl: ItemImpl = input.parse()?; - let (tasks_attrs, normal_attrs) = partition_tasks_attrs(&item_impl); - let tasks_attr = match tasks_attrs.first() { - Some(attr) => Some(parse2::(attr.to_token_stream())?), - None => None, - }; - if let Some(extra_tasks_attr) = tasks_attrs.get(1) { - return Err(Error::new( - extra_tasks_attr.span(), - "unexpected extra `#[pallet::tasks_experimental]` attribute", - )) - } - let tasks: Vec = if tasks_attr.is_some() { - item_impl - .items - .clone() - .into_iter() - .filter(|impl_item| matches!(impl_item, ImplItem::Fn(_))) - .map(|item| parse2::(item.to_token_stream())) - .collect::>()? - } else { - Vec::new() - }; - let mut task_indices = HashSet::::new(); - for task in tasks.iter() { - let task_index = &task.index_attr.meta.index; - if !task_indices.insert(task_index.clone()) { - return Err(Error::new( - task_index.span(), - format!("duplicate task index `{}`", task_index), - )) - } - } - let mut item_impl = item_impl; - item_impl.attrs = normal_attrs; - - // we require the path on the impl to be a TypePath - let enum_path = parse2::(item_impl.self_ty.to_token_stream())?; - let segments = enum_path.path.segments.iter().collect::>(); - let (Some(last_seg), None) = (segments.get(0), segments.get(1)) else { - return Err(Error::new( - enum_path.span(), - "if specified manually, the task enum must be defined locally in this \ + fn parse(input: ParseStream) -> Result { + let item_impl: ItemImpl = input.parse()?; + let (tasks_attrs, normal_attrs) = partition_tasks_attrs(&item_impl); + let tasks_attr = match tasks_attrs.first() { + Some(attr) => Some(parse2::(attr.to_token_stream())?), + None => None, + }; + if let Some(extra_tasks_attr) = tasks_attrs.get(1) { + return Err(Error::new( + extra_tasks_attr.span(), + "unexpected extra `#[pallet::tasks_experimental]` attribute", + )); + } + let tasks: Vec = if tasks_attr.is_some() { + item_impl + .items + .clone() + .into_iter() + .filter(|impl_item| matches!(impl_item, ImplItem::Fn(_))) + .map(|item| parse2::(item.to_token_stream())) + .collect::>()? + } else { + Vec::new() + }; + let mut task_indices = HashSet::::new(); + for task in tasks.iter() { + let task_index = &task.index_attr.meta.index; + if !task_indices.insert(task_index.clone()) { + return Err(Error::new( + task_index.span(), + format!("duplicate task index `{}`", task_index), + )); + } + } + let mut item_impl = item_impl; + item_impl.attrs = normal_attrs; + + // we require the path on the impl to be a TypePath + let enum_path = parse2::(item_impl.self_ty.to_token_stream())?; + let segments = enum_path.path.segments.iter().collect::>(); + let (Some(last_seg), None) = (segments.get(0), segments.get(1)) else { + return Err(Error::new( + enum_path.span(), + "if specified manually, the task enum must be defined locally in this \ pallet and cannot be a re-export", - )) - }; - let enum_ident = last_seg.ident.clone(); - let enum_arguments = last_seg.arguments.clone(); - - // We do this here because it would be improper to do something fallible like this at - // the expansion phase. Fallible stuff should happen during parsing. - let scrate = generate_access_from_frame_or_crate("frame-support")?; - - Ok(TasksDef { tasks_attr, item_impl, tasks, scrate, enum_ident, enum_arguments }) - } + )); + }; + let enum_ident = last_seg.ident.clone(); + let enum_arguments = last_seg.arguments.clone(); + + // We do this here because it would be improper to do something fallible like this at + // the expansion phase. Fallible stuff should happen during parsing. + let scrate = generate_access_from_frame_or_crate("frame-support")?; + + Ok(TasksDef { + tasks_attr, + item_impl, + tasks, + scrate, + enum_ident, + enum_arguments, + }) + } } /// Parsing for a `#[pallet::tasks_experimental]` attr. @@ -148,821 +155,851 @@ pub type PalletTaskEnumAttr = PalletTaskAttr; /// attached `#[pallet::task_enum]` attribute. #[derive(Clone, Debug)] pub struct TaskEnumDef { - pub attr: Option, - pub item_enum: ItemEnum, - pub scrate: Path, - pub type_use_generics: TokenStream2, + pub attr: Option, + pub item_enum: ItemEnum, + pub scrate: Path, + pub type_use_generics: TokenStream2, } impl syn::parse::Parse for TaskEnumDef { - fn parse(input: ParseStream) -> Result { - let mut item_enum = input.parse::()?; - let attr = extract_pallet_attr(&mut item_enum)?; - let attr = match attr { - Some(attr) => Some(parse2(attr)?), - None => None, - }; + fn parse(input: ParseStream) -> Result { + let mut item_enum = input.parse::()?; + let attr = extract_pallet_attr(&mut item_enum)?; + let attr = match attr { + Some(attr) => Some(parse2(attr)?), + None => None, + }; - // We do this here because it would be improper to do something fallible like this at - // the expansion phase. Fallible stuff should happen during parsing. - let scrate = generate_access_from_frame_or_crate("frame-support")?; + // We do this here because it would be improper to do something fallible like this at + // the expansion phase. Fallible stuff should happen during parsing. + let scrate = generate_access_from_frame_or_crate("frame-support")?; - let type_use_generics = quote!(T); + let type_use_generics = quote!(T); - Ok(TaskEnumDef { attr, item_enum, scrate, type_use_generics }) - } + Ok(TaskEnumDef { + attr, + item_enum, + scrate, + type_use_generics, + }) + } } /// Represents an individual tasks within a [`TasksDef`]. #[derive(Debug, Clone)] pub struct TaskDef { - pub index_attr: TaskIndexAttr, - pub condition_attr: TaskConditionAttr, - pub list_attr: TaskListAttr, - pub weight_attr: TaskWeightAttr, - pub normal_attrs: Vec, - pub item: ImplItemFn, - pub arg_names: Vec, + pub index_attr: TaskIndexAttr, + pub condition_attr: TaskConditionAttr, + pub list_attr: TaskListAttr, + pub weight_attr: TaskWeightAttr, + pub normal_attrs: Vec, + pub item: ImplItemFn, + pub arg_names: Vec, } impl syn::parse::Parse for TaskDef { - fn parse(input: ParseStream) -> Result { - let item = input.parse::()?; - // we only want to activate TaskAttrType parsing errors for tasks-related attributes, - // so we filter them here - let (task_attrs, normal_attrs) = partition_task_attrs(&item); - - let task_attrs: Vec = task_attrs - .into_iter() - .map(|attr| parse2(attr.to_token_stream())) - .collect::>()?; - - let Some(index_attr) = task_attrs - .iter() - .find(|attr| matches!(attr.meta, TaskAttrMeta::TaskIndex(_))) - .cloned() - else { - return Err(Error::new( - item.sig.ident.span(), - "missing `#[pallet::task_index(..)]` attribute", - )) - }; - - let Some(condition_attr) = task_attrs - .iter() - .find(|attr| matches!(attr.meta, TaskAttrMeta::TaskCondition(_))) - .cloned() - else { - return Err(Error::new( - item.sig.ident.span(), - "missing `#[pallet::task_condition(..)]` attribute", - )) - }; - - let Some(list_attr) = task_attrs - .iter() - .find(|attr| matches!(attr.meta, TaskAttrMeta::TaskList(_))) - .cloned() - else { - return Err(Error::new( - item.sig.ident.span(), - "missing `#[pallet::task_list(..)]` attribute", - )) - }; - - let Some(weight_attr) = task_attrs - .iter() - .find(|attr| matches!(attr.meta, TaskAttrMeta::TaskWeight(_))) - .cloned() - else { - return Err(Error::new( - item.sig.ident.span(), - "missing `#[pallet::task_weight(..)]` attribute", - )) - }; - - if let Some(duplicate) = task_attrs - .iter() - .filter(|attr| matches!(attr.meta, TaskAttrMeta::TaskCondition(_))) - .collect::>() - .get(1) - { - return Err(Error::new( - duplicate.span(), - "unexpected extra `#[pallet::task_condition(..)]` attribute", - )) - } - - if let Some(duplicate) = task_attrs - .iter() - .filter(|attr| matches!(attr.meta, TaskAttrMeta::TaskList(_))) - .collect::>() - .get(1) - { - return Err(Error::new( - duplicate.span(), - "unexpected extra `#[pallet::task_list(..)]` attribute", - )) - } - - if let Some(duplicate) = task_attrs - .iter() - .filter(|attr| matches!(attr.meta, TaskAttrMeta::TaskIndex(_))) - .collect::>() - .get(1) - { - return Err(Error::new( - duplicate.span(), - "unexpected extra `#[pallet::task_index(..)]` attribute", - )) - } - - let mut arg_names = vec![]; - for input in item.sig.inputs.iter() { - match input { - syn::FnArg::Typed(pat_type) => match &*pat_type.pat { - syn::Pat::Ident(ident) => arg_names.push(ident.ident.clone()), - _ => return Err(Error::new(input.span(), "unexpected pattern type")), - }, - _ => return Err(Error::new(input.span(), "unexpected function argument type")), - } - } - - let index_attr = index_attr.try_into().expect("we check the type above; QED"); - let condition_attr = condition_attr.try_into().expect("we check the type above; QED"); - let list_attr = list_attr.try_into().expect("we check the type above; QED"); - let weight_attr = weight_attr.try_into().expect("we check the type above; QED"); - - Ok(TaskDef { - index_attr, - condition_attr, - list_attr, - weight_attr, - normal_attrs, - item, - arg_names, - }) - } + fn parse(input: ParseStream) -> Result { + let item = input.parse::()?; + // we only want to activate TaskAttrType parsing errors for tasks-related attributes, + // so we filter them here + let (task_attrs, normal_attrs) = partition_task_attrs(&item); + + let task_attrs: Vec = task_attrs + .into_iter() + .map(|attr| parse2(attr.to_token_stream())) + .collect::>()?; + + let Some(index_attr) = task_attrs + .iter() + .find(|attr| matches!(attr.meta, TaskAttrMeta::TaskIndex(_))) + .cloned() + else { + return Err(Error::new( + item.sig.ident.span(), + "missing `#[pallet::task_index(..)]` attribute", + )); + }; + + let Some(condition_attr) = task_attrs + .iter() + .find(|attr| matches!(attr.meta, TaskAttrMeta::TaskCondition(_))) + .cloned() + else { + return Err(Error::new( + item.sig.ident.span(), + "missing `#[pallet::task_condition(..)]` attribute", + )); + }; + + let Some(list_attr) = task_attrs + .iter() + .find(|attr| matches!(attr.meta, TaskAttrMeta::TaskList(_))) + .cloned() + else { + return Err(Error::new( + item.sig.ident.span(), + "missing `#[pallet::task_list(..)]` attribute", + )); + }; + + let Some(weight_attr) = task_attrs + .iter() + .find(|attr| matches!(attr.meta, TaskAttrMeta::TaskWeight(_))) + .cloned() + else { + return Err(Error::new( + item.sig.ident.span(), + "missing `#[pallet::task_weight(..)]` attribute", + )); + }; + + if let Some(duplicate) = task_attrs + .iter() + .filter(|attr| matches!(attr.meta, TaskAttrMeta::TaskCondition(_))) + .collect::>() + .get(1) + { + return Err(Error::new( + duplicate.span(), + "unexpected extra `#[pallet::task_condition(..)]` attribute", + )); + } + + if let Some(duplicate) = task_attrs + .iter() + .filter(|attr| matches!(attr.meta, TaskAttrMeta::TaskList(_))) + .collect::>() + .get(1) + { + return Err(Error::new( + duplicate.span(), + "unexpected extra `#[pallet::task_list(..)]` attribute", + )); + } + + if let Some(duplicate) = task_attrs + .iter() + .filter(|attr| matches!(attr.meta, TaskAttrMeta::TaskIndex(_))) + .collect::>() + .get(1) + { + return Err(Error::new( + duplicate.span(), + "unexpected extra `#[pallet::task_index(..)]` attribute", + )); + } + + let mut arg_names = vec![]; + for input in item.sig.inputs.iter() { + match input { + syn::FnArg::Typed(pat_type) => match &*pat_type.pat { + syn::Pat::Ident(ident) => arg_names.push(ident.ident.clone()), + _ => return Err(Error::new(input.span(), "unexpected pattern type")), + }, + _ => { + return Err(Error::new( + input.span(), + "unexpected function argument type", + )) + } + } + } + + let index_attr = index_attr.try_into().expect("we check the type above; QED"); + let condition_attr = condition_attr + .try_into() + .expect("we check the type above; QED"); + let list_attr = list_attr.try_into().expect("we check the type above; QED"); + let weight_attr = weight_attr + .try_into() + .expect("we check the type above; QED"); + + Ok(TaskDef { + index_attr, + condition_attr, + list_attr, + weight_attr, + normal_attrs, + item, + arg_names, + }) + } } /// The contents of a [`TasksDef`]-related attribute. #[derive(Parse, Debug, Clone)] pub enum TaskAttrMeta { - #[peek(keywords::task_list, name = "#[pallet::task_list(..)]")] - TaskList(TaskListAttrMeta), - #[peek(keywords::task_index, name = "#[pallet::task_index(..)")] - TaskIndex(TaskIndexAttrMeta), - #[peek(keywords::task_condition, name = "#[pallet::task_condition(..)")] - TaskCondition(TaskConditionAttrMeta), - #[peek(keywords::task_weight, name = "#[pallet::task_weight(..)")] - TaskWeight(TaskWeightAttrMeta), + #[peek(keywords::task_list, name = "#[pallet::task_list(..)]")] + TaskList(TaskListAttrMeta), + #[peek(keywords::task_index, name = "#[pallet::task_index(..)")] + TaskIndex(TaskIndexAttrMeta), + #[peek(keywords::task_condition, name = "#[pallet::task_condition(..)")] + TaskCondition(TaskConditionAttrMeta), + #[peek(keywords::task_weight, name = "#[pallet::task_weight(..)")] + TaskWeight(TaskWeightAttrMeta), } /// The contents of a `#[pallet::task_list]` attribute. #[derive(Parse, Debug, Clone)] pub struct TaskListAttrMeta { - pub task_list: keywords::task_list, - #[paren] - _paren: Paren, - #[inside(_paren)] - pub expr: Expr, + pub task_list: keywords::task_list, + #[paren] + _paren: Paren, + #[inside(_paren)] + pub expr: Expr, } /// The contents of a `#[pallet::task_index]` attribute. #[derive(Parse, Debug, Clone)] pub struct TaskIndexAttrMeta { - pub task_index: keywords::task_index, - #[paren] - _paren: Paren, - #[inside(_paren)] - pub index: LitInt, + pub task_index: keywords::task_index, + #[paren] + _paren: Paren, + #[inside(_paren)] + pub index: LitInt, } /// The contents of a `#[pallet::task_condition]` attribute. #[derive(Parse, Debug, Clone)] pub struct TaskConditionAttrMeta { - pub task_condition: keywords::task_condition, - #[paren] - _paren: Paren, - #[inside(_paren)] - pub expr: Expr, + pub task_condition: keywords::task_condition, + #[paren] + _paren: Paren, + #[inside(_paren)] + pub expr: Expr, } /// The contents of a `#[pallet::task_weight]` attribute. #[derive(Parse, Debug, Clone)] pub struct TaskWeightAttrMeta { - pub task_weight: keywords::task_weight, - #[paren] - _paren: Paren, - #[inside(_paren)] - pub expr: Expr, + pub task_weight: keywords::task_weight, + #[paren] + _paren: Paren, + #[inside(_paren)] + pub expr: Expr, } /// The contents of a `#[pallet::task]` attribute. #[derive(Parse, Debug, Clone)] pub struct PalletTaskAttr { - pub pound: Pound, - #[bracket] - _bracket: Bracket, - #[inside(_bracket)] - pub pallet: keywords::pallet, - #[inside(_bracket)] - pub colons: PathSep, - #[inside(_bracket)] - pub meta: T, + pub pound: Pound, + #[bracket] + _bracket: Bracket, + #[inside(_bracket)] + pub pallet: keywords::pallet, + #[inside(_bracket)] + pub colons: PathSep, + #[inside(_bracket)] + pub meta: T, } impl ToTokens for TaskListAttrMeta { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let task_list = self.task_list; - let expr = &self.expr; - tokens.extend(quote!(#task_list(#expr))); - } + fn to_tokens(&self, tokens: &mut TokenStream2) { + let task_list = self.task_list; + let expr = &self.expr; + tokens.extend(quote!(#task_list(#expr))); + } } impl ToTokens for TaskConditionAttrMeta { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let task_condition = self.task_condition; - let expr = &self.expr; - tokens.extend(quote!(#task_condition(#expr))); - } + fn to_tokens(&self, tokens: &mut TokenStream2) { + let task_condition = self.task_condition; + let expr = &self.expr; + tokens.extend(quote!(#task_condition(#expr))); + } } impl ToTokens for TaskWeightAttrMeta { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let task_weight = self.task_weight; - let expr = &self.expr; - tokens.extend(quote!(#task_weight(#expr))); - } + fn to_tokens(&self, tokens: &mut TokenStream2) { + let task_weight = self.task_weight; + let expr = &self.expr; + tokens.extend(quote!(#task_weight(#expr))); + } } impl ToTokens for TaskIndexAttrMeta { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let task_index = self.task_index; - let index = &self.index; - tokens.extend(quote!(#task_index(#index))) - } + fn to_tokens(&self, tokens: &mut TokenStream2) { + let task_index = self.task_index; + let index = &self.index; + tokens.extend(quote!(#task_index(#index))) + } } impl ToTokens for TaskAttrMeta { - fn to_tokens(&self, tokens: &mut TokenStream2) { - match self { - TaskAttrMeta::TaskList(list) => tokens.extend(list.to_token_stream()), - TaskAttrMeta::TaskIndex(index) => tokens.extend(index.to_token_stream()), - TaskAttrMeta::TaskCondition(condition) => tokens.extend(condition.to_token_stream()), - TaskAttrMeta::TaskWeight(weight) => tokens.extend(weight.to_token_stream()), - } - } + fn to_tokens(&self, tokens: &mut TokenStream2) { + match self { + TaskAttrMeta::TaskList(list) => tokens.extend(list.to_token_stream()), + TaskAttrMeta::TaskIndex(index) => tokens.extend(index.to_token_stream()), + TaskAttrMeta::TaskCondition(condition) => tokens.extend(condition.to_token_stream()), + TaskAttrMeta::TaskWeight(weight) => tokens.extend(weight.to_token_stream()), + } + } } impl ToTokens for PalletTaskAttr { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let pound = self.pound; - let pallet = self.pallet; - let colons = self.colons; - let meta = &self.meta; - tokens.extend(quote!(#pound[#pallet #colons #meta])); - } + fn to_tokens(&self, tokens: &mut TokenStream2) { + let pound = self.pound; + let pallet = self.pallet; + let colons = self.colons; + let meta = &self.meta; + tokens.extend(quote!(#pound[#pallet #colons #meta])); + } } impl TryFrom> for TaskIndexAttr { - type Error = syn::Error; - - fn try_from(value: PalletTaskAttr) -> Result { - let pound = value.pound; - let pallet = value.pallet; - let colons = value.colons; - match value.meta { - TaskAttrMeta::TaskIndex(meta) => parse2(quote!(#pound[#pallet #colons #meta])), - _ => - return Err(Error::new( - value.span(), - format!("`{:?}` cannot be converted to a `TaskIndexAttr`", value.meta), - )), - } - } + type Error = syn::Error; + + fn try_from(value: PalletTaskAttr) -> Result { + let pound = value.pound; + let pallet = value.pallet; + let colons = value.colons; + match value.meta { + TaskAttrMeta::TaskIndex(meta) => parse2(quote!(#pound[#pallet #colons #meta])), + _ => { + return Err(Error::new( + value.span(), + format!( + "`{:?}` cannot be converted to a `TaskIndexAttr`", + value.meta + ), + )) + } + } + } } impl TryFrom> for TaskConditionAttr { - type Error = syn::Error; - - fn try_from(value: PalletTaskAttr) -> Result { - let pound = value.pound; - let pallet = value.pallet; - let colons = value.colons; - match value.meta { - TaskAttrMeta::TaskCondition(meta) => parse2(quote!(#pound[#pallet #colons #meta])), - _ => - return Err(Error::new( - value.span(), - format!("`{:?}` cannot be converted to a `TaskConditionAttr`", value.meta), - )), - } - } + type Error = syn::Error; + + fn try_from(value: PalletTaskAttr) -> Result { + let pound = value.pound; + let pallet = value.pallet; + let colons = value.colons; + match value.meta { + TaskAttrMeta::TaskCondition(meta) => parse2(quote!(#pound[#pallet #colons #meta])), + _ => { + return Err(Error::new( + value.span(), + format!( + "`{:?}` cannot be converted to a `TaskConditionAttr`", + value.meta + ), + )) + } + } + } } impl TryFrom> for TaskWeightAttr { - type Error = syn::Error; - - fn try_from(value: PalletTaskAttr) -> Result { - let pound = value.pound; - let pallet = value.pallet; - let colons = value.colons; - match value.meta { - TaskAttrMeta::TaskWeight(meta) => parse2(quote!(#pound[#pallet #colons #meta])), - _ => - return Err(Error::new( - value.span(), - format!("`{:?}` cannot be converted to a `TaskWeightAttr`", value.meta), - )), - } - } + type Error = syn::Error; + + fn try_from(value: PalletTaskAttr) -> Result { + let pound = value.pound; + let pallet = value.pallet; + let colons = value.colons; + match value.meta { + TaskAttrMeta::TaskWeight(meta) => parse2(quote!(#pound[#pallet #colons #meta])), + _ => { + return Err(Error::new( + value.span(), + format!( + "`{:?}` cannot be converted to a `TaskWeightAttr`", + value.meta + ), + )) + } + } + } } impl TryFrom> for TaskListAttr { - type Error = syn::Error; - - fn try_from(value: PalletTaskAttr) -> Result { - let pound = value.pound; - let pallet = value.pallet; - let colons = value.colons; - match value.meta { - TaskAttrMeta::TaskList(meta) => parse2(quote!(#pound[#pallet #colons #meta])), - _ => - return Err(Error::new( - value.span(), - format!("`{:?}` cannot be converted to a `TaskListAttr`", value.meta), - )), - } - } + type Error = syn::Error; + + fn try_from(value: PalletTaskAttr) -> Result { + let pound = value.pound; + let pallet = value.pallet; + let colons = value.colons; + match value.meta { + TaskAttrMeta::TaskList(meta) => parse2(quote!(#pound[#pallet #colons #meta])), + _ => { + return Err(Error::new( + value.span(), + format!("`{:?}` cannot be converted to a `TaskListAttr`", value.meta), + )) + } + } + } } fn extract_pallet_attr(item_enum: &mut ItemEnum) -> Result> { - let mut duplicate = None; - let mut attr = None; - item_enum.attrs = item_enum - .attrs - .iter() - .filter(|found_attr| { - let segs = found_attr - .path() - .segments - .iter() - .map(|seg| seg.ident.clone()) - .collect::>(); - let (Some(seg1), Some(_), None) = (segs.get(0), segs.get(1), segs.get(2)) else { - return true - }; - if seg1 != "pallet" { - return true - } - if attr.is_some() { - duplicate = Some(found_attr.span()); - } - attr = Some(found_attr.to_token_stream()); - false - }) - .cloned() - .collect(); - if let Some(span) = duplicate { - return Err(Error::new(span, "only one `#[pallet::_]` attribute is supported on this item")) - } - Ok(attr) + let mut duplicate = None; + let mut attr = None; + item_enum.attrs = item_enum + .attrs + .iter() + .filter(|found_attr| { + let segs = found_attr + .path() + .segments + .iter() + .map(|seg| seg.ident.clone()) + .collect::>(); + let (Some(seg1), Some(_), None) = (segs.get(0), segs.get(1), segs.get(2)) else { + return true; + }; + if seg1 != "pallet" { + return true; + } + if attr.is_some() { + duplicate = Some(found_attr.span()); + } + attr = Some(found_attr.to_token_stream()); + false + }) + .cloned() + .collect(); + if let Some(span) = duplicate { + return Err(Error::new( + span, + "only one `#[pallet::_]` attribute is supported on this item", + )); + } + Ok(attr) } fn partition_tasks_attrs(item_impl: &ItemImpl) -> (Vec, Vec) { - item_impl.attrs.clone().into_iter().partition(|attr| { - let mut path_segs = attr.path().segments.iter(); - let (Some(prefix), Some(suffix), None) = - (path_segs.next(), path_segs.next(), path_segs.next()) - else { - return false - }; - prefix.ident == "pallet" && suffix.ident == "tasks_experimental" - }) + item_impl.attrs.clone().into_iter().partition(|attr| { + let mut path_segs = attr.path().segments.iter(); + let (Some(prefix), Some(suffix), None) = + (path_segs.next(), path_segs.next(), path_segs.next()) + else { + return false; + }; + prefix.ident == "pallet" && suffix.ident == "tasks_experimental" + }) } fn partition_task_attrs(item: &ImplItemFn) -> (Vec, Vec) { - item.attrs.clone().into_iter().partition(|attr| { - let mut path_segs = attr.path().segments.iter(); - let (Some(prefix), Some(suffix)) = (path_segs.next(), path_segs.next()) else { - return false - }; - // N.B: the `PartialEq` impl between `Ident` and `&str` is more efficient than - // parsing and makes no stack or heap allocations - prefix.ident == "pallet" && - (suffix.ident == "tasks_experimental" || - suffix.ident == "task_list" || - suffix.ident == "task_condition" || - suffix.ident == "task_weight" || - suffix.ident == "task_index") - }) + item.attrs.clone().into_iter().partition(|attr| { + let mut path_segs = attr.path().segments.iter(); + let (Some(prefix), Some(suffix)) = (path_segs.next(), path_segs.next()) else { + return false; + }; + // N.B: the `PartialEq` impl between `Ident` and `&str` is more efficient than + // parsing and makes no stack or heap allocations + prefix.ident == "pallet" + && (suffix.ident == "tasks_experimental" + || suffix.ident == "task_list" + || suffix.ident == "task_condition" + || suffix.ident == "task_weight" + || suffix.ident == "task_index") + }) } #[test] fn test_parse_task_list_() { - parse2::(quote!(#[pallet::task_list(Something::iter())])).unwrap(); - parse2::(quote!(#[pallet::task_list(Numbers::::iter_keys())])).unwrap(); - parse2::(quote!(#[pallet::task_list(iter())])).unwrap(); - assert_parse_error_matches!( - parse2::(quote!(#[pallet::task_list()])), - "expected an expression" - ); - assert_parse_error_matches!( - parse2::(quote!(#[pallet::task_list])), - "expected parentheses" - ); + parse2::(quote!(#[pallet::task_list(Something::iter())])).unwrap(); + parse2::(quote!(#[pallet::task_list(Numbers::::iter_keys())])).unwrap(); + parse2::(quote!(#[pallet::task_list(iter())])).unwrap(); + assert_parse_error_matches!( + parse2::(quote!(#[pallet::task_list()])), + "expected an expression" + ); + assert_parse_error_matches!( + parse2::(quote!(#[pallet::task_list])), + "expected parentheses" + ); } #[test] fn test_parse_task_index() { - parse2::(quote!(#[pallet::task_index(3)])).unwrap(); - parse2::(quote!(#[pallet::task_index(0)])).unwrap(); - parse2::(quote!(#[pallet::task_index(17)])).unwrap(); - assert_parse_error_matches!( - parse2::(quote!(#[pallet::task_index])), - "expected parentheses" - ); - assert_parse_error_matches!( - parse2::(quote!(#[pallet::task_index("hey")])), - "expected integer literal" - ); - assert_parse_error_matches!( - parse2::(quote!(#[pallet::task_index(0.3)])), - "expected integer literal" - ); + parse2::(quote!(#[pallet::task_index(3)])).unwrap(); + parse2::(quote!(#[pallet::task_index(0)])).unwrap(); + parse2::(quote!(#[pallet::task_index(17)])).unwrap(); + assert_parse_error_matches!( + parse2::(quote!(#[pallet::task_index])), + "expected parentheses" + ); + assert_parse_error_matches!( + parse2::(quote!(#[pallet::task_index("hey")])), + "expected integer literal" + ); + assert_parse_error_matches!( + parse2::(quote!(#[pallet::task_index(0.3)])), + "expected integer literal" + ); } #[test] fn test_parse_task_condition() { - parse2::(quote!(#[pallet::task_condition(|x| x.is_some())])).unwrap(); - parse2::(quote!(#[pallet::task_condition(|_x| some_expr())])).unwrap(); - parse2::(quote!(#[pallet::task_condition(|| some_expr())])).unwrap(); - parse2::(quote!(#[pallet::task_condition(some_expr())])).unwrap(); + parse2::(quote!(#[pallet::task_condition(|x| x.is_some())])).unwrap(); + parse2::(quote!(#[pallet::task_condition(|_x| some_expr())])).unwrap(); + parse2::(quote!(#[pallet::task_condition(|| some_expr())])).unwrap(); + parse2::(quote!(#[pallet::task_condition(some_expr())])).unwrap(); } #[test] fn test_parse_tasks_attr() { - parse2::(quote!(#[pallet::tasks_experimental])).unwrap(); - assert_parse_error_matches!( - parse2::(quote!(#[pallet::taskss])), - "expected `tasks_experimental`" - ); - assert_parse_error_matches!( - parse2::(quote!(#[pallet::tasks_])), - "expected `tasks_experimental`" - ); - assert_parse_error_matches!( - parse2::(quote!(#[pal::tasks])), - "expected `pallet`" - ); - assert_parse_error_matches!( - parse2::(quote!(#[pallet::tasks_experimental()])), - "unexpected token" - ); + parse2::(quote!(#[pallet::tasks_experimental])).unwrap(); + assert_parse_error_matches!( + parse2::(quote!(#[pallet::taskss])), + "expected `tasks_experimental`" + ); + assert_parse_error_matches!( + parse2::(quote!(#[pallet::tasks_])), + "expected `tasks_experimental`" + ); + assert_parse_error_matches!( + parse2::(quote!(#[pal::tasks])), + "expected `pallet`" + ); + assert_parse_error_matches!( + parse2::(quote!(#[pallet::tasks_experimental()])), + "unexpected token" + ); } #[test] fn test_parse_tasks_def_basic() { - simulate_manifest_dir("../../examples/basic", || { - let parsed = parse2::(quote! { - #[pallet::tasks_experimental] - impl, I: 'static> Pallet { - /// Add a pair of numbers into the totals and remove them. - #[pallet::task_list(Numbers::::iter_keys())] - #[pallet::task_condition(|i| Numbers::::contains_key(i))] - #[pallet::task_index(0)] - #[pallet::task_weight(0)] - pub fn add_number_into_total(i: u32) -> DispatchResult { - let v = Numbers::::take(i).ok_or(Error::::NotFound)?; - Total::::mutate(|(total_keys, total_values)| { - *total_keys += i; - *total_values += v; - }); - Ok(()) - } - } - }) - .unwrap(); - assert_eq!(parsed.tasks.len(), 1); - }); + simulate_manifest_dir("../../pallets/subtensor", || { + let parsed = parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + /// Add a pair of numbers into the totals and remove them. + #[pallet::task_list(Numbers::::iter_keys())] + #[pallet::task_condition(|i| Numbers::::contains_key(i))] + #[pallet::task_index(0)] + #[pallet::task_weight(0)] + pub fn add_number_into_total(i: u32) -> DispatchResult { + let v = Numbers::::take(i).ok_or(Error::::NotFound)?; + Total::::mutate(|(total_keys, total_values)| { + *total_keys += i; + *total_values += v; + }); + Ok(()) + } + } + }) + .unwrap(); + assert_eq!(parsed.tasks.len(), 1); + }); } #[test] fn test_parse_tasks_def_basic_increment_decrement() { - simulate_manifest_dir("../../examples/basic", || { - let parsed = parse2::(quote! { - #[pallet::tasks_experimental] - impl, I: 'static> Pallet { - /// Get the value and check if it can be incremented - #[pallet::task_index(0)] - #[pallet::task_condition(|| { - let value = Value::::get().unwrap(); - value < 255 - })] - #[pallet::task_list(Vec::>::new())] - #[pallet::task_weight(0)] - fn increment() -> DispatchResult { - let value = Value::::get().unwrap_or_default(); - if value >= 255 { - Err(Error::::ValueOverflow.into()) - } else { - let new_val = value.checked_add(1).ok_or(Error::::ValueOverflow)?; - Value::::put(new_val); - Pallet::::deposit_event(Event::Incremented { new_val }); - Ok(()) - } - } - - // Get the value and check if it can be decremented - #[pallet::task_index(1)] - #[pallet::task_condition(|| { - let value = Value::::get().unwrap(); - value > 0 - })] - #[pallet::task_list(Vec::>::new())] - #[pallet::task_weight(0)] - fn decrement() -> DispatchResult { - let value = Value::::get().unwrap_or_default(); - if value == 0 { - Err(Error::::ValueUnderflow.into()) - } else { - let new_val = value.checked_sub(1).ok_or(Error::::ValueUnderflow)?; - Value::::put(new_val); - Pallet::::deposit_event(Event::Decremented { new_val }); - Ok(()) - } - } - } - }) - .unwrap(); - assert_eq!(parsed.tasks.len(), 2); - }); + simulate_manifest_dir("../../pallets/subtensor", || { + let parsed = parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + /// Get the value and check if it can be incremented + #[pallet::task_index(0)] + #[pallet::task_condition(|| { + let value = Value::::get().unwrap(); + value < 255 + })] + #[pallet::task_list(Vec::>::new())] + #[pallet::task_weight(0)] + fn increment() -> DispatchResult { + let value = Value::::get().unwrap_or_default(); + if value >= 255 { + Err(Error::::ValueOverflow.into()) + } else { + let new_val = value.checked_add(1).ok_or(Error::::ValueOverflow)?; + Value::::put(new_val); + Pallet::::deposit_event(Event::Incremented { new_val }); + Ok(()) + } + } + + // Get the value and check if it can be decremented + #[pallet::task_index(1)] + #[pallet::task_condition(|| { + let value = Value::::get().unwrap(); + value > 0 + })] + #[pallet::task_list(Vec::>::new())] + #[pallet::task_weight(0)] + fn decrement() -> DispatchResult { + let value = Value::::get().unwrap_or_default(); + if value == 0 { + Err(Error::::ValueUnderflow.into()) + } else { + let new_val = value.checked_sub(1).ok_or(Error::::ValueUnderflow)?; + Value::::put(new_val); + Pallet::::deposit_event(Event::Decremented { new_val }); + Ok(()) + } + } + } + }) + .unwrap(); + assert_eq!(parsed.tasks.len(), 2); + }); } #[test] fn test_parse_tasks_def_duplicate_index() { - simulate_manifest_dir("../../examples/basic", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::tasks_experimental] - impl, I: 'static> Pallet { - #[pallet::task_list(Something::iter())] - #[pallet::task_condition(|i| i % 2 == 0)] - #[pallet::task_index(0)] - #[pallet::task_weight(0)] - pub fn foo(i: u32) -> DispatchResult { - Ok(()) - } - - #[pallet::task_list(Numbers::::iter_keys())] - #[pallet::task_condition(|i| Numbers::::contains_key(i))] - #[pallet::task_index(0)] - #[pallet::task_weight(0)] - pub fn bar(i: u32) -> DispatchResult { - Ok(()) - } - } - }), - "duplicate task index `0`" - ); - }); + simulate_manifest_dir("../../pallets/subtensor", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + #[pallet::task_list(Something::iter())] + #[pallet::task_condition(|i| i % 2 == 0)] + #[pallet::task_index(0)] + #[pallet::task_weight(0)] + pub fn foo(i: u32) -> DispatchResult { + Ok(()) + } + + #[pallet::task_list(Numbers::::iter_keys())] + #[pallet::task_condition(|i| Numbers::::contains_key(i))] + #[pallet::task_index(0)] + #[pallet::task_weight(0)] + pub fn bar(i: u32) -> DispatchResult { + Ok(()) + } + } + }), + "duplicate task index `0`" + ); + }); } #[test] fn test_parse_tasks_def_missing_task_list() { - simulate_manifest_dir("../../examples/basic", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::tasks_experimental] - impl, I: 'static> Pallet { - #[pallet::task_condition(|i| i % 2 == 0)] - #[pallet::task_index(0)] - pub fn foo(i: u32) -> DispatchResult { - Ok(()) - } - } - }), - r"missing `#\[pallet::task_list\(\.\.\)\]`" - ); - }); + simulate_manifest_dir("../../pallets/subtensor", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + #[pallet::task_condition(|i| i % 2 == 0)] + #[pallet::task_index(0)] + pub fn foo(i: u32) -> DispatchResult { + Ok(()) + } + } + }), + r"missing `#\[pallet::task_list\(\.\.\)\]`" + ); + }); } #[test] fn test_parse_tasks_def_missing_task_condition() { - simulate_manifest_dir("../../examples/basic", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::tasks_experimental] - impl, I: 'static> Pallet { - #[pallet::task_list(Something::iter())] - #[pallet::task_index(0)] - pub fn foo(i: u32) -> DispatchResult { - Ok(()) - } - } - }), - r"missing `#\[pallet::task_condition\(\.\.\)\]`" - ); - }); + simulate_manifest_dir("../../pallets/subtensor", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + #[pallet::task_list(Something::iter())] + #[pallet::task_index(0)] + pub fn foo(i: u32) -> DispatchResult { + Ok(()) + } + } + }), + r"missing `#\[pallet::task_condition\(\.\.\)\]`" + ); + }); } #[test] fn test_parse_tasks_def_missing_task_index() { - simulate_manifest_dir("../../examples/basic", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::tasks_experimental] - impl, I: 'static> Pallet { - #[pallet::task_condition(|i| i % 2 == 0)] - #[pallet::task_list(Something::iter())] - pub fn foo(i: u32) -> DispatchResult { - Ok(()) - } - } - }), - r"missing `#\[pallet::task_index\(\.\.\)\]`" - ); - }); + simulate_manifest_dir("../../pallets/subtensor", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + #[pallet::task_condition(|i| i % 2 == 0)] + #[pallet::task_list(Something::iter())] + pub fn foo(i: u32) -> DispatchResult { + Ok(()) + } + } + }), + r"missing `#\[pallet::task_index\(\.\.\)\]`" + ); + }); } #[test] fn test_parse_tasks_def_missing_task_weight() { - simulate_manifest_dir("../../examples/basic", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::tasks_experimental] - impl, I: 'static> Pallet { - #[pallet::task_condition(|i| i % 2 == 0)] - #[pallet::task_list(Something::iter())] - #[pallet::task_index(0)] - pub fn foo(i: u32) -> DispatchResult { - Ok(()) - } - } - }), - r"missing `#\[pallet::task_weight\(\.\.\)\]`" - ); - }); + simulate_manifest_dir("../../pallets/subtensor", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + #[pallet::task_condition(|i| i % 2 == 0)] + #[pallet::task_list(Something::iter())] + #[pallet::task_index(0)] + pub fn foo(i: u32) -> DispatchResult { + Ok(()) + } + } + }), + r"missing `#\[pallet::task_weight\(\.\.\)\]`" + ); + }); } #[test] fn test_parse_tasks_def_unexpected_extra_task_list_attr() { - simulate_manifest_dir("../../examples/basic", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::tasks_experimental] - impl, I: 'static> Pallet { - #[pallet::task_condition(|i| i % 2 == 0)] - #[pallet::task_index(0)] - #[pallet::task_weight(0)] - #[pallet::task_list(Something::iter())] - #[pallet::task_list(SomethingElse::iter())] - pub fn foo(i: u32) -> DispatchResult { - Ok(()) - } - } - }), - r"unexpected extra `#\[pallet::task_list\(\.\.\)\]`" - ); - }); + simulate_manifest_dir("../../pallets/subtensor", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + #[pallet::task_condition(|i| i % 2 == 0)] + #[pallet::task_index(0)] + #[pallet::task_weight(0)] + #[pallet::task_list(Something::iter())] + #[pallet::task_list(SomethingElse::iter())] + pub fn foo(i: u32) -> DispatchResult { + Ok(()) + } + } + }), + r"unexpected extra `#\[pallet::task_list\(\.\.\)\]`" + ); + }); } #[test] fn test_parse_tasks_def_unexpected_extra_task_condition_attr() { - simulate_manifest_dir("../../examples/basic", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::tasks_experimental] - impl, I: 'static> Pallet { - #[pallet::task_condition(|i| i % 2 == 0)] - #[pallet::task_condition(|i| i % 4 == 0)] - #[pallet::task_index(0)] - #[pallet::task_list(Something::iter())] - #[pallet::task_weight(0)] - pub fn foo(i: u32) -> DispatchResult { - Ok(()) - } - } - }), - r"unexpected extra `#\[pallet::task_condition\(\.\.\)\]`" - ); - }); + simulate_manifest_dir("../../pallets/subtensor", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + #[pallet::task_condition(|i| i % 2 == 0)] + #[pallet::task_condition(|i| i % 4 == 0)] + #[pallet::task_index(0)] + #[pallet::task_list(Something::iter())] + #[pallet::task_weight(0)] + pub fn foo(i: u32) -> DispatchResult { + Ok(()) + } + } + }), + r"unexpected extra `#\[pallet::task_condition\(\.\.\)\]`" + ); + }); } #[test] fn test_parse_tasks_def_unexpected_extra_task_index_attr() { - simulate_manifest_dir("../../examples/basic", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::tasks_experimental] - impl, I: 'static> Pallet { - #[pallet::task_condition(|i| i % 2 == 0)] - #[pallet::task_index(0)] - #[pallet::task_index(0)] - #[pallet::task_list(Something::iter())] - #[pallet::task_weight(0)] - pub fn foo(i: u32) -> DispatchResult { - Ok(()) - } - } - }), - r"unexpected extra `#\[pallet::task_index\(\.\.\)\]`" - ); - }); + simulate_manifest_dir("../../pallets/subtensor", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + #[pallet::task_condition(|i| i % 2 == 0)] + #[pallet::task_index(0)] + #[pallet::task_index(0)] + #[pallet::task_list(Something::iter())] + #[pallet::task_weight(0)] + pub fn foo(i: u32) -> DispatchResult { + Ok(()) + } + } + }), + r"unexpected extra `#\[pallet::task_index\(\.\.\)\]`" + ); + }); } #[test] fn test_parse_tasks_def_extra_tasks_attribute() { - simulate_manifest_dir("../../examples/basic", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::tasks_experimental] - #[pallet::tasks_experimental] - impl, I: 'static> Pallet {} - }), - r"unexpected extra `#\[pallet::tasks_experimental\]` attribute" - ); - }); + simulate_manifest_dir("../../pallets/subtensor", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + #[pallet::tasks_experimental] + impl, I: 'static> Pallet {} + }), + r"unexpected extra `#\[pallet::tasks_experimental\]` attribute" + ); + }); } #[test] fn test_parse_task_enum_def_basic() { - simulate_manifest_dir("../../examples/basic", || { - parse2::(quote! { - #[pallet::task_enum] - pub enum Task { - Increment, - Decrement, - } - }) - .unwrap(); - }); + simulate_manifest_dir("../../pallets/subtensor", || { + parse2::(quote! { + #[pallet::task_enum] + pub enum Task { + Increment, + Decrement, + } + }) + .unwrap(); + }); } #[test] fn test_parse_task_enum_def_non_task_name() { - simulate_manifest_dir("../../examples/basic", || { - parse2::(quote! { - #[pallet::task_enum] - pub enum Something { - Foo - } - }) - .unwrap(); - }); + simulate_manifest_dir("../../pallets/subtensor", || { + parse2::(quote! { + #[pallet::task_enum] + pub enum Something { + Foo + } + }) + .unwrap(); + }); } #[test] fn test_parse_task_enum_def_missing_attr_allowed() { - simulate_manifest_dir("../../examples/basic", || { - parse2::(quote! { - pub enum Task { - Increment, - Decrement, - } - }) - .unwrap(); - }); + simulate_manifest_dir("../../pallets/subtensor", || { + parse2::(quote! { + pub enum Task { + Increment, + Decrement, + } + }) + .unwrap(); + }); } #[test] fn test_parse_task_enum_def_missing_attr_alternate_name_allowed() { - simulate_manifest_dir("../../examples/basic", || { - parse2::(quote! { - pub enum Foo { - Red, - } - }) - .unwrap(); - }); + simulate_manifest_dir("../../pallets/subtensor", || { + parse2::(quote! { + pub enum Foo { + Red, + } + }) + .unwrap(); + }); } #[test] fn test_parse_task_enum_def_wrong_attr() { - simulate_manifest_dir("../../examples/basic", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::something] - pub enum Task { - Increment, - Decrement, - } - }), - "expected `task_enum`" - ); - }); + simulate_manifest_dir("../../pallets/subtensor", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::something] + pub enum Task { + Increment, + Decrement, + } + }), + "expected `task_enum`" + ); + }); } #[test] fn test_parse_task_enum_def_wrong_item() { - simulate_manifest_dir("../../examples/basic", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::task_enum] - pub struct Something; - }), - "expected `enum`" - ); - }); + simulate_manifest_dir("../../pallets/subtensor", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::task_enum] + pub struct Something; + }), + "expected `enum`" + ); + }); } diff --git a/support/procedural-fork/src/pallet/parse/tests/mod.rs b/support/procedural-fork/src/pallet/parse/tests/mod.rs index a3661f307..7206a20bf 100644 --- a/support/procedural-fork/src/pallet/parse/tests/mod.rs +++ b/support/procedural-fork/src/pallet/parse/tests/mod.rs @@ -20,7 +20,7 @@ use syn::parse_quote; #[doc(hidden)] pub mod __private { - pub use regex; + pub use regex; } /// Allows you to assert that the input expression resolves to an error whose string @@ -63,22 +63,22 @@ pub mod __private { /// enough that it will work with any error with a reasonable [`core::fmt::Display`] impl. #[macro_export] macro_rules! assert_parse_error_matches { - ($expr:expr, $reg:literal) => { - match $expr { - Ok(_) => panic!("Expected an `Error(..)`, but got Ok(..)"), - Err(e) => { - let error_message = e.to_string(); - let re = $crate::pallet::parse::tests::__private::regex::Regex::new($reg) - .expect("Invalid regex pattern"); - assert!( - re.is_match(&error_message), - "Error message \"{}\" does not match the pattern \"{}\"", - error_message, - $reg - ); - }, - } - }; + ($expr:expr, $reg:literal) => { + match $expr { + Ok(_) => panic!("Expected an `Error(..)`, but got Ok(..)"), + Err(e) => { + let error_message = e.to_string(); + let re = $crate::pallet::parse::tests::__private::regex::Regex::new($reg) + .expect("Invalid regex pattern"); + assert!( + re.is_match(&error_message), + "Error message \"{}\" does not match the pattern \"{}\"", + error_message, + $reg + ); + } + } + }; } /// Allows you to assert that an entire pallet parses successfully. A custom syntax is used for @@ -88,7 +88,7 @@ macro_rules! assert_parse_error_matches { /// /// ```ignore /// assert_pallet_parses! { -/// #[manifest_dir("../../examples/basic")] +/// #[manifest_dir("../../pallets/subtensor")] /// #[frame_support::pallet] /// pub mod pallet { /// #[pallet::config] @@ -142,7 +142,7 @@ macro_rules! assert_pallet_parses { /// /// ``` /// assert_pallet_parse_error! { -/// #[manifest_dir("../../examples/basic")] +/// #[manifest_dir("../../pallets/subtensor")] /// #[error_regex("Missing `\\#\\[pallet::pallet\\]`")] /// #[frame_support::pallet] /// pub mod pallet { @@ -183,82 +183,82 @@ macro_rules! assert_pallet_parse_error { /// This function uses a [`Mutex`] to avoid a race condition created when multiple tests try to /// modify and then restore the `CARGO_MANIFEST_DIR` ENV var in an overlapping way. pub fn simulate_manifest_dir, F: FnOnce() + std::panic::UnwindSafe>( - path: P, - closure: F, + path: P, + closure: F, ) { - use std::{env::*, path::*}; + use std::{env::*, path::*}; - /// Ensures that only one thread can modify/restore the `CARGO_MANIFEST_DIR` ENV var at a time, - /// avoiding a race condition because `cargo test` runs tests in parallel. - /// - /// Although this forces all tests that use [`simulate_manifest_dir`] to run sequentially with - /// respect to each other, this is still several orders of magnitude faster than using UI - /// tests, even if they are run in parallel. - static MANIFEST_DIR_LOCK: Mutex<()> = Mutex::new(()); + /// Ensures that only one thread can modify/restore the `CARGO_MANIFEST_DIR` ENV var at a time, + /// avoiding a race condition because `cargo test` runs tests in parallel. + /// + /// Although this forces all tests that use [`simulate_manifest_dir`] to run sequentially with + /// respect to each other, this is still several orders of magnitude faster than using UI + /// tests, even if they are run in parallel. + static MANIFEST_DIR_LOCK: Mutex<()> = Mutex::new(()); - // avoid race condition when swapping out `CARGO_MANIFEST_DIR` - let guard = MANIFEST_DIR_LOCK.lock().unwrap(); + // avoid race condition when swapping out `CARGO_MANIFEST_DIR` + let guard = MANIFEST_DIR_LOCK.lock().unwrap(); - // obtain the current/original `CARGO_MANIFEST_DIR` - let orig = PathBuf::from( - var("CARGO_MANIFEST_DIR").expect("failed to read ENV var `CARGO_MANIFEST_DIR`"), - ); + // obtain the current/original `CARGO_MANIFEST_DIR` + let orig = PathBuf::from( + var("CARGO_MANIFEST_DIR").expect("failed to read ENV var `CARGO_MANIFEST_DIR`"), + ); - // set `CARGO_MANIFEST_DIR` to the provided path, relative to current working dir - set_var("CARGO_MANIFEST_DIR", orig.join(path.as_ref())); + // set `CARGO_MANIFEST_DIR` to the provided path, relative to current working dir + set_var("CARGO_MANIFEST_DIR", orig.join(path.as_ref())); - // safely run closure catching any panics - let result = panic::catch_unwind(closure); + // safely run closure catching any panics + let result = panic::catch_unwind(closure); - // restore original `CARGO_MANIFEST_DIR` before unwinding - set_var("CARGO_MANIFEST_DIR", &orig); + // restore original `CARGO_MANIFEST_DIR` before unwinding + set_var("CARGO_MANIFEST_DIR", &orig); - // unlock the mutex so we don't poison it if there is a panic - drop(guard); + // unlock the mutex so we don't poison it if there is a panic + drop(guard); - // unwind any panics originally encountered when running closure - result.unwrap(); + // unwind any panics originally encountered when running closure + result.unwrap(); } mod tasks; #[test] fn test_parse_minimal_pallet() { - assert_pallet_parses! { - #[manifest_dir("../../examples/basic")] - #[frame_support::pallet] - pub mod pallet { - #[pallet::config] - pub trait Config: frame_system::Config {} + assert_pallet_parses! { + #[manifest_dir("../../pallets/subtensor")] + #[frame_support::pallet] + pub mod pallet { + #[pallet::config] + pub trait Config: frame_system::Config {} - #[pallet::pallet] - pub struct Pallet(_); - } - }; + #[pallet::pallet] + pub struct Pallet(_); + } + }; } #[test] fn test_parse_pallet_missing_pallet() { - assert_pallet_parse_error! { - #[manifest_dir("../../examples/basic")] - #[error_regex("Missing `\\#\\[pallet::pallet\\]`")] - #[frame_support::pallet] - pub mod pallet { - #[pallet::config] - pub trait Config: frame_system::Config {} - } - } + assert_pallet_parse_error! { + #[manifest_dir("../../pallets/subtensor")] + #[error_regex("Missing `\\#\\[pallet::pallet\\]`")] + #[frame_support::pallet] + pub mod pallet { + #[pallet::config] + pub trait Config: frame_system::Config {} + } + } } #[test] fn test_parse_pallet_missing_config() { - assert_pallet_parse_error! { - #[manifest_dir("../../examples/basic")] - #[error_regex("Missing `\\#\\[pallet::config\\]`")] - #[frame_support::pallet] - pub mod pallet { - #[pallet::pallet] - pub struct Pallet(_); - } - } + assert_pallet_parse_error! { + #[manifest_dir("../../pallets/subtensor")] + #[error_regex("Missing `\\#\\[pallet::config\\]`")] + #[frame_support::pallet] + pub mod pallet { + #[pallet::pallet] + pub struct Pallet(_); + } + } } diff --git a/support/procedural-fork/src/pallet/parse/tests/tasks.rs b/support/procedural-fork/src/pallet/parse/tests/tasks.rs index 9f1436284..22a757c8f 100644 --- a/support/procedural-fork/src/pallet/parse/tests/tasks.rs +++ b/support/procedural-fork/src/pallet/parse/tests/tasks.rs @@ -19,222 +19,222 @@ use syn::parse_quote; #[test] fn test_parse_pallet_with_task_enum_missing_impl() { - assert_pallet_parse_error! { - #[manifest_dir("../../examples/basic")] - #[error_regex("Missing `\\#\\[pallet::tasks_experimental\\]` impl")] - #[frame_support::pallet] - pub mod pallet { - #[pallet::task_enum] - pub enum Task { - Something, - } - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(_); - } - } + assert_pallet_parse_error! { + #[manifest_dir("../../pallets/subtensor")] + #[error_regex("Missing `\\#\\[pallet::tasks_experimental\\]` impl")] + #[frame_support::pallet] + pub mod pallet { + #[pallet::task_enum] + pub enum Task { + Something, + } + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + } } #[test] fn test_parse_pallet_with_task_enum_wrong_attribute() { - assert_pallet_parse_error! { - #[manifest_dir("../../examples/basic")] - #[error_regex("expected one of")] - #[frame_support::pallet] - pub mod pallet { - #[pallet::wrong_attribute] - pub enum Task { - Something, - } - - #[pallet::task_list] - impl frame_support::traits::Task for Task - where - T: TypeInfo, - {} - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(_); - } - } + assert_pallet_parse_error! { + #[manifest_dir("../../pallets/subtensor")] + #[error_regex("expected one of")] + #[frame_support::pallet] + pub mod pallet { + #[pallet::wrong_attribute] + pub enum Task { + Something, + } + + #[pallet::task_list] + impl frame_support::traits::Task for Task + where + T: TypeInfo, + {} + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + } } #[test] fn test_parse_pallet_missing_task_enum() { - assert_pallet_parses! { - #[manifest_dir("../../examples/basic")] - #[frame_support::pallet] - pub mod pallet { - #[pallet::tasks_experimental] - #[cfg(test)] // aha, this means it's being eaten - impl frame_support::traits::Task for Task - where - T: TypeInfo, - {} - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(_); - } - }; + assert_pallet_parses! { + #[manifest_dir("../../pallets/subtensor")] + #[frame_support::pallet] + pub mod pallet { + #[pallet::tasks_experimental] + #[cfg(test)] // aha, this means it's being eaten + impl frame_support::traits::Task for Task + where + T: TypeInfo, + {} + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + }; } #[test] fn test_parse_pallet_task_list_in_wrong_place() { - assert_pallet_parse_error! { - #[manifest_dir("../../examples/basic")] - #[error_regex("can only be used on items within an `impl` statement.")] - #[frame_support::pallet] - pub mod pallet { - pub enum MyCustomTaskEnum { - Something, - } - - #[pallet::task_list] - pub fn something() { - println!("hey"); - } - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(_); - } - } + assert_pallet_parse_error! { + #[manifest_dir("../../pallets/subtensor")] + #[error_regex("can only be used on items within an `impl` statement.")] + #[frame_support::pallet] + pub mod pallet { + pub enum MyCustomTaskEnum { + Something, + } + + #[pallet::task_list] + pub fn something() { + println!("hey"); + } + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + } } #[test] fn test_parse_pallet_manual_tasks_impl_without_manual_tasks_enum() { - assert_pallet_parse_error! { - #[manifest_dir("../../examples/basic")] - #[error_regex(".*attribute must be attached to your.*")] - #[frame_support::pallet] - pub mod pallet { - - impl frame_support::traits::Task for Task - where - T: TypeInfo, - { - type Enumeration = sp_std::vec::IntoIter>; - - fn iter() -> Self::Enumeration { - sp_std::vec![Task::increment, Task::decrement].into_iter() - } - } - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(_); - } - } + assert_pallet_parse_error! { + #[manifest_dir("../../pallets/subtensor")] + #[error_regex(".*attribute must be attached to your.*")] + #[frame_support::pallet] + pub mod pallet { + + impl frame_support::traits::Task for Task + where + T: TypeInfo, + { + type Enumeration = sp_std::vec::IntoIter>; + + fn iter() -> Self::Enumeration { + sp_std::vec![Task::increment, Task::decrement].into_iter() + } + } + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + } } #[test] fn test_parse_pallet_manual_task_enum_non_manual_impl() { - assert_pallet_parses! { - #[manifest_dir("../../examples/basic")] - #[frame_support::pallet] - pub mod pallet { - pub enum MyCustomTaskEnum { - Something, - } - - #[pallet::tasks_experimental] - impl frame_support::traits::Task for MyCustomTaskEnum - where - T: TypeInfo, - {} - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(_); - } - }; + assert_pallet_parses! { + #[manifest_dir("../../pallets/subtensor")] + #[frame_support::pallet] + pub mod pallet { + pub enum MyCustomTaskEnum { + Something, + } + + #[pallet::tasks_experimental] + impl frame_support::traits::Task for MyCustomTaskEnum + where + T: TypeInfo, + {} + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + }; } #[test] fn test_parse_pallet_non_manual_task_enum_manual_impl() { - assert_pallet_parses! { - #[manifest_dir("../../examples/basic")] - #[frame_support::pallet] - pub mod pallet { - #[pallet::task_enum] - pub enum MyCustomTaskEnum { - Something, - } - - impl frame_support::traits::Task for MyCustomTaskEnum - where - T: TypeInfo, - {} - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(_); - } - }; + assert_pallet_parses! { + #[manifest_dir("../../pallets/subtensor")] + #[frame_support::pallet] + pub mod pallet { + #[pallet::task_enum] + pub enum MyCustomTaskEnum { + Something, + } + + impl frame_support::traits::Task for MyCustomTaskEnum + where + T: TypeInfo, + {} + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + }; } #[test] fn test_parse_pallet_manual_task_enum_manual_impl() { - assert_pallet_parses! { - #[manifest_dir("../../examples/basic")] - #[frame_support::pallet] - pub mod pallet { - pub enum MyCustomTaskEnum { - Something, - } - - impl frame_support::traits::Task for MyCustomTaskEnum - where - T: TypeInfo, - {} - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(_); - } - }; + assert_pallet_parses! { + #[manifest_dir("../../pallets/subtensor")] + #[frame_support::pallet] + pub mod pallet { + pub enum MyCustomTaskEnum { + Something, + } + + impl frame_support::traits::Task for MyCustomTaskEnum + where + T: TypeInfo, + {} + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + }; } #[test] fn test_parse_pallet_manual_task_enum_mismatch_ident() { - assert_pallet_parses! { - #[manifest_dir("../../examples/basic")] - #[frame_support::pallet] - pub mod pallet { - pub enum WrongIdent { - Something, - } - - #[pallet::tasks_experimental] - impl frame_support::traits::Task for MyCustomTaskEnum - where - T: TypeInfo, - {} - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(_); - } - }; + assert_pallet_parses! { + #[manifest_dir("../../pallets/subtensor")] + #[frame_support::pallet] + pub mod pallet { + pub enum WrongIdent { + Something, + } + + #[pallet::tasks_experimental] + impl frame_support::traits::Task for MyCustomTaskEnum + where + T: TypeInfo, + {} + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + }; } From 2c2247f752592bff53b5f8fa4f9ec2536b4d0f02 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 19 Sep 2024 01:53:11 -0400 Subject: [PATCH 33/53] cargo fmt --- support/procedural-fork/src/benchmark.rs | 2117 +++++++++-------- .../src/construct_runtime/expand/call.rs | 393 +-- .../expand/composite_helper.rs | 132 +- .../src/construct_runtime/expand/config.rs | 208 +- .../construct_runtime/expand/freeze_reason.rs | 90 +- .../construct_runtime/expand/hold_reason.rs | 90 +- .../src/construct_runtime/expand/inherent.rs | 459 ++-- .../src/construct_runtime/expand/lock_id.rs | 72 +- .../src/construct_runtime/expand/metadata.rs | 399 ++-- .../src/construct_runtime/expand/origin.rs | 846 +++---- .../construct_runtime/expand/outer_enums.rs | 379 +-- .../construct_runtime/expand/slash_reason.rs | 72 +- .../src/construct_runtime/expand/task.rs | 212 +- .../src/construct_runtime/expand/unsigned.rs | 113 +- .../src/construct_runtime/mod.rs | 1010 ++++---- .../src/construct_runtime/parse.rs | 1257 +++++----- support/procedural-fork/src/crate_version.rs | 36 +- .../procedural-fork/src/dummy_part_checker.rs | 98 +- support/procedural-fork/src/dynamic_params.rs | 422 ++-- support/procedural-fork/src/key_prefix.rs | 142 +- .../procedural-fork/src/match_and_insert.rs | 244 +- support/procedural-fork/src/no_bound/clone.rs | 162 +- support/procedural-fork/src/no_bound/debug.rs | 186 +- .../procedural-fork/src/no_bound/default.rs | 139 +- support/procedural-fork/src/no_bound/ord.rs | 96 +- .../src/no_bound/partial_eq.rs | 214 +- .../src/no_bound/partial_ord.rs | 119 +- .../procedural-fork/src/pallet/expand/call.rs | 874 +++---- .../src/pallet/expand/composite.rs | 20 +- .../src/pallet/expand/config.rs | 120 +- .../src/pallet/expand/constants.rs | 172 +- .../src/pallet/expand/doc_only.rs | 152 +- .../src/pallet/expand/documentation.rs | 189 +- .../src/pallet/expand/error.rs | 298 +-- .../src/pallet/expand/event.rs | 303 +-- .../src/pallet/expand/genesis_build.rs | 50 +- .../src/pallet/expand/genesis_config.rs | 239 +- .../src/pallet/expand/hooks.rs | 588 ++--- .../src/pallet/expand/inherent.rs | 59 +- .../src/pallet/expand/instances.rs | 32 +- .../procedural-fork/src/pallet/expand/mod.rs | 124 +- .../src/pallet/expand/origin.rs | 59 +- .../src/pallet/expand/pallet_struct.rs | 524 ++-- .../src/pallet/expand/storage.rs | 1415 +++++------ .../src/pallet/expand/tasks.rs | 308 +-- .../src/pallet/expand/tt_default_parts.rs | 394 +-- .../src/pallet/expand/type_value.rs | 90 +- .../src/pallet/expand/validate_unsigned.rs | 60 +- .../src/pallet/expand/warnings.rs | 111 +- support/procedural-fork/src/pallet/mod.rs | 38 +- .../procedural-fork/src/pallet/parse/call.rs | 791 +++--- .../src/pallet/parse/composite.rs | 330 +-- .../src/pallet/parse/config.rs | 983 ++++---- .../procedural-fork/src/pallet/parse/error.rs | 148 +- .../procedural-fork/src/pallet/parse/event.rs | 206 +- .../src/pallet/parse/extra_constants.rs | 240 +- .../src/pallet/parse/genesis_build.rs | 69 +- .../src/pallet/parse/genesis_config.rs | 81 +- .../src/pallet/parse/helper.rs | 933 ++++---- .../procedural-fork/src/pallet/parse/hooks.rs | 104 +- .../src/pallet/parse/inherent.rs | 68 +- .../procedural-fork/src/pallet/parse/mod.rs | 1160 ++++----- .../src/pallet/parse/origin.rs | 80 +- .../src/pallet/parse/pallet_struct.rs | 220 +- .../src/pallet/parse/storage.rs | 1739 +++++++------- .../src/pallet/parse/type_value.rs | 176 +- .../src/pallet/parse/validate_unsigned.rs | 64 +- support/procedural-fork/src/pallet_error.rs | 307 +-- .../procedural-fork/src/runtime/expand/mod.rs | 574 ++--- support/procedural-fork/src/runtime/mod.rs | 32 +- .../src/runtime/parse/helper.rs | 29 +- .../procedural-fork/src/runtime/parse/mod.rs | 345 +-- .../src/runtime/parse/pallet.rs | 146 +- .../src/runtime/parse/pallet_decl.rs | 77 +- .../src/runtime/parse/runtime_struct.rs | 25 +- .../src/runtime/parse/runtime_types.rs | 90 +- support/procedural-fork/src/storage_alias.rs | 1211 +++++----- support/procedural-fork/src/transactional.rs | 76 +- support/procedural-fork/src/tt_macro.rs | 82 +- 79 files changed, 13576 insertions(+), 12736 deletions(-) diff --git a/support/procedural-fork/src/benchmark.rs b/support/procedural-fork/src/benchmark.rs index 0a62c3f92..376200d6e 100644 --- a/support/procedural-fork/src/benchmark.rs +++ b/support/procedural-fork/src/benchmark.rs @@ -23,332 +23,369 @@ use proc_macro::TokenStream; use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; use quote::{quote, ToTokens}; use syn::{ - parse::{Nothing, ParseStream}, - parse_quote, - punctuated::Punctuated, - spanned::Spanned, - token::{Comma, Gt, Lt, PathSep}, - Attribute, Error, Expr, ExprBlock, ExprCall, ExprPath, FnArg, Item, ItemFn, ItemMod, Pat, Path, - PathArguments, PathSegment, Result, ReturnType, Signature, Stmt, Token, Type, TypePath, - Visibility, WhereClause, + parse::{Nothing, ParseStream}, + parse_quote, + punctuated::Punctuated, + spanned::Spanned, + token::{Comma, Gt, Lt, PathSep}, + Attribute, Error, Expr, ExprBlock, ExprCall, ExprPath, FnArg, Item, ItemFn, ItemMod, Pat, Path, + PathArguments, PathSegment, Result, ReturnType, Signature, Stmt, Token, Type, TypePath, + Visibility, WhereClause, }; mod keywords { - use syn::custom_keyword; - - custom_keyword!(benchmark); - custom_keyword!(benchmarks); - custom_keyword!(block); - custom_keyword!(extra); - custom_keyword!(pov_mode); - custom_keyword!(extrinsic_call); - custom_keyword!(skip_meta); - custom_keyword!(BenchmarkError); - custom_keyword!(Result); - custom_keyword!(MaxEncodedLen); - custom_keyword!(Measured); - custom_keyword!(Ignored); - - pub const BENCHMARK_TOKEN: &str = stringify!(benchmark); - pub const BENCHMARKS_TOKEN: &str = stringify!(benchmarks); + use syn::custom_keyword; + + custom_keyword!(benchmark); + custom_keyword!(benchmarks); + custom_keyword!(block); + custom_keyword!(extra); + custom_keyword!(pov_mode); + custom_keyword!(extrinsic_call); + custom_keyword!(skip_meta); + custom_keyword!(BenchmarkError); + custom_keyword!(Result); + custom_keyword!(MaxEncodedLen); + custom_keyword!(Measured); + custom_keyword!(Ignored); + + pub const BENCHMARK_TOKEN: &str = stringify!(benchmark); + pub const BENCHMARKS_TOKEN: &str = stringify!(benchmarks); } /// This represents the raw parsed data for a param definition such as `x: Linear<10, 20>`. #[derive(Clone)] struct ParamDef { - name: String, - _typ: Type, - start: syn::GenericArgument, - end: syn::GenericArgument, + name: String, + _typ: Type, + start: syn::GenericArgument, + end: syn::GenericArgument, } /// Allows easy parsing of the `<10, 20>` component of `x: Linear<10, 20>`. #[derive(Parse)] struct RangeArgs { - _lt_token: Lt, - start: syn::GenericArgument, - _comma: Comma, - end: syn::GenericArgument, - _trailing_comma: Option, - _gt_token: Gt, + _lt_token: Lt, + start: syn::GenericArgument, + _comma: Comma, + end: syn::GenericArgument, + _trailing_comma: Option, + _gt_token: Gt, } #[derive(Clone, Debug)] struct BenchmarkAttrs { - skip_meta: bool, - extra: bool, - pov_mode: Option, + skip_meta: bool, + extra: bool, + pov_mode: Option, } /// Represents a single benchmark option enum BenchmarkAttr { - Extra, - SkipMeta, - /// How the PoV should be measured. - PoV(PovModeAttr), + Extra, + SkipMeta, + /// How the PoV should be measured. + PoV(PovModeAttr), } impl syn::parse::Parse for PovModeAttr { - fn parse(input: ParseStream) -> Result { - let _pov: keywords::pov_mode = input.parse()?; - let _eq: Token![=] = input.parse()?; - let root = PovEstimationMode::parse(input)?; - - let mut maybe_content = None; - let _ = || -> Result<()> { - let content; - syn::braced!(content in input); - maybe_content = Some(content); - Ok(()) - }(); - - let per_key = match maybe_content { - Some(content) => { - let per_key = Punctuated::::parse_terminated(&content)?; - per_key.into_iter().collect() - }, - None => Vec::new(), - }; - - Ok(Self { root, per_key }) - } + fn parse(input: ParseStream) -> Result { + let _pov: keywords::pov_mode = input.parse()?; + let _eq: Token![=] = input.parse()?; + let root = PovEstimationMode::parse(input)?; + + let mut maybe_content = None; + let _ = || -> Result<()> { + let content; + syn::braced!(content in input); + maybe_content = Some(content); + Ok(()) + }(); + + let per_key = match maybe_content { + Some(content) => { + let per_key = Punctuated::::parse_terminated(&content)?; + per_key.into_iter().collect() + } + None => Vec::new(), + }; + + Ok(Self { root, per_key }) + } } impl syn::parse::Parse for BenchmarkAttr { - fn parse(input: ParseStream) -> Result { - let lookahead = input.lookahead1(); - if lookahead.peek(keywords::extra) { - let _extra: keywords::extra = input.parse()?; - Ok(BenchmarkAttr::Extra) - } else if lookahead.peek(keywords::skip_meta) { - let _skip_meta: keywords::skip_meta = input.parse()?; - Ok(BenchmarkAttr::SkipMeta) - } else if lookahead.peek(keywords::pov_mode) { - PovModeAttr::parse(input).map(BenchmarkAttr::PoV) - } else { - Err(lookahead.error()) - } - } + fn parse(input: ParseStream) -> Result { + let lookahead = input.lookahead1(); + if lookahead.peek(keywords::extra) { + let _extra: keywords::extra = input.parse()?; + Ok(BenchmarkAttr::Extra) + } else if lookahead.peek(keywords::skip_meta) { + let _skip_meta: keywords::skip_meta = input.parse()?; + Ok(BenchmarkAttr::SkipMeta) + } else if lookahead.peek(keywords::pov_mode) { + PovModeAttr::parse(input).map(BenchmarkAttr::PoV) + } else { + Err(lookahead.error()) + } + } } /// A `#[pov_mode = .. { .. }]` attribute. #[derive(Debug, Clone)] struct PovModeAttr { - /// The root mode for this benchmarks. - root: PovEstimationMode, - /// The pov-mode for a specific key. This overwrites `root` for this key. - per_key: Vec, + /// The root mode for this benchmarks. + root: PovEstimationMode, + /// The pov-mode for a specific key. This overwrites `root` for this key. + per_key: Vec, } /// A single key-value pair inside the `{}` of a `#[pov_mode = .. { .. }]` attribute. #[derive(Debug, Clone, derive_syn_parse::Parse)] struct PovModeKeyAttr { - /// A specific storage key for which to set the PoV mode. - key: Path, - _underscore: Token![:], - /// The PoV mode for this key. - mode: PovEstimationMode, + /// A specific storage key for which to set the PoV mode. + key: Path, + _underscore: Token![:], + /// The PoV mode for this key. + mode: PovEstimationMode, } /// How the PoV should be estimated. #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum PovEstimationMode { - /// Use the maximal encoded length as provided by [`codec::MaxEncodedLen`]. - MaxEncodedLen, - /// Measure the accessed value size in the pallet benchmarking and add some trie overhead. - Measured, - /// Do not estimate the PoV size for this storage item or benchmark. - Ignored, + /// Use the maximal encoded length as provided by [`codec::MaxEncodedLen`]. + MaxEncodedLen, + /// Measure the accessed value size in the pallet benchmarking and add some trie overhead. + Measured, + /// Do not estimate the PoV size for this storage item or benchmark. + Ignored, } impl syn::parse::Parse for PovEstimationMode { - fn parse(input: ParseStream) -> Result { - let lookahead = input.lookahead1(); - if lookahead.peek(keywords::MaxEncodedLen) { - let _max_encoded_len: keywords::MaxEncodedLen = input.parse()?; - return Ok(PovEstimationMode::MaxEncodedLen) - } else if lookahead.peek(keywords::Measured) { - let _measured: keywords::Measured = input.parse()?; - return Ok(PovEstimationMode::Measured) - } else if lookahead.peek(keywords::Ignored) { - let _ignored: keywords::Ignored = input.parse()?; - return Ok(PovEstimationMode::Ignored) - } else { - return Err(lookahead.error()) - } - } + fn parse(input: ParseStream) -> Result { + let lookahead = input.lookahead1(); + if lookahead.peek(keywords::MaxEncodedLen) { + let _max_encoded_len: keywords::MaxEncodedLen = input.parse()?; + return Ok(PovEstimationMode::MaxEncodedLen); + } else if lookahead.peek(keywords::Measured) { + let _measured: keywords::Measured = input.parse()?; + return Ok(PovEstimationMode::Measured); + } else if lookahead.peek(keywords::Ignored) { + let _ignored: keywords::Ignored = input.parse()?; + return Ok(PovEstimationMode::Ignored); + } else { + return Err(lookahead.error()); + } + } } impl ToString for PovEstimationMode { - fn to_string(&self) -> String { - match self { - PovEstimationMode::MaxEncodedLen => "MaxEncodedLen".into(), - PovEstimationMode::Measured => "Measured".into(), - PovEstimationMode::Ignored => "Ignored".into(), - } - } + fn to_string(&self) -> String { + match self { + PovEstimationMode::MaxEncodedLen => "MaxEncodedLen".into(), + PovEstimationMode::Measured => "Measured".into(), + PovEstimationMode::Ignored => "Ignored".into(), + } + } } impl quote::ToTokens for PovEstimationMode { - fn to_tokens(&self, tokens: &mut TokenStream2) { - match self { - PovEstimationMode::MaxEncodedLen => tokens.extend(quote!(MaxEncodedLen)), - PovEstimationMode::Measured => tokens.extend(quote!(Measured)), - PovEstimationMode::Ignored => tokens.extend(quote!(Ignored)), - } - } + fn to_tokens(&self, tokens: &mut TokenStream2) { + match self { + PovEstimationMode::MaxEncodedLen => tokens.extend(quote!(MaxEncodedLen)), + PovEstimationMode::Measured => tokens.extend(quote!(Measured)), + PovEstimationMode::Ignored => tokens.extend(quote!(Ignored)), + } + } } impl syn::parse::Parse for BenchmarkAttrs { - fn parse(input: ParseStream) -> syn::Result { - let mut extra = false; - let mut skip_meta = false; - let mut pov_mode = None; - let args = Punctuated::::parse_terminated(&input)?; - - for arg in args.into_iter() { - match arg { - BenchmarkAttr::Extra => { - if extra { - return Err(input.error("`extra` can only be specified once")) - } - extra = true; - }, - BenchmarkAttr::SkipMeta => { - if skip_meta { - return Err(input.error("`skip_meta` can only be specified once")) - } - skip_meta = true; - }, - BenchmarkAttr::PoV(mode) => { - if pov_mode.is_some() { - return Err(input.error("`pov_mode` can only be specified once")) - } - pov_mode = Some(mode); - }, - } - } - Ok(BenchmarkAttrs { extra, skip_meta, pov_mode }) - } + fn parse(input: ParseStream) -> syn::Result { + let mut extra = false; + let mut skip_meta = false; + let mut pov_mode = None; + let args = Punctuated::::parse_terminated(&input)?; + + for arg in args.into_iter() { + match arg { + BenchmarkAttr::Extra => { + if extra { + return Err(input.error("`extra` can only be specified once")); + } + extra = true; + } + BenchmarkAttr::SkipMeta => { + if skip_meta { + return Err(input.error("`skip_meta` can only be specified once")); + } + skip_meta = true; + } + BenchmarkAttr::PoV(mode) => { + if pov_mode.is_some() { + return Err(input.error("`pov_mode` can only be specified once")); + } + pov_mode = Some(mode); + } + } + } + Ok(BenchmarkAttrs { + extra, + skip_meta, + pov_mode, + }) + } } /// Represents the parsed extrinsic call for a benchmark #[derive(Clone)] enum BenchmarkCallDef { - ExtrinsicCall { origin: Expr, expr_call: ExprCall, attr_span: Span }, // #[extrinsic_call] - Block { block: ExprBlock, attr_span: Span }, // #[block] + ExtrinsicCall { + origin: Expr, + expr_call: ExprCall, + attr_span: Span, + }, // #[extrinsic_call] + Block { + block: ExprBlock, + attr_span: Span, + }, // #[block] } impl BenchmarkCallDef { - /// Returns the `span()` for attribute - fn attr_span(&self) -> Span { - match self { - BenchmarkCallDef::ExtrinsicCall { origin: _, expr_call: _, attr_span } => *attr_span, - BenchmarkCallDef::Block { block: _, attr_span } => *attr_span, - } - } + /// Returns the `span()` for attribute + fn attr_span(&self) -> Span { + match self { + BenchmarkCallDef::ExtrinsicCall { + origin: _, + expr_call: _, + attr_span, + } => *attr_span, + BenchmarkCallDef::Block { + block: _, + attr_span, + } => *attr_span, + } + } } /// Represents a parsed `#[benchmark]` or `#[instance_benchmark]` item. #[derive(Clone)] struct BenchmarkDef { - params: Vec, - setup_stmts: Vec, - call_def: BenchmarkCallDef, - verify_stmts: Vec, - last_stmt: Option, - fn_sig: Signature, - fn_vis: Visibility, - fn_attrs: Vec, + params: Vec, + setup_stmts: Vec, + call_def: BenchmarkCallDef, + verify_stmts: Vec, + last_stmt: Option, + fn_sig: Signature, + fn_vis: Visibility, + fn_attrs: Vec, } /// used to parse something compatible with `Result` #[derive(Parse)] struct ResultDef { - _result_kw: keywords::Result, - _lt: Token![<], - unit: Type, - _comma: Comma, - e_type: TypePath, - _gt: Token![>], + _result_kw: keywords::Result, + _lt: Token![<], + unit: Type, + _comma: Comma, + e_type: TypePath, + _gt: Token![>], } /// Ensures that `ReturnType` is a `Result<(), BenchmarkError>`, if specified fn ensure_valid_return_type(item_fn: &ItemFn) -> Result<()> { - if let ReturnType::Type(_, typ) = &item_fn.sig.output { - let non_unit = |span| return Err(Error::new(span, "expected `()`")); - let Type::Path(TypePath { path, qself: _ }) = &**typ else { - return Err(Error::new( + if let ReturnType::Type(_, typ) = &item_fn.sig.output { + let non_unit = |span| return Err(Error::new(span, "expected `()`")); + let Type::Path(TypePath { path, qself: _ }) = &**typ else { + return Err(Error::new( typ.span(), "Only `Result<(), BenchmarkError>` or a blank return type is allowed on benchmark function definitions", - )) - }; - let seg = path - .segments - .last() - .expect("to be parsed as a TypePath, it must have at least one segment; qed"); - let res: ResultDef = syn::parse2(seg.to_token_stream())?; - // ensure T in Result is () - let Type::Tuple(tup) = res.unit else { return non_unit(res.unit.span()) }; - if !tup.elems.is_empty() { - return non_unit(tup.span()) - } - let TypePath { path, qself: _ } = res.e_type; - let seg = path - .segments - .last() - .expect("to be parsed as a TypePath, it must have at least one segment; qed"); - syn::parse2::(seg.to_token_stream())?; - } - Ok(()) + )); + }; + let seg = path + .segments + .last() + .expect("to be parsed as a TypePath, it must have at least one segment; qed"); + let res: ResultDef = syn::parse2(seg.to_token_stream())?; + // ensure T in Result is () + let Type::Tuple(tup) = res.unit else { + return non_unit(res.unit.span()); + }; + if !tup.elems.is_empty() { + return non_unit(tup.span()); + } + let TypePath { path, qself: _ } = res.e_type; + let seg = path + .segments + .last() + .expect("to be parsed as a TypePath, it must have at least one segment; qed"); + syn::parse2::(seg.to_token_stream())?; + } + Ok(()) } /// Parses params such as `x: Linear<0, 1>` fn parse_params(item_fn: &ItemFn) -> Result> { - let mut params: Vec = Vec::new(); - for arg in &item_fn.sig.inputs { - let invalid_param = |span| { - return Err(Error::new( - span, - "Invalid benchmark function param. A valid example would be `x: Linear<5, 10>`.", - )) - }; - - let FnArg::Typed(arg) = arg else { return invalid_param(arg.span()) }; - let Pat::Ident(ident) = &*arg.pat else { return invalid_param(arg.span()) }; - - // check param name - let var_span = ident.span(); - let invalid_param_name = || { - return Err(Error::new( + let mut params: Vec = Vec::new(); + for arg in &item_fn.sig.inputs { + let invalid_param = |span| { + return Err(Error::new( + span, + "Invalid benchmark function param. A valid example would be `x: Linear<5, 10>`.", + )); + }; + + let FnArg::Typed(arg) = arg else { + return invalid_param(arg.span()); + }; + let Pat::Ident(ident) = &*arg.pat else { + return invalid_param(arg.span()); + }; + + // check param name + let var_span = ident.span(); + let invalid_param_name = || { + return Err(Error::new( var_span, "Benchmark parameter names must consist of a single lowercase letter (a-z) and no other characters.", )); - }; - let name = ident.ident.to_token_stream().to_string(); - if name.len() > 1 { - return invalid_param_name() - }; - let Some(name_char) = name.chars().next() else { return invalid_param_name() }; - if !name_char.is_alphabetic() || !name_char.is_lowercase() { - return invalid_param_name() - } - - // parse type - let typ = &*arg.ty; - let Type::Path(tpath) = typ else { return invalid_param(typ.span()) }; - let Some(segment) = tpath.path.segments.last() else { return invalid_param(typ.span()) }; - let args = segment.arguments.to_token_stream().into(); - let Ok(args) = syn::parse::(args) else { return invalid_param(typ.span()) }; - - params.push(ParamDef { name, _typ: typ.clone(), start: args.start, end: args.end }); - } - Ok(params) + }; + let name = ident.ident.to_token_stream().to_string(); + if name.len() > 1 { + return invalid_param_name(); + }; + let Some(name_char) = name.chars().next() else { + return invalid_param_name(); + }; + if !name_char.is_alphabetic() || !name_char.is_lowercase() { + return invalid_param_name(); + } + + // parse type + let typ = &*arg.ty; + let Type::Path(tpath) = typ else { + return invalid_param(typ.span()); + }; + let Some(segment) = tpath.path.segments.last() else { + return invalid_param(typ.span()); + }; + let args = segment.arguments.to_token_stream().into(); + let Ok(args) = syn::parse::(args) else { + return invalid_param(typ.span()); + }; + + params.push(ParamDef { + name, + _typ: typ.clone(), + start: args.start, + end: args.end, + }); + } + Ok(params) } /// Used in several places where the `#[extrinsic_call]` or `#[body]` annotation is missing fn missing_call(item_fn: &ItemFn) -> Result { - return Err(Error::new( + return Err(Error::new( item_fn.block.brace_token.span.join(), "No valid #[extrinsic_call] or #[block] annotation could be found in benchmark function body." )); @@ -358,8 +395,8 @@ fn missing_call(item_fn: &ItemFn) -> Result { /// returns them. Also handles parsing errors for invalid / extra call defs. AKA this is /// general handling for `#[extrinsic_call]` and `#[block]` fn parse_call_def(item_fn: &ItemFn) -> Result<(usize, BenchmarkCallDef)> { - // #[extrinsic_call] / #[block] handling - let call_defs = item_fn.block.stmts.iter().enumerate().filter_map(|(i, child)| { + // #[extrinsic_call] / #[block] handling + let call_defs = item_fn.block.stmts.iter().enumerate().filter_map(|(i, child)| { if let Stmt::Expr(Expr::Call(expr_call), _semi) = child { // #[extrinsic_call] case expr_call.attrs.iter().enumerate().find_map(|(k, attr)| { @@ -393,810 +430,850 @@ fn parse_call_def(item_fn: &ItemFn) -> Result<(usize, BenchmarkCallDef)> { None } }).collect::>>()?; - Ok(match &call_defs[..] { - [(i, call_def)] => (*i, call_def.clone()), // = 1 - [] => return missing_call(item_fn), - _ => - return Err(Error::new( - call_defs[1].1.attr_span(), - "Only one #[extrinsic_call] or #[block] attribute is allowed per benchmark.", - )), - }) + Ok(match &call_defs[..] { + [(i, call_def)] => (*i, call_def.clone()), // = 1 + [] => return missing_call(item_fn), + _ => { + return Err(Error::new( + call_defs[1].1.attr_span(), + "Only one #[extrinsic_call] or #[block] attribute is allowed per benchmark.", + )) + } + }) } impl BenchmarkDef { - /// Constructs a [`BenchmarkDef`] by traversing an existing [`ItemFn`] node. - pub fn from(item_fn: &ItemFn) -> Result { - let params = parse_params(item_fn)?; - ensure_valid_return_type(item_fn)?; - let (i, call_def) = parse_call_def(&item_fn)?; - - let (verify_stmts, last_stmt) = match item_fn.sig.output { - ReturnType::Default => - // no return type, last_stmt should be None - (Vec::from(&item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len()]), None), - ReturnType::Type(_, _) => { - // defined return type, last_stmt should be Result<(), BenchmarkError> - // compatible and should not be included in verify_stmts - if i + 1 >= item_fn.block.stmts.len() { - return Err(Error::new( - item_fn.block.span(), - "Benchmark `#[block]` or `#[extrinsic_call]` item cannot be the \ + /// Constructs a [`BenchmarkDef`] by traversing an existing [`ItemFn`] node. + pub fn from(item_fn: &ItemFn) -> Result { + let params = parse_params(item_fn)?; + ensure_valid_return_type(item_fn)?; + let (i, call_def) = parse_call_def(&item_fn)?; + + let (verify_stmts, last_stmt) = match item_fn.sig.output { + ReturnType::Default => + // no return type, last_stmt should be None + { + ( + Vec::from(&item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len()]), + None, + ) + } + ReturnType::Type(_, _) => { + // defined return type, last_stmt should be Result<(), BenchmarkError> + // compatible and should not be included in verify_stmts + if i + 1 >= item_fn.block.stmts.len() { + return Err(Error::new( + item_fn.block.span(), + "Benchmark `#[block]` or `#[extrinsic_call]` item cannot be the \ last statement of your benchmark function definition if you have \ defined a return type. You should return something compatible \ with Result<(), BenchmarkError> (i.e. `Ok(())`) as the last statement \ or change your signature to a blank return type.", - )) - } - let Some(stmt) = item_fn.block.stmts.last() else { return missing_call(item_fn) }; - ( - Vec::from(&item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len() - 1]), - Some(stmt.clone()), - ) - }, - }; - - Ok(BenchmarkDef { - params, - setup_stmts: Vec::from(&item_fn.block.stmts[0..i]), - call_def, - verify_stmts, - last_stmt, - fn_sig: item_fn.sig.clone(), - fn_vis: item_fn.vis.clone(), - fn_attrs: item_fn.attrs.clone(), - }) - } + )); + } + let Some(stmt) = item_fn.block.stmts.last() else { + return missing_call(item_fn); + }; + ( + Vec::from(&item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len() - 1]), + Some(stmt.clone()), + ) + } + }; + + Ok(BenchmarkDef { + params, + setup_stmts: Vec::from(&item_fn.block.stmts[0..i]), + call_def, + verify_stmts, + last_stmt, + fn_sig: item_fn.sig.clone(), + fn_vis: item_fn.vis.clone(), + fn_attrs: item_fn.attrs.clone(), + }) + } } /// Parses and expands a `#[benchmarks]` or `#[instance_benchmarks]` invocation pub fn benchmarks( - attrs: TokenStream, - tokens: TokenStream, - instance: bool, + attrs: TokenStream, + tokens: TokenStream, + instance: bool, ) -> syn::Result { - let krate = generate_access_from_frame_or_crate("frame-benchmarking")?; - // gather module info - let module: ItemMod = syn::parse(tokens)?; - let mod_span = module.span(); - let where_clause = match syn::parse::(attrs.clone()) { - Ok(_) => quote!(), - Err(_) => syn::parse::(attrs)?.predicates.to_token_stream(), - }; - let mod_vis = module.vis; - let mod_name = module.ident; - - // consume #[benchmarks] attribute by excluding it from mod_attrs - let mod_attrs: Vec<&Attribute> = module - .attrs - .iter() - .filter(|attr| !attr.path().is_ident(keywords::BENCHMARKS_TOKEN)) - .collect(); - - let mut benchmark_names: Vec = Vec::new(); - let mut extra_benchmark_names: Vec = Vec::new(); - let mut skip_meta_benchmark_names: Vec = Vec::new(); - // Map benchmarks to PoV modes. - let mut pov_modes = Vec::new(); - - let (_brace, mut content) = - module.content.ok_or(syn::Error::new(mod_span, "Module cannot be empty!"))?; - - // find all function defs marked with #[benchmark] - let benchmark_fn_metas = content.iter_mut().filter_map(|stmt| { - // parse as a function def first - let Item::Fn(func) = stmt else { return None }; - - // find #[benchmark] attribute on function def - let benchmark_attr = - func.attrs.iter().find(|attr| attr.path().is_ident(keywords::BENCHMARK_TOKEN))?; - - Some((benchmark_attr.clone(), func.clone(), stmt)) - }); - - // parse individual benchmark defs and args - for (benchmark_attr, func, stmt) in benchmark_fn_metas { - // parse benchmark def - let benchmark_def = BenchmarkDef::from(&func)?; - - // record benchmark name - let name = &func.sig.ident; - benchmark_names.push(name.clone()); - - // Check if we need to parse any args - if benchmark_attr.meta.require_path_only().is_err() { - // parse any args provided to #[benchmark] - let benchmark_attrs: BenchmarkAttrs = benchmark_attr.parse_args()?; - - // record name sets - if benchmark_attrs.extra { - extra_benchmark_names.push(name.clone()); - } else if benchmark_attrs.skip_meta { - skip_meta_benchmark_names.push(name.clone()); - } - - if let Some(mode) = benchmark_attrs.pov_mode { - let mut modes = Vec::new(); - // We cannot expand strings here since it is no-std, but syn does not expand bytes. - let name = name.to_string(); - let m = mode.root.to_string(); - modes.push(quote!(("ALL".as_bytes().to_vec(), #m.as_bytes().to_vec()))); - - for attr in mode.per_key.iter() { - // syn always puts spaces in quoted paths: - let key = attr.key.clone().into_token_stream().to_string().replace(" ", ""); - let mode = attr.mode.to_string(); - modes.push(quote!((#key.as_bytes().to_vec(), #mode.as_bytes().to_vec()))); - } - - pov_modes.push( - quote!((#name.as_bytes().to_vec(), #krate::__private::vec![#(#modes),*])), - ); - } - } - - // expand benchmark - let expanded = expand_benchmark(benchmark_def, name, instance, where_clause.clone()); - - // replace original function def with expanded code - *stmt = Item::Verbatim(expanded); - } - - // generics - let type_use_generics = match instance { - false => quote!(T), - true => quote!(T, I), - }; - let type_impl_generics = match instance { - false => quote!(T: Config), - true => quote!(T: Config, I: 'static), - }; - - let frame_system = generate_access_from_frame_or_crate("frame-system")?; - - // benchmark name variables - let benchmark_names_str: Vec = benchmark_names.iter().map(|n| n.to_string()).collect(); - let extra_benchmark_names_str: Vec = - extra_benchmark_names.iter().map(|n| n.to_string()).collect(); - let skip_meta_benchmark_names_str: Vec = - skip_meta_benchmark_names.iter().map(|n| n.to_string()).collect(); - let mut selected_benchmark_mappings: Vec = Vec::new(); - let mut benchmarks_by_name_mappings: Vec = Vec::new(); - let test_idents: Vec = benchmark_names_str - .iter() - .map(|n| Ident::new(format!("test_benchmark_{}", n).as_str(), Span::call_site())) - .collect(); - for i in 0..benchmark_names.len() { - let name_ident = &benchmark_names[i]; - let name_str = &benchmark_names_str[i]; - let test_ident = &test_idents[i]; - selected_benchmark_mappings.push(quote!(#name_str => SelectedBenchmark::#name_ident)); - benchmarks_by_name_mappings.push(quote!(#name_str => Self::#test_ident())) - } - - let impl_test_function = content - .iter_mut() - .find_map(|item| { - let Item::Macro(item_macro) = item else { - return None; - }; - - if !item_macro - .mac - .path - .segments - .iter() - .any(|s| s.ident == "impl_benchmark_test_suite") - { - return None; - } - - let tokens = item_macro.mac.tokens.clone(); - *item = Item::Verbatim(quote! {}); - - Some(quote! { - impl_test_function!( - (#( {} #benchmark_names )*) - (#( #extra_benchmark_names )*) - (#( #skip_meta_benchmark_names )*) - #tokens - ); - }) - }) - .unwrap_or(quote! {}); - - // emit final quoted tokens - let res = quote! { - #(#mod_attrs) - * - #mod_vis mod #mod_name { - #(#content) - * - - #[allow(non_camel_case_types)] - enum SelectedBenchmark { - #(#benchmark_names), - * - } - - impl<#type_impl_generics> #krate::BenchmarkingSetup<#type_use_generics> for SelectedBenchmark where #where_clause { - fn components(&self) -> #krate::__private::Vec<(#krate::BenchmarkParameter, u32, u32)> { - match self { - #( - Self::#benchmark_names => { - <#benchmark_names as #krate::BenchmarkingSetup<#type_use_generics>>::components(&#benchmark_names) - } - ) - * - } - } - - fn instance( - &self, - components: &[(#krate::BenchmarkParameter, u32)], - verify: bool, - ) -> Result< - #krate::__private::Box Result<(), #krate::BenchmarkError>>, - #krate::BenchmarkError, - > { - match self { - #( - Self::#benchmark_names => { - <#benchmark_names as #krate::BenchmarkingSetup< - #type_use_generics - >>::instance(&#benchmark_names, components, verify) - } - ) - * - } - } - } - #[cfg(any(feature = "runtime-benchmarks", test))] - impl<#type_impl_generics> #krate::Benchmarking for Pallet<#type_use_generics> - where T: #frame_system::Config, #where_clause - { - fn benchmarks( - extra: bool, - ) -> #krate::__private::Vec<#krate::BenchmarkMetadata> { - let mut all_names = #krate::__private::vec![ - #(#benchmark_names_str), - * - ]; - if !extra { - let extra = [ - #(#extra_benchmark_names_str), - * - ]; - all_names.retain(|x| !extra.contains(x)); - } - let pov_modes: - #krate::__private::Vec<( - #krate::__private::Vec, - #krate::__private::Vec<( - #krate::__private::Vec, - #krate::__private::Vec - )>, - )> = #krate::__private::vec![ - #( #pov_modes ),* - ]; - all_names.into_iter().map(|benchmark| { - let selected_benchmark = match benchmark { - #(#selected_benchmark_mappings), - *, - _ => panic!("all benchmarks should be selectable") - }; - let components = >::components(&selected_benchmark); - let name = benchmark.as_bytes().to_vec(); - let modes = pov_modes.iter().find(|p| p.0 == name).map(|p| p.1.clone()); - - #krate::BenchmarkMetadata { - name: benchmark.as_bytes().to_vec(), - components, - pov_modes: modes.unwrap_or_default(), - } - }).collect::<#krate::__private::Vec<_>>() - } - - fn run_benchmark( - extrinsic: &[u8], - c: &[(#krate::BenchmarkParameter, u32)], - whitelist: &[#krate::__private::TrackedStorageKey], - verify: bool, - internal_repeats: u32, - ) -> Result<#krate::__private::Vec<#krate::BenchmarkResult>, #krate::BenchmarkError> { - let extrinsic = #krate::__private::str::from_utf8(extrinsic).map_err(|_| "`extrinsic` is not a valid utf-8 string!")?; - let selected_benchmark = match extrinsic { - #(#selected_benchmark_mappings), - *, - _ => return Err("Could not find extrinsic.".into()), - }; - let mut whitelist = whitelist.to_vec(); - let whitelisted_caller_key = <#frame_system::Account< - T, - > as #krate::__private::storage::StorageMap<_, _,>>::hashed_key_for( - #krate::whitelisted_caller::() - ); - whitelist.push(whitelisted_caller_key.into()); - let transactional_layer_key = #krate::__private::TrackedStorageKey::new( - #krate::__private::storage::transactional::TRANSACTION_LEVEL_KEY.into(), - ); - whitelist.push(transactional_layer_key); - // Whitelist the `:extrinsic_index`. - let extrinsic_index = #krate::__private::TrackedStorageKey::new( - #krate::__private::well_known_keys::EXTRINSIC_INDEX.into() - ); - whitelist.push(extrinsic_index); - // Whitelist the `:intrablock_entropy`. - let intrablock_entropy = #krate::__private::TrackedStorageKey::new( - #krate::__private::well_known_keys::INTRABLOCK_ENTROPY.into() - ); - whitelist.push(intrablock_entropy); - - #krate::benchmarking::set_whitelist(whitelist.clone()); - let mut results: #krate::__private::Vec<#krate::BenchmarkResult> = #krate::__private::Vec::new(); - - // Always do at least one internal repeat... - for _ in 0 .. internal_repeats.max(1) { - // Always reset the state after the benchmark. - #krate::__private::defer!(#krate::benchmarking::wipe_db()); - - // Set up the externalities environment for the setup we want to - // benchmark. - let closure_to_benchmark = < - SelectedBenchmark as #krate::BenchmarkingSetup<#type_use_generics> - >::instance(&selected_benchmark, c, verify)?; - - // Set the block number to at least 1 so events are deposited. - if #krate::__private::Zero::is_zero(&#frame_system::Pallet::::block_number()) { - #frame_system::Pallet::::set_block_number(1u32.into()); - } - - // Commit the externalities to the database, flushing the DB cache. - // This will enable worst case scenario for reading from the database. - #krate::benchmarking::commit_db(); - - // Access all whitelisted keys to get them into the proof recorder since the - // recorder does now have a whitelist. - for key in &whitelist { - #krate::__private::storage::unhashed::get_raw(&key.key); - } - - // Reset the read/write counter so we don't count operations in the setup process. - #krate::benchmarking::reset_read_write_count(); - - // Time the extrinsic logic. - #krate::__private::log::trace!( - target: "benchmark", - "Start Benchmark: {} ({:?})", - extrinsic, - c - ); - - let start_pov = #krate::benchmarking::proof_size(); - let start_extrinsic = #krate::benchmarking::current_time(); - - closure_to_benchmark()?; - - let finish_extrinsic = #krate::benchmarking::current_time(); - let end_pov = #krate::benchmarking::proof_size(); - - // Calculate the diff caused by the benchmark. - let elapsed_extrinsic = finish_extrinsic.saturating_sub(start_extrinsic); - let diff_pov = match (start_pov, end_pov) { - (Some(start), Some(end)) => end.saturating_sub(start), - _ => Default::default(), - }; - - // Commit the changes to get proper write count - #krate::benchmarking::commit_db(); - #krate::__private::log::trace!( - target: "benchmark", - "End Benchmark: {} ns", elapsed_extrinsic - ); - let read_write_count = #krate::benchmarking::read_write_count(); - #krate::__private::log::trace!( - target: "benchmark", - "Read/Write Count {:?}", read_write_count - ); - - // Time the storage root recalculation. - let start_storage_root = #krate::benchmarking::current_time(); - #krate::__private::storage_root(#krate::__private::StateVersion::V1); - let finish_storage_root = #krate::benchmarking::current_time(); - let elapsed_storage_root = finish_storage_root - start_storage_root; - - let skip_meta = [ #(#skip_meta_benchmark_names_str),* ]; - let read_and_written_keys = if skip_meta.contains(&extrinsic) { - #krate::__private::vec![(b"Skipped Metadata".to_vec(), 0, 0, false)] - } else { - #krate::benchmarking::get_read_and_written_keys() - }; - - results.push(#krate::BenchmarkResult { - components: c.to_vec(), - extrinsic_time: elapsed_extrinsic, - storage_root_time: elapsed_storage_root, - reads: read_write_count.0, - repeat_reads: read_write_count.1, - writes: read_write_count.2, - repeat_writes: read_write_count.3, - proof_size: diff_pov, - keys: read_and_written_keys, - }); - } - - return Ok(results); - } - } - - #[cfg(test)] - impl<#type_impl_generics> Pallet<#type_use_generics> where T: #frame_system::Config, #where_clause { - /// Test a particular benchmark by name. - /// - /// This isn't called `test_benchmark_by_name` just in case some end-user eventually - /// writes a benchmark, itself called `by_name`; the function would be shadowed in - /// that case. - /// - /// This is generally intended to be used by child test modules such as those created - /// by the `impl_benchmark_test_suite` macro. However, it is not an error if a pallet - /// author chooses not to implement benchmarks. - #[allow(unused)] - fn test_bench_by_name(name: &[u8]) -> Result<(), #krate::BenchmarkError> { - let name = #krate::__private::str::from_utf8(name) - .map_err(|_| -> #krate::BenchmarkError { "`name` is not a valid utf8 string!".into() })?; - match name { - #(#benchmarks_by_name_mappings), - *, - _ => Err("Could not find test for requested benchmark.".into()), - } - } - } - - #impl_test_function - } - #mod_vis use #mod_name::*; - }; - Ok(res.into()) + let krate = generate_access_from_frame_or_crate("frame-benchmarking")?; + // gather module info + let module: ItemMod = syn::parse(tokens)?; + let mod_span = module.span(); + let where_clause = match syn::parse::(attrs.clone()) { + Ok(_) => quote!(), + Err(_) => syn::parse::(attrs)? + .predicates + .to_token_stream(), + }; + let mod_vis = module.vis; + let mod_name = module.ident; + + // consume #[benchmarks] attribute by excluding it from mod_attrs + let mod_attrs: Vec<&Attribute> = module + .attrs + .iter() + .filter(|attr| !attr.path().is_ident(keywords::BENCHMARKS_TOKEN)) + .collect(); + + let mut benchmark_names: Vec = Vec::new(); + let mut extra_benchmark_names: Vec = Vec::new(); + let mut skip_meta_benchmark_names: Vec = Vec::new(); + // Map benchmarks to PoV modes. + let mut pov_modes = Vec::new(); + + let (_brace, mut content) = module + .content + .ok_or(syn::Error::new(mod_span, "Module cannot be empty!"))?; + + // find all function defs marked with #[benchmark] + let benchmark_fn_metas = content.iter_mut().filter_map(|stmt| { + // parse as a function def first + let Item::Fn(func) = stmt else { return None }; + + // find #[benchmark] attribute on function def + let benchmark_attr = func + .attrs + .iter() + .find(|attr| attr.path().is_ident(keywords::BENCHMARK_TOKEN))?; + + Some((benchmark_attr.clone(), func.clone(), stmt)) + }); + + // parse individual benchmark defs and args + for (benchmark_attr, func, stmt) in benchmark_fn_metas { + // parse benchmark def + let benchmark_def = BenchmarkDef::from(&func)?; + + // record benchmark name + let name = &func.sig.ident; + benchmark_names.push(name.clone()); + + // Check if we need to parse any args + if benchmark_attr.meta.require_path_only().is_err() { + // parse any args provided to #[benchmark] + let benchmark_attrs: BenchmarkAttrs = benchmark_attr.parse_args()?; + + // record name sets + if benchmark_attrs.extra { + extra_benchmark_names.push(name.clone()); + } else if benchmark_attrs.skip_meta { + skip_meta_benchmark_names.push(name.clone()); + } + + if let Some(mode) = benchmark_attrs.pov_mode { + let mut modes = Vec::new(); + // We cannot expand strings here since it is no-std, but syn does not expand bytes. + let name = name.to_string(); + let m = mode.root.to_string(); + modes.push(quote!(("ALL".as_bytes().to_vec(), #m.as_bytes().to_vec()))); + + for attr in mode.per_key.iter() { + // syn always puts spaces in quoted paths: + let key = attr + .key + .clone() + .into_token_stream() + .to_string() + .replace(" ", ""); + let mode = attr.mode.to_string(); + modes.push(quote!((#key.as_bytes().to_vec(), #mode.as_bytes().to_vec()))); + } + + pov_modes.push( + quote!((#name.as_bytes().to_vec(), #krate::__private::vec![#(#modes),*])), + ); + } + } + + // expand benchmark + let expanded = expand_benchmark(benchmark_def, name, instance, where_clause.clone()); + + // replace original function def with expanded code + *stmt = Item::Verbatim(expanded); + } + + // generics + let type_use_generics = match instance { + false => quote!(T), + true => quote!(T, I), + }; + let type_impl_generics = match instance { + false => quote!(T: Config), + true => quote!(T: Config, I: 'static), + }; + + let frame_system = generate_access_from_frame_or_crate("frame-system")?; + + // benchmark name variables + let benchmark_names_str: Vec = benchmark_names.iter().map(|n| n.to_string()).collect(); + let extra_benchmark_names_str: Vec = extra_benchmark_names + .iter() + .map(|n| n.to_string()) + .collect(); + let skip_meta_benchmark_names_str: Vec = skip_meta_benchmark_names + .iter() + .map(|n| n.to_string()) + .collect(); + let mut selected_benchmark_mappings: Vec = Vec::new(); + let mut benchmarks_by_name_mappings: Vec = Vec::new(); + let test_idents: Vec = benchmark_names_str + .iter() + .map(|n| Ident::new(format!("test_benchmark_{}", n).as_str(), Span::call_site())) + .collect(); + for i in 0..benchmark_names.len() { + let name_ident = &benchmark_names[i]; + let name_str = &benchmark_names_str[i]; + let test_ident = &test_idents[i]; + selected_benchmark_mappings.push(quote!(#name_str => SelectedBenchmark::#name_ident)); + benchmarks_by_name_mappings.push(quote!(#name_str => Self::#test_ident())) + } + + let impl_test_function = content + .iter_mut() + .find_map(|item| { + let Item::Macro(item_macro) = item else { + return None; + }; + + if !item_macro + .mac + .path + .segments + .iter() + .any(|s| s.ident == "impl_benchmark_test_suite") + { + return None; + } + + let tokens = item_macro.mac.tokens.clone(); + *item = Item::Verbatim(quote! {}); + + Some(quote! { + impl_test_function!( + (#( {} #benchmark_names )*) + (#( #extra_benchmark_names )*) + (#( #skip_meta_benchmark_names )*) + #tokens + ); + }) + }) + .unwrap_or(quote! {}); + + // emit final quoted tokens + let res = quote! { + #(#mod_attrs) + * + #mod_vis mod #mod_name { + #(#content) + * + + #[allow(non_camel_case_types)] + enum SelectedBenchmark { + #(#benchmark_names), + * + } + + impl<#type_impl_generics> #krate::BenchmarkingSetup<#type_use_generics> for SelectedBenchmark where #where_clause { + fn components(&self) -> #krate::__private::Vec<(#krate::BenchmarkParameter, u32, u32)> { + match self { + #( + Self::#benchmark_names => { + <#benchmark_names as #krate::BenchmarkingSetup<#type_use_generics>>::components(&#benchmark_names) + } + ) + * + } + } + + fn instance( + &self, + components: &[(#krate::BenchmarkParameter, u32)], + verify: bool, + ) -> Result< + #krate::__private::Box Result<(), #krate::BenchmarkError>>, + #krate::BenchmarkError, + > { + match self { + #( + Self::#benchmark_names => { + <#benchmark_names as #krate::BenchmarkingSetup< + #type_use_generics + >>::instance(&#benchmark_names, components, verify) + } + ) + * + } + } + } + #[cfg(any(feature = "runtime-benchmarks", test))] + impl<#type_impl_generics> #krate::Benchmarking for Pallet<#type_use_generics> + where T: #frame_system::Config, #where_clause + { + fn benchmarks( + extra: bool, + ) -> #krate::__private::Vec<#krate::BenchmarkMetadata> { + let mut all_names = #krate::__private::vec![ + #(#benchmark_names_str), + * + ]; + if !extra { + let extra = [ + #(#extra_benchmark_names_str), + * + ]; + all_names.retain(|x| !extra.contains(x)); + } + let pov_modes: + #krate::__private::Vec<( + #krate::__private::Vec, + #krate::__private::Vec<( + #krate::__private::Vec, + #krate::__private::Vec + )>, + )> = #krate::__private::vec![ + #( #pov_modes ),* + ]; + all_names.into_iter().map(|benchmark| { + let selected_benchmark = match benchmark { + #(#selected_benchmark_mappings), + *, + _ => panic!("all benchmarks should be selectable") + }; + let components = >::components(&selected_benchmark); + let name = benchmark.as_bytes().to_vec(); + let modes = pov_modes.iter().find(|p| p.0 == name).map(|p| p.1.clone()); + + #krate::BenchmarkMetadata { + name: benchmark.as_bytes().to_vec(), + components, + pov_modes: modes.unwrap_or_default(), + } + }).collect::<#krate::__private::Vec<_>>() + } + + fn run_benchmark( + extrinsic: &[u8], + c: &[(#krate::BenchmarkParameter, u32)], + whitelist: &[#krate::__private::TrackedStorageKey], + verify: bool, + internal_repeats: u32, + ) -> Result<#krate::__private::Vec<#krate::BenchmarkResult>, #krate::BenchmarkError> { + let extrinsic = #krate::__private::str::from_utf8(extrinsic).map_err(|_| "`extrinsic` is not a valid utf-8 string!")?; + let selected_benchmark = match extrinsic { + #(#selected_benchmark_mappings), + *, + _ => return Err("Could not find extrinsic.".into()), + }; + let mut whitelist = whitelist.to_vec(); + let whitelisted_caller_key = <#frame_system::Account< + T, + > as #krate::__private::storage::StorageMap<_, _,>>::hashed_key_for( + #krate::whitelisted_caller::() + ); + whitelist.push(whitelisted_caller_key.into()); + let transactional_layer_key = #krate::__private::TrackedStorageKey::new( + #krate::__private::storage::transactional::TRANSACTION_LEVEL_KEY.into(), + ); + whitelist.push(transactional_layer_key); + // Whitelist the `:extrinsic_index`. + let extrinsic_index = #krate::__private::TrackedStorageKey::new( + #krate::__private::well_known_keys::EXTRINSIC_INDEX.into() + ); + whitelist.push(extrinsic_index); + // Whitelist the `:intrablock_entropy`. + let intrablock_entropy = #krate::__private::TrackedStorageKey::new( + #krate::__private::well_known_keys::INTRABLOCK_ENTROPY.into() + ); + whitelist.push(intrablock_entropy); + + #krate::benchmarking::set_whitelist(whitelist.clone()); + let mut results: #krate::__private::Vec<#krate::BenchmarkResult> = #krate::__private::Vec::new(); + + // Always do at least one internal repeat... + for _ in 0 .. internal_repeats.max(1) { + // Always reset the state after the benchmark. + #krate::__private::defer!(#krate::benchmarking::wipe_db()); + + // Set up the externalities environment for the setup we want to + // benchmark. + let closure_to_benchmark = < + SelectedBenchmark as #krate::BenchmarkingSetup<#type_use_generics> + >::instance(&selected_benchmark, c, verify)?; + + // Set the block number to at least 1 so events are deposited. + if #krate::__private::Zero::is_zero(&#frame_system::Pallet::::block_number()) { + #frame_system::Pallet::::set_block_number(1u32.into()); + } + + // Commit the externalities to the database, flushing the DB cache. + // This will enable worst case scenario for reading from the database. + #krate::benchmarking::commit_db(); + + // Access all whitelisted keys to get them into the proof recorder since the + // recorder does now have a whitelist. + for key in &whitelist { + #krate::__private::storage::unhashed::get_raw(&key.key); + } + + // Reset the read/write counter so we don't count operations in the setup process. + #krate::benchmarking::reset_read_write_count(); + + // Time the extrinsic logic. + #krate::__private::log::trace!( + target: "benchmark", + "Start Benchmark: {} ({:?})", + extrinsic, + c + ); + + let start_pov = #krate::benchmarking::proof_size(); + let start_extrinsic = #krate::benchmarking::current_time(); + + closure_to_benchmark()?; + + let finish_extrinsic = #krate::benchmarking::current_time(); + let end_pov = #krate::benchmarking::proof_size(); + + // Calculate the diff caused by the benchmark. + let elapsed_extrinsic = finish_extrinsic.saturating_sub(start_extrinsic); + let diff_pov = match (start_pov, end_pov) { + (Some(start), Some(end)) => end.saturating_sub(start), + _ => Default::default(), + }; + + // Commit the changes to get proper write count + #krate::benchmarking::commit_db(); + #krate::__private::log::trace!( + target: "benchmark", + "End Benchmark: {} ns", elapsed_extrinsic + ); + let read_write_count = #krate::benchmarking::read_write_count(); + #krate::__private::log::trace!( + target: "benchmark", + "Read/Write Count {:?}", read_write_count + ); + + // Time the storage root recalculation. + let start_storage_root = #krate::benchmarking::current_time(); + #krate::__private::storage_root(#krate::__private::StateVersion::V1); + let finish_storage_root = #krate::benchmarking::current_time(); + let elapsed_storage_root = finish_storage_root - start_storage_root; + + let skip_meta = [ #(#skip_meta_benchmark_names_str),* ]; + let read_and_written_keys = if skip_meta.contains(&extrinsic) { + #krate::__private::vec![(b"Skipped Metadata".to_vec(), 0, 0, false)] + } else { + #krate::benchmarking::get_read_and_written_keys() + }; + + results.push(#krate::BenchmarkResult { + components: c.to_vec(), + extrinsic_time: elapsed_extrinsic, + storage_root_time: elapsed_storage_root, + reads: read_write_count.0, + repeat_reads: read_write_count.1, + writes: read_write_count.2, + repeat_writes: read_write_count.3, + proof_size: diff_pov, + keys: read_and_written_keys, + }); + } + + return Ok(results); + } + } + + #[cfg(test)] + impl<#type_impl_generics> Pallet<#type_use_generics> where T: #frame_system::Config, #where_clause { + /// Test a particular benchmark by name. + /// + /// This isn't called `test_benchmark_by_name` just in case some end-user eventually + /// writes a benchmark, itself called `by_name`; the function would be shadowed in + /// that case. + /// + /// This is generally intended to be used by child test modules such as those created + /// by the `impl_benchmark_test_suite` macro. However, it is not an error if a pallet + /// author chooses not to implement benchmarks. + #[allow(unused)] + fn test_bench_by_name(name: &[u8]) -> Result<(), #krate::BenchmarkError> { + let name = #krate::__private::str::from_utf8(name) + .map_err(|_| -> #krate::BenchmarkError { "`name` is not a valid utf8 string!".into() })?; + match name { + #(#benchmarks_by_name_mappings), + *, + _ => Err("Could not find test for requested benchmark.".into()), + } + } + } + + #impl_test_function + } + #mod_vis use #mod_name::*; + }; + Ok(res.into()) } /// Prepares a [`Vec`] to be interpolated by [`quote!`] by creating easily-iterable /// arrays formatted in such a way that they can be interpolated directly. struct UnrolledParams { - param_ranges: Vec, - param_names: Vec, + param_ranges: Vec, + param_names: Vec, } impl UnrolledParams { - /// Constructs an [`UnrolledParams`] from a [`Vec`] - fn from(params: &Vec) -> UnrolledParams { - let param_ranges: Vec = params - .iter() - .map(|p| { - let name = Ident::new(&p.name, Span::call_site()); - let start = &p.start; - let end = &p.end; - quote!(#name, #start, #end) - }) - .collect(); - let param_names: Vec = params - .iter() - .map(|p| { - let name = Ident::new(&p.name, Span::call_site()); - quote!(#name) - }) - .collect(); - UnrolledParams { param_ranges, param_names } - } + /// Constructs an [`UnrolledParams`] from a [`Vec`] + fn from(params: &Vec) -> UnrolledParams { + let param_ranges: Vec = params + .iter() + .map(|p| { + let name = Ident::new(&p.name, Span::call_site()); + let start = &p.start; + let end = &p.end; + quote!(#name, #start, #end) + }) + .collect(); + let param_names: Vec = params + .iter() + .map(|p| { + let name = Ident::new(&p.name, Span::call_site()); + quote!(#name) + }) + .collect(); + UnrolledParams { + param_ranges, + param_names, + } + } } /// Performs expansion of an already-parsed [`BenchmarkDef`]. fn expand_benchmark( - benchmark_def: BenchmarkDef, - name: &Ident, - is_instance: bool, - where_clause: TokenStream2, + benchmark_def: BenchmarkDef, + name: &Ident, + is_instance: bool, + where_clause: TokenStream2, ) -> TokenStream2 { - // set up variables needed during quoting - let krate = match generate_access_from_frame_or_crate("frame-benchmarking") { - Ok(ident) => ident, - Err(err) => return err.to_compile_error().into(), - }; - let frame_system = match generate_access_from_frame_or_crate("frame-system") { - Ok(path) => path, - Err(err) => return err.to_compile_error().into(), - }; - let codec = quote!(#krate::__private::codec); - let traits = quote!(#krate::__private::traits); - let setup_stmts = benchmark_def.setup_stmts; - let verify_stmts = benchmark_def.verify_stmts; - let last_stmt = benchmark_def.last_stmt; - let test_ident = - Ident::new(format!("test_benchmark_{}", name.to_string()).as_str(), Span::call_site()); - - // unroll params (prepare for quoting) - let unrolled = UnrolledParams::from(&benchmark_def.params); - let param_names = unrolled.param_names; - let param_ranges = unrolled.param_ranges; - - let type_use_generics = match is_instance { - false => quote!(T), - true => quote!(T, I), - }; - - let type_impl_generics = match is_instance { - false => quote!(T: Config), - true => quote!(T: Config, I: 'static), - }; - - // used in the benchmarking impls - let (pre_call, post_call, fn_call_body) = match &benchmark_def.call_def { - BenchmarkCallDef::ExtrinsicCall { origin, expr_call, attr_span: _ } => { - let mut expr_call = expr_call.clone(); - - // remove first arg from expr_call - let mut final_args = Punctuated::::new(); - let args: Vec<&Expr> = expr_call.args.iter().collect(); - for arg in &args[1..] { - final_args.push((*(*arg)).clone()); - } - expr_call.args = final_args; - - let origin = match origin { - Expr::Cast(t) => { - let ty = t.ty.clone(); - quote! { - <::RuntimeOrigin as From<#ty>>::from(#origin); - } - }, - _ => quote! { - #origin.into(); - }, - }; - - // determine call name (handles `_` and normal call syntax) - let expr_span = expr_call.span(); - let call_err = || { - syn::Error::new(expr_span, "Extrinsic call must be a function call or `_`") - .to_compile_error() - }; - let call_name = match *expr_call.func { - Expr::Path(expr_path) => { - // normal function call - let Some(segment) = expr_path.path.segments.last() else { return call_err() }; - segment.ident.to_string() - }, - Expr::Infer(_) => { - // `_` style - // replace `_` with fn name - name.to_string() - }, - _ => return call_err(), - }; - - // modify extrinsic call to be prefixed with "new_call_variant" - let call_name = format!("new_call_variant_{}", call_name); - let mut punct: Punctuated = Punctuated::new(); - punct.push(PathSegment { - arguments: PathArguments::None, - ident: Ident::new(call_name.as_str(), Span::call_site()), - }); - *expr_call.func = Expr::Path(ExprPath { - attrs: vec![], - qself: None, - path: Path { leading_colon: None, segments: punct }, - }); - let pre_call = quote! { - let __call = Call::<#type_use_generics>::#expr_call; - let __benchmarked_call_encoded = #codec::Encode::encode(&__call); - }; - let post_call = quote! { - let __call_decoded = as #codec::Decode> - ::decode(&mut &__benchmarked_call_encoded[..]) - .expect("call is encoded above, encoding must be correct"); - let __origin = #origin; - as #traits::UnfilteredDispatchable>::dispatch_bypass_filter( - __call_decoded, - __origin, - ) - }; - ( - // (pre_call, post_call, fn_call_body): - pre_call.clone(), - quote!(#post_call?;), - quote! { - #pre_call - #post_call.unwrap(); - }, - ) - }, - BenchmarkCallDef::Block { block, attr_span: _ } => - (quote!(), quote!(#block), quote!(#block)), - }; - - let vis = benchmark_def.fn_vis; - - // remove #[benchmark] attribute - let fn_attrs = benchmark_def - .fn_attrs - .iter() - .filter(|attr| !attr.path().is_ident(keywords::BENCHMARK_TOKEN)); - - // modify signature generics, ident, and inputs, e.g: - // before: `fn bench(u: Linear<1, 100>) -> Result<(), BenchmarkError>` - // after: `fn _bench , I: 'static>(u: u32, verify: bool) -> Result<(), - // BenchmarkError>` - let mut sig = benchmark_def.fn_sig; - sig.generics = parse_quote!(<#type_impl_generics>); - if !where_clause.is_empty() { - sig.generics.where_clause = parse_quote!(where #where_clause); - } - sig.ident = - Ident::new(format!("_{}", name.to_token_stream().to_string()).as_str(), Span::call_site()); - let mut fn_param_inputs: Vec = - param_names.iter().map(|name| quote!(#name: u32)).collect(); - fn_param_inputs.push(quote!(verify: bool)); - sig.inputs = parse_quote!(#(#fn_param_inputs),*); - - // used in instance() impl - let impl_last_stmt = match &last_stmt { - Some(stmt) => quote!(#stmt), - None => quote!(Ok(())), - }; - let fn_attrs_clone = fn_attrs.clone(); - - let fn_def = quote! { - #( - #fn_attrs_clone - )* - #vis #sig { - #( - #setup_stmts - )* - #fn_call_body - if verify { - #( - #verify_stmts - )* - } - #last_stmt - } - }; - - // generate final quoted tokens - let res = quote! { - // benchmark function definition - #fn_def - - #[allow(non_camel_case_types)] - #( - #fn_attrs - )* - struct #name; - - #[allow(unused_variables)] - impl<#type_impl_generics> #krate::BenchmarkingSetup<#type_use_generics> - for #name where #where_clause { - fn components(&self) -> #krate::__private::Vec<(#krate::BenchmarkParameter, u32, u32)> { - #krate::__private::vec! [ - #( - (#krate::BenchmarkParameter::#param_ranges) - ),* - ] - } - - fn instance( - &self, - components: &[(#krate::BenchmarkParameter, u32)], - verify: bool - ) -> Result<#krate::__private::Box Result<(), #krate::BenchmarkError>>, #krate::BenchmarkError> { - #( - // prepare instance #param_names - let #param_names = components.iter() - .find(|&c| c.0 == #krate::BenchmarkParameter::#param_names) - .ok_or("Could not find component during benchmark preparation.")? - .1; - )* - - // benchmark setup code - #( - #setup_stmts - )* - #pre_call - Ok(#krate::__private::Box::new(move || -> Result<(), #krate::BenchmarkError> { - #post_call - if verify { - #( - #verify_stmts - )* - } - #impl_last_stmt - })) - } - } - - #[cfg(test)] - impl<#type_impl_generics> Pallet<#type_use_generics> where T: #frame_system::Config, #where_clause { - #[allow(unused)] - fn #test_ident() -> Result<(), #krate::BenchmarkError> { - let selected_benchmark = SelectedBenchmark::#name; - let components = < - SelectedBenchmark as #krate::BenchmarkingSetup - >::components(&selected_benchmark); - let execute_benchmark = | - c: #krate::__private::Vec<(#krate::BenchmarkParameter, u32)> - | -> Result<(), #krate::BenchmarkError> { - // Always reset the state after the benchmark. - #krate::__private::defer!(#krate::benchmarking::wipe_db()); - - // Set up the benchmark, return execution + verification function. - let closure_to_verify = < - SelectedBenchmark as #krate::BenchmarkingSetup - >::instance(&selected_benchmark, &c, true)?; - - // Set the block number to at least 1 so events are deposited. - if #krate::__private::Zero::is_zero(&#frame_system::Pallet::::block_number()) { - #frame_system::Pallet::::set_block_number(1u32.into()); - } - - // Run execution + verification - closure_to_verify() - }; - - if components.is_empty() { - execute_benchmark(Default::default())?; - } else { - let num_values: u32 = if let Ok(ev) = std::env::var("VALUES_PER_COMPONENT") { - ev.parse().map_err(|_| { - #krate::BenchmarkError::Stop( - "Could not parse env var `VALUES_PER_COMPONENT` as u32." - ) - })? - } else { - 6 - }; - - if num_values < 2 { - return Err("`VALUES_PER_COMPONENT` must be at least 2".into()); - } - - for (name, low, high) in components.clone().into_iter() { - // Test the lowest, highest (if its different from the lowest) - // and up to num_values-2 more equidistant values in between. - // For 0..10 and num_values=6 this would mean: [0, 2, 4, 6, 8, 10] - if high < low { - return Err("The start of a `ParamRange` must be less than or equal to the end".into()); - } - - let mut values = #krate::__private::vec![low]; - let diff = (high - low).min(num_values - 1); - let slope = (high - low) as f32 / diff as f32; - - for i in 1..=diff { - let value = ((low as f32 + slope * i as f32) as u32) - .clamp(low, high); - values.push(value); - } - - for component_value in values { - // Select the max value for all the other components. - let c: #krate::__private::Vec<(#krate::BenchmarkParameter, u32)> = components - .iter() - .map(|(n, _, h)| - if *n == name { - (*n, component_value) - } else { - (*n, *h) - } - ) - .collect(); - - execute_benchmark(c)?; - } - } - } - return Ok(()); - } - } - }; - res + // set up variables needed during quoting + let krate = match generate_access_from_frame_or_crate("frame-benchmarking") { + Ok(ident) => ident, + Err(err) => return err.to_compile_error().into(), + }; + let frame_system = match generate_access_from_frame_or_crate("frame-system") { + Ok(path) => path, + Err(err) => return err.to_compile_error().into(), + }; + let codec = quote!(#krate::__private::codec); + let traits = quote!(#krate::__private::traits); + let setup_stmts = benchmark_def.setup_stmts; + let verify_stmts = benchmark_def.verify_stmts; + let last_stmt = benchmark_def.last_stmt; + let test_ident = Ident::new( + format!("test_benchmark_{}", name.to_string()).as_str(), + Span::call_site(), + ); + + // unroll params (prepare for quoting) + let unrolled = UnrolledParams::from(&benchmark_def.params); + let param_names = unrolled.param_names; + let param_ranges = unrolled.param_ranges; + + let type_use_generics = match is_instance { + false => quote!(T), + true => quote!(T, I), + }; + + let type_impl_generics = match is_instance { + false => quote!(T: Config), + true => quote!(T: Config, I: 'static), + }; + + // used in the benchmarking impls + let (pre_call, post_call, fn_call_body) = match &benchmark_def.call_def { + BenchmarkCallDef::ExtrinsicCall { + origin, + expr_call, + attr_span: _, + } => { + let mut expr_call = expr_call.clone(); + + // remove first arg from expr_call + let mut final_args = Punctuated::::new(); + let args: Vec<&Expr> = expr_call.args.iter().collect(); + for arg in &args[1..] { + final_args.push((*(*arg)).clone()); + } + expr_call.args = final_args; + + let origin = match origin { + Expr::Cast(t) => { + let ty = t.ty.clone(); + quote! { + <::RuntimeOrigin as From<#ty>>::from(#origin); + } + } + _ => quote! { + #origin.into(); + }, + }; + + // determine call name (handles `_` and normal call syntax) + let expr_span = expr_call.span(); + let call_err = || { + syn::Error::new(expr_span, "Extrinsic call must be a function call or `_`") + .to_compile_error() + }; + let call_name = match *expr_call.func { + Expr::Path(expr_path) => { + // normal function call + let Some(segment) = expr_path.path.segments.last() else { + return call_err(); + }; + segment.ident.to_string() + } + Expr::Infer(_) => { + // `_` style + // replace `_` with fn name + name.to_string() + } + _ => return call_err(), + }; + + // modify extrinsic call to be prefixed with "new_call_variant" + let call_name = format!("new_call_variant_{}", call_name); + let mut punct: Punctuated = Punctuated::new(); + punct.push(PathSegment { + arguments: PathArguments::None, + ident: Ident::new(call_name.as_str(), Span::call_site()), + }); + *expr_call.func = Expr::Path(ExprPath { + attrs: vec![], + qself: None, + path: Path { + leading_colon: None, + segments: punct, + }, + }); + let pre_call = quote! { + let __call = Call::<#type_use_generics>::#expr_call; + let __benchmarked_call_encoded = #codec::Encode::encode(&__call); + }; + let post_call = quote! { + let __call_decoded = as #codec::Decode> + ::decode(&mut &__benchmarked_call_encoded[..]) + .expect("call is encoded above, encoding must be correct"); + let __origin = #origin; + as #traits::UnfilteredDispatchable>::dispatch_bypass_filter( + __call_decoded, + __origin, + ) + }; + ( + // (pre_call, post_call, fn_call_body): + pre_call.clone(), + quote!(#post_call?;), + quote! { + #pre_call + #post_call.unwrap(); + }, + ) + } + BenchmarkCallDef::Block { + block, + attr_span: _, + } => (quote!(), quote!(#block), quote!(#block)), + }; + + let vis = benchmark_def.fn_vis; + + // remove #[benchmark] attribute + let fn_attrs = benchmark_def + .fn_attrs + .iter() + .filter(|attr| !attr.path().is_ident(keywords::BENCHMARK_TOKEN)); + + // modify signature generics, ident, and inputs, e.g: + // before: `fn bench(u: Linear<1, 100>) -> Result<(), BenchmarkError>` + // after: `fn _bench , I: 'static>(u: u32, verify: bool) -> Result<(), + // BenchmarkError>` + let mut sig = benchmark_def.fn_sig; + sig.generics = parse_quote!(<#type_impl_generics>); + if !where_clause.is_empty() { + sig.generics.where_clause = parse_quote!(where #where_clause); + } + sig.ident = Ident::new( + format!("_{}", name.to_token_stream().to_string()).as_str(), + Span::call_site(), + ); + let mut fn_param_inputs: Vec = + param_names.iter().map(|name| quote!(#name: u32)).collect(); + fn_param_inputs.push(quote!(verify: bool)); + sig.inputs = parse_quote!(#(#fn_param_inputs),*); + + // used in instance() impl + let impl_last_stmt = match &last_stmt { + Some(stmt) => quote!(#stmt), + None => quote!(Ok(())), + }; + let fn_attrs_clone = fn_attrs.clone(); + + let fn_def = quote! { + #( + #fn_attrs_clone + )* + #vis #sig { + #( + #setup_stmts + )* + #fn_call_body + if verify { + #( + #verify_stmts + )* + } + #last_stmt + } + }; + + // generate final quoted tokens + let res = quote! { + // benchmark function definition + #fn_def + + #[allow(non_camel_case_types)] + #( + #fn_attrs + )* + struct #name; + + #[allow(unused_variables)] + impl<#type_impl_generics> #krate::BenchmarkingSetup<#type_use_generics> + for #name where #where_clause { + fn components(&self) -> #krate::__private::Vec<(#krate::BenchmarkParameter, u32, u32)> { + #krate::__private::vec! [ + #( + (#krate::BenchmarkParameter::#param_ranges) + ),* + ] + } + + fn instance( + &self, + components: &[(#krate::BenchmarkParameter, u32)], + verify: bool + ) -> Result<#krate::__private::Box Result<(), #krate::BenchmarkError>>, #krate::BenchmarkError> { + #( + // prepare instance #param_names + let #param_names = components.iter() + .find(|&c| c.0 == #krate::BenchmarkParameter::#param_names) + .ok_or("Could not find component during benchmark preparation.")? + .1; + )* + + // benchmark setup code + #( + #setup_stmts + )* + #pre_call + Ok(#krate::__private::Box::new(move || -> Result<(), #krate::BenchmarkError> { + #post_call + if verify { + #( + #verify_stmts + )* + } + #impl_last_stmt + })) + } + } + + #[cfg(test)] + impl<#type_impl_generics> Pallet<#type_use_generics> where T: #frame_system::Config, #where_clause { + #[allow(unused)] + fn #test_ident() -> Result<(), #krate::BenchmarkError> { + let selected_benchmark = SelectedBenchmark::#name; + let components = < + SelectedBenchmark as #krate::BenchmarkingSetup + >::components(&selected_benchmark); + let execute_benchmark = | + c: #krate::__private::Vec<(#krate::BenchmarkParameter, u32)> + | -> Result<(), #krate::BenchmarkError> { + // Always reset the state after the benchmark. + #krate::__private::defer!(#krate::benchmarking::wipe_db()); + + // Set up the benchmark, return execution + verification function. + let closure_to_verify = < + SelectedBenchmark as #krate::BenchmarkingSetup + >::instance(&selected_benchmark, &c, true)?; + + // Set the block number to at least 1 so events are deposited. + if #krate::__private::Zero::is_zero(&#frame_system::Pallet::::block_number()) { + #frame_system::Pallet::::set_block_number(1u32.into()); + } + + // Run execution + verification + closure_to_verify() + }; + + if components.is_empty() { + execute_benchmark(Default::default())?; + } else { + let num_values: u32 = if let Ok(ev) = std::env::var("VALUES_PER_COMPONENT") { + ev.parse().map_err(|_| { + #krate::BenchmarkError::Stop( + "Could not parse env var `VALUES_PER_COMPONENT` as u32." + ) + })? + } else { + 6 + }; + + if num_values < 2 { + return Err("`VALUES_PER_COMPONENT` must be at least 2".into()); + } + + for (name, low, high) in components.clone().into_iter() { + // Test the lowest, highest (if its different from the lowest) + // and up to num_values-2 more equidistant values in between. + // For 0..10 and num_values=6 this would mean: [0, 2, 4, 6, 8, 10] + if high < low { + return Err("The start of a `ParamRange` must be less than or equal to the end".into()); + } + + let mut values = #krate::__private::vec![low]; + let diff = (high - low).min(num_values - 1); + let slope = (high - low) as f32 / diff as f32; + + for i in 1..=diff { + let value = ((low as f32 + slope * i as f32) as u32) + .clamp(low, high); + values.push(value); + } + + for component_value in values { + // Select the max value for all the other components. + let c: #krate::__private::Vec<(#krate::BenchmarkParameter, u32)> = components + .iter() + .map(|(n, _, h)| + if *n == name { + (*n, component_value) + } else { + (*n, *h) + } + ) + .collect(); + + execute_benchmark(c)?; + } + } + } + return Ok(()); + } + } + }; + res } diff --git a/support/procedural-fork/src/construct_runtime/expand/call.rs b/support/procedural-fork/src/construct_runtime/expand/call.rs index b0041ccc0..7e8c2e856 100644 --- a/support/procedural-fork/src/construct_runtime/expand/call.rs +++ b/support/procedural-fork/src/construct_runtime/expand/call.rs @@ -22,202 +22,205 @@ use std::str::FromStr; use syn::Ident; pub fn expand_outer_dispatch( - runtime: &Ident, - system_pallet: &Pallet, - pallet_decls: &[Pallet], - scrate: &TokenStream, + runtime: &Ident, + system_pallet: &Pallet, + pallet_decls: &[Pallet], + scrate: &TokenStream, ) -> TokenStream { - let mut variant_defs = TokenStream::new(); - let mut variant_patterns = Vec::new(); - let mut query_call_part_macros = Vec::new(); - let mut pallet_names = Vec::new(); - let mut pallet_attrs = Vec::new(); - let system_path = &system_pallet.path; - - let pallets_with_call = pallet_decls.iter().filter(|decl| decl.exists_part("Call")); - - for pallet_declaration in pallets_with_call { - let name = &pallet_declaration.name; - let path = &pallet_declaration.path; - let index = pallet_declaration.index; - let attr = - pallet_declaration.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { - let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) - .expect("was successfully parsed before; qed"); - quote! { - #acc - #attr - } - }); - - variant_defs.extend(quote! { - #attr - #[codec(index = #index)] - #name( #scrate::dispatch::CallableCallFor<#name, #runtime> ), - }); - variant_patterns.push(quote!(RuntimeCall::#name(call))); - pallet_names.push(name); - pallet_attrs.push(attr); - query_call_part_macros.push(quote! { - #path::__substrate_call_check::is_call_part_defined!(#name); - }); - } - - quote! { - #( #query_call_part_macros )* - - #[derive( - Clone, PartialEq, Eq, - #scrate::__private::codec::Encode, - #scrate::__private::codec::Decode, - #scrate::__private::scale_info::TypeInfo, - #scrate::__private::RuntimeDebug, - )] - pub enum RuntimeCall { - #variant_defs - } - #[cfg(test)] - impl RuntimeCall { - /// Return a list of the module names together with their size in memory. - pub const fn sizes() -> &'static [( &'static str, usize )] { - use #scrate::dispatch::Callable; - use core::mem::size_of; - &[#( - #pallet_attrs - ( - stringify!(#pallet_names), - size_of::< <#pallet_names as Callable<#runtime>>::RuntimeCall >(), - ), - )*] - } - - /// Panics with diagnostic information if the size is greater than the given `limit`. - pub fn assert_size_under(limit: usize) { - let size = core::mem::size_of::(); - let call_oversize = size > limit; - if call_oversize { - println!("Size of `Call` is {} bytes (provided limit is {} bytes)", size, limit); - let mut sizes = Self::sizes().to_vec(); - sizes.sort_by_key(|x| -(x.1 as isize)); - for (i, &(name, size)) in sizes.iter().enumerate().take(5) { - println!("Offender #{}: {} at {} bytes", i + 1, name, size); - } - if let Some((_, next_size)) = sizes.get(5) { - println!("{} others of size {} bytes or less", sizes.len() - 5, next_size); - } - panic!( - "Size of `Call` is more than limit; use `Box` on complex parameter types to reduce the + let mut variant_defs = TokenStream::new(); + let mut variant_patterns = Vec::new(); + let mut query_call_part_macros = Vec::new(); + let mut pallet_names = Vec::new(); + let mut pallet_attrs = Vec::new(); + let system_path = &system_pallet.path; + + let pallets_with_call = pallet_decls.iter().filter(|decl| decl.exists_part("Call")); + + for pallet_declaration in pallets_with_call { + let name = &pallet_declaration.name; + let path = &pallet_declaration.path; + let index = pallet_declaration.index; + let attr = + pallet_declaration + .cfg_pattern + .iter() + .fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); + + variant_defs.extend(quote! { + #attr + #[codec(index = #index)] + #name( #scrate::dispatch::CallableCallFor<#name, #runtime> ), + }); + variant_patterns.push(quote!(RuntimeCall::#name(call))); + pallet_names.push(name); + pallet_attrs.push(attr); + query_call_part_macros.push(quote! { + #path::__substrate_call_check::is_call_part_defined!(#name); + }); + } + + quote! { + #( #query_call_part_macros )* + + #[derive( + Clone, PartialEq, Eq, + #scrate::__private::codec::Encode, + #scrate::__private::codec::Decode, + #scrate::__private::scale_info::TypeInfo, + #scrate::__private::RuntimeDebug, + )] + pub enum RuntimeCall { + #variant_defs + } + #[cfg(test)] + impl RuntimeCall { + /// Return a list of the module names together with their size in memory. + pub const fn sizes() -> &'static [( &'static str, usize )] { + use #scrate::dispatch::Callable; + use core::mem::size_of; + &[#( + #pallet_attrs + ( + stringify!(#pallet_names), + size_of::< <#pallet_names as Callable<#runtime>>::RuntimeCall >(), + ), + )*] + } + + /// Panics with diagnostic information if the size is greater than the given `limit`. + pub fn assert_size_under(limit: usize) { + let size = core::mem::size_of::(); + let call_oversize = size > limit; + if call_oversize { + println!("Size of `Call` is {} bytes (provided limit is {} bytes)", size, limit); + let mut sizes = Self::sizes().to_vec(); + sizes.sort_by_key(|x| -(x.1 as isize)); + for (i, &(name, size)) in sizes.iter().enumerate().take(5) { + println!("Offender #{}: {} at {} bytes", i + 1, name, size); + } + if let Some((_, next_size)) = sizes.get(5) { + println!("{} others of size {} bytes or less", sizes.len() - 5, next_size); + } + panic!( + "Size of `Call` is more than limit; use `Box` on complex parameter types to reduce the size of `Call`. If the limit is too strong, maybe consider providing a higher limit." - ); - } - } - } - impl #scrate::dispatch::GetDispatchInfo for RuntimeCall { - fn get_dispatch_info(&self) -> #scrate::dispatch::DispatchInfo { - match self { - #( - #pallet_attrs - #variant_patterns => call.get_dispatch_info(), - )* - } - } - } - - impl #scrate::dispatch::CheckIfFeeless for RuntimeCall { - type Origin = #system_path::pallet_prelude::OriginFor<#runtime>; - fn is_feeless(&self, origin: &Self::Origin) -> bool { - match self { - #( - #pallet_attrs - #variant_patterns => call.is_feeless(origin), - )* - } - } - } - - impl #scrate::traits::GetCallMetadata for RuntimeCall { - fn get_call_metadata(&self) -> #scrate::traits::CallMetadata { - use #scrate::traits::GetCallName; - match self { - #( - #pallet_attrs - #variant_patterns => { - let function_name = call.get_call_name(); - let pallet_name = stringify!(#pallet_names); - #scrate::traits::CallMetadata { function_name, pallet_name } - } - )* - } - } - - fn get_module_names() -> &'static [&'static str] { - &[#( - #pallet_attrs - stringify!(#pallet_names), - )*] - } - - fn get_call_names(module: &str) -> &'static [&'static str] { - use #scrate::{dispatch::Callable, traits::GetCallName}; - match module { - #( - #pallet_attrs - stringify!(#pallet_names) => - <<#pallet_names as Callable<#runtime>>::RuntimeCall - as GetCallName>::get_call_names(), - )* - _ => unreachable!(), - } - } - } - impl #scrate::__private::Dispatchable for RuntimeCall { - type RuntimeOrigin = RuntimeOrigin; - type Config = RuntimeCall; - type Info = #scrate::dispatch::DispatchInfo; - type PostInfo = #scrate::dispatch::PostDispatchInfo; - fn dispatch(self, origin: RuntimeOrigin) -> #scrate::dispatch::DispatchResultWithPostInfo { - if !::filter_call(&origin, &self) { - return ::core::result::Result::Err( - #system_path::Error::<#runtime>::CallFiltered.into() - ); - } - - #scrate::traits::UnfilteredDispatchable::dispatch_bypass_filter(self, origin) - } - } - impl #scrate::traits::UnfilteredDispatchable for RuntimeCall { - type RuntimeOrigin = RuntimeOrigin; - fn dispatch_bypass_filter(self, origin: RuntimeOrigin) -> #scrate::dispatch::DispatchResultWithPostInfo { - match self { - #( - #pallet_attrs - #variant_patterns => - #scrate::traits::UnfilteredDispatchable::dispatch_bypass_filter(call, origin), - )* - } - } - } - - #( - #pallet_attrs - impl #scrate::traits::IsSubType<#scrate::dispatch::CallableCallFor<#pallet_names, #runtime>> for RuntimeCall { - #[allow(unreachable_patterns)] - fn is_sub_type(&self) -> Option<&#scrate::dispatch::CallableCallFor<#pallet_names, #runtime>> { - match self { - #variant_patterns => Some(call), - // May be unreachable - _ => None, - } - } - } - - #pallet_attrs - impl From<#scrate::dispatch::CallableCallFor<#pallet_names, #runtime>> for RuntimeCall { - fn from(call: #scrate::dispatch::CallableCallFor<#pallet_names, #runtime>) -> Self { - #variant_patterns - } - } - )* - } + ); + } + } + } + impl #scrate::dispatch::GetDispatchInfo for RuntimeCall { + fn get_dispatch_info(&self) -> #scrate::dispatch::DispatchInfo { + match self { + #( + #pallet_attrs + #variant_patterns => call.get_dispatch_info(), + )* + } + } + } + + impl #scrate::dispatch::CheckIfFeeless for RuntimeCall { + type Origin = #system_path::pallet_prelude::OriginFor<#runtime>; + fn is_feeless(&self, origin: &Self::Origin) -> bool { + match self { + #( + #pallet_attrs + #variant_patterns => call.is_feeless(origin), + )* + } + } + } + + impl #scrate::traits::GetCallMetadata for RuntimeCall { + fn get_call_metadata(&self) -> #scrate::traits::CallMetadata { + use #scrate::traits::GetCallName; + match self { + #( + #pallet_attrs + #variant_patterns => { + let function_name = call.get_call_name(); + let pallet_name = stringify!(#pallet_names); + #scrate::traits::CallMetadata { function_name, pallet_name } + } + )* + } + } + + fn get_module_names() -> &'static [&'static str] { + &[#( + #pallet_attrs + stringify!(#pallet_names), + )*] + } + + fn get_call_names(module: &str) -> &'static [&'static str] { + use #scrate::{dispatch::Callable, traits::GetCallName}; + match module { + #( + #pallet_attrs + stringify!(#pallet_names) => + <<#pallet_names as Callable<#runtime>>::RuntimeCall + as GetCallName>::get_call_names(), + )* + _ => unreachable!(), + } + } + } + impl #scrate::__private::Dispatchable for RuntimeCall { + type RuntimeOrigin = RuntimeOrigin; + type Config = RuntimeCall; + type Info = #scrate::dispatch::DispatchInfo; + type PostInfo = #scrate::dispatch::PostDispatchInfo; + fn dispatch(self, origin: RuntimeOrigin) -> #scrate::dispatch::DispatchResultWithPostInfo { + if !::filter_call(&origin, &self) { + return ::core::result::Result::Err( + #system_path::Error::<#runtime>::CallFiltered.into() + ); + } + + #scrate::traits::UnfilteredDispatchable::dispatch_bypass_filter(self, origin) + } + } + impl #scrate::traits::UnfilteredDispatchable for RuntimeCall { + type RuntimeOrigin = RuntimeOrigin; + fn dispatch_bypass_filter(self, origin: RuntimeOrigin) -> #scrate::dispatch::DispatchResultWithPostInfo { + match self { + #( + #pallet_attrs + #variant_patterns => + #scrate::traits::UnfilteredDispatchable::dispatch_bypass_filter(call, origin), + )* + } + } + } + + #( + #pallet_attrs + impl #scrate::traits::IsSubType<#scrate::dispatch::CallableCallFor<#pallet_names, #runtime>> for RuntimeCall { + #[allow(unreachable_patterns)] + fn is_sub_type(&self) -> Option<&#scrate::dispatch::CallableCallFor<#pallet_names, #runtime>> { + match self { + #variant_patterns => Some(call), + // May be unreachable + _ => None, + } + } + } + + #pallet_attrs + impl From<#scrate::dispatch::CallableCallFor<#pallet_names, #runtime>> for RuntimeCall { + fn from(call: #scrate::dispatch::CallableCallFor<#pallet_names, #runtime>) -> Self { + #variant_patterns + } + } + )* + } } diff --git a/support/procedural-fork/src/construct_runtime/expand/composite_helper.rs b/support/procedural-fork/src/construct_runtime/expand/composite_helper.rs index 101a476fb..be6b2f085 100644 --- a/support/procedural-fork/src/construct_runtime/expand/composite_helper.rs +++ b/support/procedural-fork/src/construct_runtime/expand/composite_helper.rs @@ -20,82 +20,82 @@ use proc_macro2::{Ident, TokenStream}; use quote::quote; pub(crate) fn expand_conversion_fn( - composite_name: &str, - path: &PalletPath, - instance: Option<&Ident>, - variant_name: &Ident, + composite_name: &str, + path: &PalletPath, + instance: Option<&Ident>, + variant_name: &Ident, ) -> TokenStream { - let composite_name = quote::format_ident!("{}", composite_name); - let runtime_composite_name = quote::format_ident!("Runtime{}", composite_name); + let composite_name = quote::format_ident!("{}", composite_name); + let runtime_composite_name = quote::format_ident!("Runtime{}", composite_name); - if let Some(inst) = instance { - quote! { - impl From<#path::#composite_name<#path::#inst>> for #runtime_composite_name { - fn from(hr: #path::#composite_name<#path::#inst>) -> Self { - #runtime_composite_name::#variant_name(hr) - } - } - } - } else { - quote! { - impl From<#path::#composite_name> for #runtime_composite_name { - fn from(hr: #path::#composite_name) -> Self { - #runtime_composite_name::#variant_name(hr) - } - } - } - } + if let Some(inst) = instance { + quote! { + impl From<#path::#composite_name<#path::#inst>> for #runtime_composite_name { + fn from(hr: #path::#composite_name<#path::#inst>) -> Self { + #runtime_composite_name::#variant_name(hr) + } + } + } + } else { + quote! { + impl From<#path::#composite_name> for #runtime_composite_name { + fn from(hr: #path::#composite_name) -> Self { + #runtime_composite_name::#variant_name(hr) + } + } + } + } } pub(crate) fn expand_variant( - composite_name: &str, - index: u8, - path: &PalletPath, - instance: Option<&Ident>, - variant_name: &Ident, + composite_name: &str, + index: u8, + path: &PalletPath, + instance: Option<&Ident>, + variant_name: &Ident, ) -> TokenStream { - let composite_name = quote::format_ident!("{}", composite_name); + let composite_name = quote::format_ident!("{}", composite_name); - if let Some(inst) = instance { - quote! { - #[codec(index = #index)] - #variant_name(#path::#composite_name<#path::#inst>), - } - } else { - quote! { - #[codec(index = #index)] - #variant_name(#path::#composite_name), - } - } + if let Some(inst) = instance { + quote! { + #[codec(index = #index)] + #variant_name(#path::#composite_name<#path::#inst>), + } + } else { + quote! { + #[codec(index = #index)] + #variant_name(#path::#composite_name), + } + } } pub(crate) fn expand_variant_count( - composite_name: &str, - path: &PalletPath, - instance: Option<&Ident>, + composite_name: &str, + path: &PalletPath, + instance: Option<&Ident>, ) -> TokenStream { - let composite_name = quote::format_ident!("{}", composite_name); + let composite_name = quote::format_ident!("{}", composite_name); - if let Some(inst) = instance { - quote! { - #path::#composite_name::<#path::#inst>::VARIANT_COUNT - } - } else { - // Wrapped `<`..`>` means: use default type parameter for enum. - // - // This is used for pallets without instance support or pallets with instance support when - // we don't specify instance: - // - // ``` - // pub struct Pallet{..} - // - // #[pallet::composite_enum] - // pub enum HoldReason {..} - // - // Pallet1: pallet_x, // <- default type parameter - // ``` - quote! { - <#path::#composite_name>::VARIANT_COUNT - } - } + if let Some(inst) = instance { + quote! { + #path::#composite_name::<#path::#inst>::VARIANT_COUNT + } + } else { + // Wrapped `<`..`>` means: use default type parameter for enum. + // + // This is used for pallets without instance support or pallets with instance support when + // we don't specify instance: + // + // ``` + // pub struct Pallet{..} + // + // #[pallet::composite_enum] + // pub enum HoldReason {..} + // + // Pallet1: pallet_x, // <- default type parameter + // ``` + quote! { + <#path::#composite_name>::VARIANT_COUNT + } + } } diff --git a/support/procedural-fork/src/construct_runtime/expand/config.rs b/support/procedural-fork/src/construct_runtime/expand/config.rs index dbbe6ba6e..ff715e584 100644 --- a/support/procedural-fork/src/construct_runtime/expand/config.rs +++ b/support/procedural-fork/src/construct_runtime/expand/config.rs @@ -23,125 +23,135 @@ use std::str::FromStr; use syn::Ident; pub fn expand_outer_config( - runtime: &Ident, - pallet_decls: &[Pallet], - scrate: &TokenStream, + runtime: &Ident, + pallet_decls: &[Pallet], + scrate: &TokenStream, ) -> TokenStream { - let mut types = TokenStream::new(); - let mut fields = TokenStream::new(); - let mut genesis_build_calls = TokenStream::new(); - let mut query_genesis_config_part_macros = Vec::new(); + let mut types = TokenStream::new(); + let mut fields = TokenStream::new(); + let mut genesis_build_calls = TokenStream::new(); + let mut query_genesis_config_part_macros = Vec::new(); - for decl in pallet_decls { - if let Some(pallet_entry) = decl.find_part("Config") { - let path = &decl.path; - let pallet_name = &decl.name; - let path_str = path.into_token_stream().to_string(); - let config = format_ident!("{}Config", pallet_name); - let field_name = - &Ident::new(&pallet_name.to_string().to_snake_case(), decl.name.span()); - let part_is_generic = !pallet_entry.generics.params.is_empty(); - let attr = &decl.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { - let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) - .expect("was successfully parsed before; qed"); - quote! { - #acc - #attr - } - }); + for decl in pallet_decls { + if let Some(pallet_entry) = decl.find_part("Config") { + let path = &decl.path; + let pallet_name = &decl.name; + let path_str = path.into_token_stream().to_string(); + let config = format_ident!("{}Config", pallet_name); + let field_name = + &Ident::new(&pallet_name.to_string().to_snake_case(), decl.name.span()); + let part_is_generic = !pallet_entry.generics.params.is_empty(); + let attr = &decl + .cfg_pattern + .iter() + .fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); - types.extend(expand_config_types(attr, runtime, decl, &config, part_is_generic)); - fields.extend(quote!(#attr pub #field_name: #config,)); - genesis_build_calls - .extend(expand_config_build_storage_call(scrate, &config, attr, field_name)); - query_genesis_config_part_macros.push(quote! { + types.extend(expand_config_types( + attr, + runtime, + decl, + &config, + part_is_generic, + )); + fields.extend(quote!(#attr pub #field_name: #config,)); + genesis_build_calls.extend(expand_config_build_storage_call( + scrate, &config, attr, field_name, + )); + query_genesis_config_part_macros.push(quote! { #path::__substrate_genesis_config_check::is_genesis_config_defined!(#pallet_name); #[cfg(feature = "std")] #path::__substrate_genesis_config_check::is_std_enabled_for_genesis!(#pallet_name, #path_str); }); - } - } + } + } - quote! { - #( #query_genesis_config_part_macros )* + quote! { + #( #query_genesis_config_part_macros )* - #types + #types - use #scrate::__private::serde as __genesis_config_serde_import__; - #[derive(#scrate::__private::serde::Serialize, #scrate::__private::serde::Deserialize, Default)] - #[serde(rename_all = "camelCase")] - #[serde(deny_unknown_fields)] - #[serde(crate = "__genesis_config_serde_import__")] - pub struct RuntimeGenesisConfig { - #fields - } + use #scrate::__private::serde as __genesis_config_serde_import__; + #[derive(#scrate::__private::serde::Serialize, #scrate::__private::serde::Deserialize, Default)] + #[serde(rename_all = "camelCase")] + #[serde(deny_unknown_fields)] + #[serde(crate = "__genesis_config_serde_import__")] + pub struct RuntimeGenesisConfig { + #fields + } - #[cfg(any(feature = "std", test))] - impl #scrate::sp_runtime::BuildStorage for RuntimeGenesisConfig { - fn assimilate_storage( - &self, - storage: &mut #scrate::sp_runtime::Storage, - ) -> std::result::Result<(), String> { - #scrate::__private::BasicExternalities::execute_with_storage(storage, || { - ::build(&self); - Ok(()) - }) - } - } + #[cfg(any(feature = "std", test))] + impl #scrate::sp_runtime::BuildStorage for RuntimeGenesisConfig { + fn assimilate_storage( + &self, + storage: &mut #scrate::sp_runtime::Storage, + ) -> std::result::Result<(), String> { + #scrate::__private::BasicExternalities::execute_with_storage(storage, || { + ::build(&self); + Ok(()) + }) + } + } - impl #scrate::traits::BuildGenesisConfig for RuntimeGenesisConfig { - fn build(&self) { - #genesis_build_calls - ::on_genesis(); - } - } + impl #scrate::traits::BuildGenesisConfig for RuntimeGenesisConfig { + fn build(&self) { + #genesis_build_calls + ::on_genesis(); + } + } - /// Test the `Default` derive impl of the `RuntimeGenesisConfig`. - #[cfg(test)] - #[test] - fn test_genesis_config_builds() { - #scrate::__private::sp_io::TestExternalities::default().execute_with(|| { - ::build( - &RuntimeGenesisConfig::default() - ); - }); - } - } + /// Test the `Default` derive impl of the `RuntimeGenesisConfig`. + #[cfg(test)] + #[test] + fn test_genesis_config_builds() { + #scrate::__private::sp_io::TestExternalities::default().execute_with(|| { + ::build( + &RuntimeGenesisConfig::default() + ); + }); + } + } } fn expand_config_types( - attr: &TokenStream, - runtime: &Ident, - decl: &Pallet, - config: &Ident, - part_is_generic: bool, + attr: &TokenStream, + runtime: &Ident, + decl: &Pallet, + config: &Ident, + part_is_generic: bool, ) -> TokenStream { - let path = &decl.path; + let path = &decl.path; - match (decl.instance.as_ref(), part_is_generic) { - (Some(inst), true) => quote! { - #attr - pub type #config = #path::GenesisConfig<#runtime, #path::#inst>; - }, - (None, true) => quote! { - #attr - pub type #config = #path::GenesisConfig<#runtime>; - }, - (_, false) => quote! { - #attr - pub type #config = #path::GenesisConfig; - }, - } + match (decl.instance.as_ref(), part_is_generic) { + (Some(inst), true) => quote! { + #attr + pub type #config = #path::GenesisConfig<#runtime, #path::#inst>; + }, + (None, true) => quote! { + #attr + pub type #config = #path::GenesisConfig<#runtime>; + }, + (_, false) => quote! { + #attr + pub type #config = #path::GenesisConfig; + }, + } } fn expand_config_build_storage_call( - scrate: &TokenStream, - pallet_genesis_config: &Ident, - attr: &TokenStream, - field_name: &Ident, + scrate: &TokenStream, + pallet_genesis_config: &Ident, + attr: &TokenStream, + field_name: &Ident, ) -> TokenStream { - quote! { - #attr - <#pallet_genesis_config as #scrate::traits::BuildGenesisConfig>::build(&self.#field_name); - } + quote! { + #attr + <#pallet_genesis_config as #scrate::traits::BuildGenesisConfig>::build(&self.#field_name); + } } diff --git a/support/procedural-fork/src/construct_runtime/expand/freeze_reason.rs b/support/procedural-fork/src/construct_runtime/expand/freeze_reason.rs index f12f99526..131c919ef 100644 --- a/support/procedural-fork/src/construct_runtime/expand/freeze_reason.rs +++ b/support/procedural-fork/src/construct_runtime/expand/freeze_reason.rs @@ -21,55 +21,55 @@ use proc_macro2::TokenStream; use quote::quote; pub fn expand_outer_freeze_reason(pallet_decls: &[Pallet], scrate: &TokenStream) -> TokenStream { - let mut conversion_fns = Vec::new(); - let mut freeze_reason_variants = Vec::new(); - let mut freeze_reason_variants_count = Vec::new(); - for decl in pallet_decls { - if let Some(_) = decl.find_part("FreezeReason") { - let variant_name = &decl.name; - let path = &decl.path; - let index = decl.index; - let instance = decl.instance.as_ref(); + let mut conversion_fns = Vec::new(); + let mut freeze_reason_variants = Vec::new(); + let mut freeze_reason_variants_count = Vec::new(); + for decl in pallet_decls { + if let Some(_) = decl.find_part("FreezeReason") { + let variant_name = &decl.name; + let path = &decl.path; + let index = decl.index; + let instance = decl.instance.as_ref(); - conversion_fns.push(composite_helper::expand_conversion_fn( - "FreezeReason", - path, - instance, - variant_name, - )); + conversion_fns.push(composite_helper::expand_conversion_fn( + "FreezeReason", + path, + instance, + variant_name, + )); - freeze_reason_variants.push(composite_helper::expand_variant( - "FreezeReason", - index, - path, - instance, - variant_name, - )); + freeze_reason_variants.push(composite_helper::expand_variant( + "FreezeReason", + index, + path, + instance, + variant_name, + )); - freeze_reason_variants_count.push(composite_helper::expand_variant_count( - "FreezeReason", - path, - instance, - )); - } - } + freeze_reason_variants_count.push(composite_helper::expand_variant_count( + "FreezeReason", + path, + instance, + )); + } + } - quote! { - /// A reason for placing a freeze on funds. - #[derive( - Copy, Clone, Eq, PartialEq, - #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, - #scrate::__private::scale_info::TypeInfo, - #scrate::__private::RuntimeDebug, - )] - pub enum RuntimeFreezeReason { - #( #freeze_reason_variants )* - } + quote! { + /// A reason for placing a freeze on funds. + #[derive( + Copy, Clone, Eq, PartialEq, + #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, + #scrate::__private::scale_info::TypeInfo, + #scrate::__private::RuntimeDebug, + )] + pub enum RuntimeFreezeReason { + #( #freeze_reason_variants )* + } - impl #scrate::traits::VariantCount for RuntimeFreezeReason { - const VARIANT_COUNT: u32 = 0 #( + #freeze_reason_variants_count )*; - } + impl #scrate::traits::VariantCount for RuntimeFreezeReason { + const VARIANT_COUNT: u32 = 0 #( + #freeze_reason_variants_count )*; + } - #( #conversion_fns )* - } + #( #conversion_fns )* + } } diff --git a/support/procedural-fork/src/construct_runtime/expand/hold_reason.rs b/support/procedural-fork/src/construct_runtime/expand/hold_reason.rs index cdab92712..58870a321 100644 --- a/support/procedural-fork/src/construct_runtime/expand/hold_reason.rs +++ b/support/procedural-fork/src/construct_runtime/expand/hold_reason.rs @@ -21,55 +21,55 @@ use proc_macro2::TokenStream; use quote::quote; pub fn expand_outer_hold_reason(pallet_decls: &[Pallet], scrate: &TokenStream) -> TokenStream { - let mut conversion_fns = Vec::new(); - let mut hold_reason_variants = Vec::new(); - let mut hold_reason_variants_count = Vec::new(); - for decl in pallet_decls { - if let Some(_) = decl.find_part("HoldReason") { - let variant_name = &decl.name; - let path = &decl.path; - let index = decl.index; - let instance = decl.instance.as_ref(); + let mut conversion_fns = Vec::new(); + let mut hold_reason_variants = Vec::new(); + let mut hold_reason_variants_count = Vec::new(); + for decl in pallet_decls { + if let Some(_) = decl.find_part("HoldReason") { + let variant_name = &decl.name; + let path = &decl.path; + let index = decl.index; + let instance = decl.instance.as_ref(); - conversion_fns.push(composite_helper::expand_conversion_fn( - "HoldReason", - path, - instance, - variant_name, - )); + conversion_fns.push(composite_helper::expand_conversion_fn( + "HoldReason", + path, + instance, + variant_name, + )); - hold_reason_variants.push(composite_helper::expand_variant( - "HoldReason", - index, - path, - instance, - variant_name, - )); + hold_reason_variants.push(composite_helper::expand_variant( + "HoldReason", + index, + path, + instance, + variant_name, + )); - hold_reason_variants_count.push(composite_helper::expand_variant_count( - "HoldReason", - path, - instance, - )); - } - } + hold_reason_variants_count.push(composite_helper::expand_variant_count( + "HoldReason", + path, + instance, + )); + } + } - quote! { - /// A reason for placing a hold on funds. - #[derive( - Copy, Clone, Eq, PartialEq, - #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, - #scrate::__private::scale_info::TypeInfo, - #scrate::__private::RuntimeDebug, - )] - pub enum RuntimeHoldReason { - #( #hold_reason_variants )* - } + quote! { + /// A reason for placing a hold on funds. + #[derive( + Copy, Clone, Eq, PartialEq, + #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, + #scrate::__private::scale_info::TypeInfo, + #scrate::__private::RuntimeDebug, + )] + pub enum RuntimeHoldReason { + #( #hold_reason_variants )* + } - impl #scrate::traits::VariantCount for RuntimeHoldReason { - const VARIANT_COUNT: u32 = 0 #( + #hold_reason_variants_count )*; - } + impl #scrate::traits::VariantCount for RuntimeHoldReason { + const VARIANT_COUNT: u32 = 0 #( + #hold_reason_variants_count )*; + } - #( #conversion_fns )* - } + #( #conversion_fns )* + } } diff --git a/support/procedural-fork/src/construct_runtime/expand/inherent.rs b/support/procedural-fork/src/construct_runtime/expand/inherent.rs index da483fa6c..b58d540fe 100644 --- a/support/procedural-fork/src/construct_runtime/expand/inherent.rs +++ b/support/procedural-fork/src/construct_runtime/expand/inherent.rs @@ -22,233 +22,236 @@ use std::str::FromStr; use syn::Ident; pub fn expand_outer_inherent( - runtime: &Ident, - block: &TokenStream, - unchecked_extrinsic: &TokenStream, - pallet_decls: &[Pallet], - scrate: &TokenStream, + runtime: &Ident, + block: &TokenStream, + unchecked_extrinsic: &TokenStream, + pallet_decls: &[Pallet], + scrate: &TokenStream, ) -> TokenStream { - let mut pallet_names = Vec::new(); - let mut pallet_attrs = Vec::new(); - let mut query_inherent_part_macros = Vec::new(); - - for pallet_decl in pallet_decls { - if pallet_decl.exists_part("Inherent") { - let name = &pallet_decl.name; - let path = &pallet_decl.path; - let attr = pallet_decl.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { - let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) - .expect("was successfully parsed before; qed"); - quote! { - #acc - #attr - } - }); - - pallet_names.push(name); - pallet_attrs.push(attr); - query_inherent_part_macros.push(quote! { - #path::__substrate_inherent_check::is_inherent_part_defined!(#name); - }); - } - } - - quote! { - #( #query_inherent_part_macros )* - - trait InherentDataExt { - fn create_extrinsics(&self) -> - #scrate::__private::sp_std::vec::Vec<<#block as #scrate::sp_runtime::traits::Block>::Extrinsic>; - fn check_extrinsics(&self, block: &#block) -> #scrate::inherent::CheckInherentsResult; - } - - impl InherentDataExt for #scrate::inherent::InherentData { - fn create_extrinsics(&self) -> - #scrate::__private::sp_std::vec::Vec<<#block as #scrate::sp_runtime::traits::Block>::Extrinsic> - { - use #scrate::inherent::ProvideInherent; - - let mut inherents = #scrate::__private::sp_std::vec::Vec::new(); - - #( - #pallet_attrs - if let Some(inherent) = #pallet_names::create_inherent(self) { - let inherent = <#unchecked_extrinsic as #scrate::sp_runtime::traits::Extrinsic>::new( - inherent.into(), - None, - ).expect("Runtime UncheckedExtrinsic is not Opaque, so it has to return \ - `Some`; qed"); - - inherents.push(inherent); - } - )* - - inherents - } - - fn check_extrinsics(&self, block: &#block) -> #scrate::inherent::CheckInherentsResult { - use #scrate::inherent::{ProvideInherent, IsFatalError}; - use #scrate::traits::{IsSubType, ExtrinsicCall}; - use #scrate::sp_runtime::traits::Block as _; - use #scrate::__private::{sp_inherents::Error, log}; - - let mut result = #scrate::inherent::CheckInherentsResult::new(); - - // This handle assume we abort on the first fatal error. - fn handle_put_error_result(res: Result<(), Error>) { - const LOG_TARGET: &str = "runtime::inherent"; - match res { - Ok(()) => (), - Err(Error::InherentDataExists(id)) => - log::debug!( - target: LOG_TARGET, - "Some error already reported for inherent {:?}, new non fatal \ - error is ignored", - id - ), - Err(Error::FatalErrorReported) => - log::error!( - target: LOG_TARGET, - "Fatal error already reported, unexpected considering there is \ - only one fatal error", - ), - Err(_) => - log::error!( - target: LOG_TARGET, - "Unexpected error from `put_error` operation", - ), - } - } - - for xt in block.extrinsics() { - // Inherents are before any other extrinsics. - // And signed extrinsics are not inherents. - if #scrate::sp_runtime::traits::Extrinsic::is_signed(xt).unwrap_or(false) { - break - } - - let mut is_inherent = false; - - #( - #pallet_attrs - { - let call = <#unchecked_extrinsic as ExtrinsicCall>::call(xt); - if let Some(call) = IsSubType::<_>::is_sub_type(call) { - if #pallet_names::is_inherent(call) { - is_inherent = true; - if let Err(e) = #pallet_names::check_inherent(call, self) { - handle_put_error_result(result.put_error( - #pallet_names::INHERENT_IDENTIFIER, &e - )); - if e.is_fatal_error() { - return result; - } - } - } - } - } - )* - - // Inherents are before any other extrinsics. - // No module marked it as inherent thus it is not. - if !is_inherent { - break - } - } - - #( - #pallet_attrs - match #pallet_names::is_inherent_required(self) { - Ok(Some(e)) => { - let found = block.extrinsics().iter().any(|xt| { - let is_signed = #scrate::sp_runtime::traits::Extrinsic::is_signed(xt) - .unwrap_or(false); - - if !is_signed { - let call = < - #unchecked_extrinsic as ExtrinsicCall - >::call(xt); - if let Some(call) = IsSubType::<_>::is_sub_type(call) { - #pallet_names::is_inherent(&call) - } else { - false - } - } else { - // Signed extrinsics are not inherents. - false - } - }); - - if !found { - handle_put_error_result(result.put_error( - #pallet_names::INHERENT_IDENTIFIER, &e - )); - if e.is_fatal_error() { - return result; - } - } - }, - Ok(None) => (), - Err(e) => { - handle_put_error_result(result.put_error( - #pallet_names::INHERENT_IDENTIFIER, &e - )); - if e.is_fatal_error() { - return result; - } - }, - } - )* - - result - } - } - - impl #scrate::traits::IsInherent<<#block as #scrate::sp_runtime::traits::Block>::Extrinsic> for #runtime { - fn is_inherent(ext: &<#block as #scrate::sp_runtime::traits::Block>::Extrinsic) -> bool { - use #scrate::inherent::ProvideInherent; - use #scrate::traits::{IsSubType, ExtrinsicCall}; - - if #scrate::sp_runtime::traits::Extrinsic::is_signed(ext).unwrap_or(false) { - // Signed extrinsics are never inherents. - return false - } - - #( - #pallet_attrs - { - let call = <#unchecked_extrinsic as ExtrinsicCall>::call(ext); - if let Some(call) = IsSubType::<_>::is_sub_type(call) { - if <#pallet_names as ProvideInherent>::is_inherent(&call) { - return true; - } - } - } - )* - false - } - } - - impl #scrate::traits::EnsureInherentsAreFirst<#block> for #runtime { - fn ensure_inherents_are_first(block: &#block) -> Result { - use #scrate::inherent::ProvideInherent; - use #scrate::traits::{IsSubType, ExtrinsicCall}; - use #scrate::sp_runtime::traits::Block as _; - - let mut num_inherents = 0u32; - - for (i, xt) in block.extrinsics().iter().enumerate() { - if >::is_inherent(xt) { - if num_inherents != i as u32 { - return Err(i as u32); - } - - num_inherents += 1; // Safe since we are in an `enumerate` loop. - } - } - - Ok(num_inherents) - } - } - } + let mut pallet_names = Vec::new(); + let mut pallet_attrs = Vec::new(); + let mut query_inherent_part_macros = Vec::new(); + + for pallet_decl in pallet_decls { + if pallet_decl.exists_part("Inherent") { + let name = &pallet_decl.name; + let path = &pallet_decl.path; + let attr = pallet_decl + .cfg_pattern + .iter() + .fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); + + pallet_names.push(name); + pallet_attrs.push(attr); + query_inherent_part_macros.push(quote! { + #path::__substrate_inherent_check::is_inherent_part_defined!(#name); + }); + } + } + + quote! { + #( #query_inherent_part_macros )* + + trait InherentDataExt { + fn create_extrinsics(&self) -> + #scrate::__private::sp_std::vec::Vec<<#block as #scrate::sp_runtime::traits::Block>::Extrinsic>; + fn check_extrinsics(&self, block: &#block) -> #scrate::inherent::CheckInherentsResult; + } + + impl InherentDataExt for #scrate::inherent::InherentData { + fn create_extrinsics(&self) -> + #scrate::__private::sp_std::vec::Vec<<#block as #scrate::sp_runtime::traits::Block>::Extrinsic> + { + use #scrate::inherent::ProvideInherent; + + let mut inherents = #scrate::__private::sp_std::vec::Vec::new(); + + #( + #pallet_attrs + if let Some(inherent) = #pallet_names::create_inherent(self) { + let inherent = <#unchecked_extrinsic as #scrate::sp_runtime::traits::Extrinsic>::new( + inherent.into(), + None, + ).expect("Runtime UncheckedExtrinsic is not Opaque, so it has to return \ + `Some`; qed"); + + inherents.push(inherent); + } + )* + + inherents + } + + fn check_extrinsics(&self, block: &#block) -> #scrate::inherent::CheckInherentsResult { + use #scrate::inherent::{ProvideInherent, IsFatalError}; + use #scrate::traits::{IsSubType, ExtrinsicCall}; + use #scrate::sp_runtime::traits::Block as _; + use #scrate::__private::{sp_inherents::Error, log}; + + let mut result = #scrate::inherent::CheckInherentsResult::new(); + + // This handle assume we abort on the first fatal error. + fn handle_put_error_result(res: Result<(), Error>) { + const LOG_TARGET: &str = "runtime::inherent"; + match res { + Ok(()) => (), + Err(Error::InherentDataExists(id)) => + log::debug!( + target: LOG_TARGET, + "Some error already reported for inherent {:?}, new non fatal \ + error is ignored", + id + ), + Err(Error::FatalErrorReported) => + log::error!( + target: LOG_TARGET, + "Fatal error already reported, unexpected considering there is \ + only one fatal error", + ), + Err(_) => + log::error!( + target: LOG_TARGET, + "Unexpected error from `put_error` operation", + ), + } + } + + for xt in block.extrinsics() { + // Inherents are before any other extrinsics. + // And signed extrinsics are not inherents. + if #scrate::sp_runtime::traits::Extrinsic::is_signed(xt).unwrap_or(false) { + break + } + + let mut is_inherent = false; + + #( + #pallet_attrs + { + let call = <#unchecked_extrinsic as ExtrinsicCall>::call(xt); + if let Some(call) = IsSubType::<_>::is_sub_type(call) { + if #pallet_names::is_inherent(call) { + is_inherent = true; + if let Err(e) = #pallet_names::check_inherent(call, self) { + handle_put_error_result(result.put_error( + #pallet_names::INHERENT_IDENTIFIER, &e + )); + if e.is_fatal_error() { + return result; + } + } + } + } + } + )* + + // Inherents are before any other extrinsics. + // No module marked it as inherent thus it is not. + if !is_inherent { + break + } + } + + #( + #pallet_attrs + match #pallet_names::is_inherent_required(self) { + Ok(Some(e)) => { + let found = block.extrinsics().iter().any(|xt| { + let is_signed = #scrate::sp_runtime::traits::Extrinsic::is_signed(xt) + .unwrap_or(false); + + if !is_signed { + let call = < + #unchecked_extrinsic as ExtrinsicCall + >::call(xt); + if let Some(call) = IsSubType::<_>::is_sub_type(call) { + #pallet_names::is_inherent(&call) + } else { + false + } + } else { + // Signed extrinsics are not inherents. + false + } + }); + + if !found { + handle_put_error_result(result.put_error( + #pallet_names::INHERENT_IDENTIFIER, &e + )); + if e.is_fatal_error() { + return result; + } + } + }, + Ok(None) => (), + Err(e) => { + handle_put_error_result(result.put_error( + #pallet_names::INHERENT_IDENTIFIER, &e + )); + if e.is_fatal_error() { + return result; + } + }, + } + )* + + result + } + } + + impl #scrate::traits::IsInherent<<#block as #scrate::sp_runtime::traits::Block>::Extrinsic> for #runtime { + fn is_inherent(ext: &<#block as #scrate::sp_runtime::traits::Block>::Extrinsic) -> bool { + use #scrate::inherent::ProvideInherent; + use #scrate::traits::{IsSubType, ExtrinsicCall}; + + if #scrate::sp_runtime::traits::Extrinsic::is_signed(ext).unwrap_or(false) { + // Signed extrinsics are never inherents. + return false + } + + #( + #pallet_attrs + { + let call = <#unchecked_extrinsic as ExtrinsicCall>::call(ext); + if let Some(call) = IsSubType::<_>::is_sub_type(call) { + if <#pallet_names as ProvideInherent>::is_inherent(&call) { + return true; + } + } + } + )* + false + } + } + + impl #scrate::traits::EnsureInherentsAreFirst<#block> for #runtime { + fn ensure_inherents_are_first(block: &#block) -> Result { + use #scrate::inherent::ProvideInherent; + use #scrate::traits::{IsSubType, ExtrinsicCall}; + use #scrate::sp_runtime::traits::Block as _; + + let mut num_inherents = 0u32; + + for (i, xt) in block.extrinsics().iter().enumerate() { + if >::is_inherent(xt) { + if num_inherents != i as u32 { + return Err(i as u32); + } + + num_inherents += 1; // Safe since we are in an `enumerate` loop. + } + } + + Ok(num_inherents) + } + } + } } diff --git a/support/procedural-fork/src/construct_runtime/expand/lock_id.rs b/support/procedural-fork/src/construct_runtime/expand/lock_id.rs index e67c0da00..67c2fb933 100644 --- a/support/procedural-fork/src/construct_runtime/expand/lock_id.rs +++ b/support/procedural-fork/src/construct_runtime/expand/lock_id.rs @@ -21,44 +21,44 @@ use proc_macro2::TokenStream; use quote::quote; pub fn expand_outer_lock_id(pallet_decls: &[Pallet], scrate: &TokenStream) -> TokenStream { - let mut conversion_fns = Vec::new(); - let mut lock_id_variants = Vec::new(); - for decl in pallet_decls { - if let Some(_) = decl.find_part("LockId") { - let variant_name = &decl.name; - let path = &decl.path; - let index = decl.index; - let instance = decl.instance.as_ref(); + let mut conversion_fns = Vec::new(); + let mut lock_id_variants = Vec::new(); + for decl in pallet_decls { + if let Some(_) = decl.find_part("LockId") { + let variant_name = &decl.name; + let path = &decl.path; + let index = decl.index; + let instance = decl.instance.as_ref(); - conversion_fns.push(composite_helper::expand_conversion_fn( - "LockId", - path, - instance, - variant_name, - )); + conversion_fns.push(composite_helper::expand_conversion_fn( + "LockId", + path, + instance, + variant_name, + )); - lock_id_variants.push(composite_helper::expand_variant( - "LockId", - index, - path, - instance, - variant_name, - )); - } - } + lock_id_variants.push(composite_helper::expand_variant( + "LockId", + index, + path, + instance, + variant_name, + )); + } + } - quote! { - /// An identifier for each lock placed on funds. - #[derive( - Copy, Clone, Eq, PartialEq, - #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, - #scrate::__private::scale_info::TypeInfo, - #scrate::__private::RuntimeDebug, - )] - pub enum RuntimeLockId { - #( #lock_id_variants )* - } + quote! { + /// An identifier for each lock placed on funds. + #[derive( + Copy, Clone, Eq, PartialEq, + #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, + #scrate::__private::scale_info::TypeInfo, + #scrate::__private::RuntimeDebug, + )] + pub enum RuntimeLockId { + #( #lock_id_variants )* + } - #( #conversion_fns )* - } + #( #conversion_fns )* + } } diff --git a/support/procedural-fork/src/construct_runtime/expand/metadata.rs b/support/procedural-fork/src/construct_runtime/expand/metadata.rs index 0e76f9a92..f98c719ca 100644 --- a/support/procedural-fork/src/construct_runtime/expand/metadata.rs +++ b/support/procedural-fork/src/construct_runtime/expand/metadata.rs @@ -22,237 +22,240 @@ use std::str::FromStr; use syn::Ident; pub fn expand_runtime_metadata( - runtime: &Ident, - pallet_declarations: &[Pallet], - scrate: &TokenStream, - extrinsic: &TokenStream, - system_path: &PalletPath, + runtime: &Ident, + pallet_declarations: &[Pallet], + scrate: &TokenStream, + extrinsic: &TokenStream, + system_path: &PalletPath, ) -> TokenStream { - let pallets = pallet_declarations - .iter() - .filter_map(|pallet_declaration| { - pallet_declaration.find_part("Pallet").map(|_| { - let filtered_names: Vec<_> = pallet_declaration - .pallet_parts() - .iter() - .filter(|part| part.name() != "Pallet") - .map(|part| part.name()) - .collect(); - (pallet_declaration, filtered_names) - }) - }) - .map(|(decl, filtered_names)| { - let name = &decl.name; - let index = &decl.index; - let storage = expand_pallet_metadata_storage(&filtered_names, runtime, decl); - let calls = expand_pallet_metadata_calls(&filtered_names, runtime, decl); - let event = expand_pallet_metadata_events(&filtered_names, runtime, scrate, decl); - let constants = expand_pallet_metadata_constants(runtime, decl); - let errors = expand_pallet_metadata_errors(runtime, decl); - let docs = expand_pallet_metadata_docs(runtime, decl); - let attr = decl.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { - let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) - .expect("was successfully parsed before; qed"); - quote! { - #acc - #attr - } - }); + let pallets = pallet_declarations + .iter() + .filter_map(|pallet_declaration| { + pallet_declaration.find_part("Pallet").map(|_| { + let filtered_names: Vec<_> = pallet_declaration + .pallet_parts() + .iter() + .filter(|part| part.name() != "Pallet") + .map(|part| part.name()) + .collect(); + (pallet_declaration, filtered_names) + }) + }) + .map(|(decl, filtered_names)| { + let name = &decl.name; + let index = &decl.index; + let storage = expand_pallet_metadata_storage(&filtered_names, runtime, decl); + let calls = expand_pallet_metadata_calls(&filtered_names, runtime, decl); + let event = expand_pallet_metadata_events(&filtered_names, runtime, scrate, decl); + let constants = expand_pallet_metadata_constants(runtime, decl); + let errors = expand_pallet_metadata_errors(runtime, decl); + let docs = expand_pallet_metadata_docs(runtime, decl); + let attr = decl + .cfg_pattern + .iter() + .fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); - quote! { - #attr - #scrate::__private::metadata_ir::PalletMetadataIR { - name: stringify!(#name), - index: #index, - storage: #storage, - calls: #calls, - event: #event, - constants: #constants, - error: #errors, - docs: #docs, - } - } - }) - .collect::>(); + quote! { + #attr + #scrate::__private::metadata_ir::PalletMetadataIR { + name: stringify!(#name), + index: #index, + storage: #storage, + calls: #calls, + event: #event, + constants: #constants, + error: #errors, + docs: #docs, + } + } + }) + .collect::>(); - quote! { - impl #runtime { - fn metadata_ir() -> #scrate::__private::metadata_ir::MetadataIR { - // Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata. - // The function is implemented by calling `impl_runtime_apis!`. - // - // However, the `construct_runtime!` may be called without calling `impl_runtime_apis!`. - // Rely on the `Deref` trait to differentiate between a runtime that implements - // APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro construct_runtime!). - // - // Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function. - // `InternalConstructRuntime` is implemented by the `construct_runtime!` for Runtime references (`& Runtime`), - // while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`). - // - // Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!` - // when both macros are called; and will resolve an empty `runtime_metadata` when only the `construct_runtime!` - // is called. - // - // `Deref` needs a reference for resolving the function call. - let rt = #runtime; + quote! { + impl #runtime { + fn metadata_ir() -> #scrate::__private::metadata_ir::MetadataIR { + // Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata. + // The function is implemented by calling `impl_runtime_apis!`. + // + // However, the `construct_runtime!` may be called without calling `impl_runtime_apis!`. + // Rely on the `Deref` trait to differentiate between a runtime that implements + // APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro construct_runtime!). + // + // Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function. + // `InternalConstructRuntime` is implemented by the `construct_runtime!` for Runtime references (`& Runtime`), + // while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`). + // + // Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!` + // when both macros are called; and will resolve an empty `runtime_metadata` when only the `construct_runtime!` + // is called. + // + // `Deref` needs a reference for resolving the function call. + let rt = #runtime; - let ty = #scrate::__private::scale_info::meta_type::<#extrinsic>(); - let address_ty = #scrate::__private::scale_info::meta_type::< - <<#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::SignaturePayload as #scrate::sp_runtime::traits::SignaturePayload>::SignatureAddress - >(); - let call_ty = #scrate::__private::scale_info::meta_type::< - <#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::Call - >(); - let signature_ty = #scrate::__private::scale_info::meta_type::< - <<#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::SignaturePayload as #scrate::sp_runtime::traits::SignaturePayload>::Signature - >(); - let extra_ty = #scrate::__private::scale_info::meta_type::< - <<#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::SignaturePayload as #scrate::sp_runtime::traits::SignaturePayload>::SignatureExtra - >(); + let ty = #scrate::__private::scale_info::meta_type::<#extrinsic>(); + let address_ty = #scrate::__private::scale_info::meta_type::< + <<#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::SignaturePayload as #scrate::sp_runtime::traits::SignaturePayload>::SignatureAddress + >(); + let call_ty = #scrate::__private::scale_info::meta_type::< + <#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::Call + >(); + let signature_ty = #scrate::__private::scale_info::meta_type::< + <<#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::SignaturePayload as #scrate::sp_runtime::traits::SignaturePayload>::Signature + >(); + let extra_ty = #scrate::__private::scale_info::meta_type::< + <<#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::SignaturePayload as #scrate::sp_runtime::traits::SignaturePayload>::SignatureExtra + >(); - #scrate::__private::metadata_ir::MetadataIR { - pallets: #scrate::__private::sp_std::vec![ #(#pallets),* ], - extrinsic: #scrate::__private::metadata_ir::ExtrinsicMetadataIR { - ty, - version: <#extrinsic as #scrate::sp_runtime::traits::ExtrinsicMetadata>::VERSION, - address_ty, - call_ty, - signature_ty, - extra_ty, - signed_extensions: < - < - #extrinsic as #scrate::sp_runtime::traits::ExtrinsicMetadata - >::SignedExtensions as #scrate::sp_runtime::traits::SignedExtension - >::metadata() - .into_iter() - .map(|meta| #scrate::__private::metadata_ir::SignedExtensionMetadataIR { - identifier: meta.identifier, - ty: meta.ty, - additional_signed: meta.additional_signed, - }) - .collect(), - }, - ty: #scrate::__private::scale_info::meta_type::<#runtime>(), - apis: (&rt).runtime_metadata(), - outer_enums: #scrate::__private::metadata_ir::OuterEnumsIR { - call_enum_ty: #scrate::__private::scale_info::meta_type::< - <#runtime as #system_path::Config>::RuntimeCall - >(), - event_enum_ty: #scrate::__private::scale_info::meta_type::(), - error_enum_ty: #scrate::__private::scale_info::meta_type::(), - } - } - } + #scrate::__private::metadata_ir::MetadataIR { + pallets: #scrate::__private::sp_std::vec![ #(#pallets),* ], + extrinsic: #scrate::__private::metadata_ir::ExtrinsicMetadataIR { + ty, + version: <#extrinsic as #scrate::sp_runtime::traits::ExtrinsicMetadata>::VERSION, + address_ty, + call_ty, + signature_ty, + extra_ty, + signed_extensions: < + < + #extrinsic as #scrate::sp_runtime::traits::ExtrinsicMetadata + >::SignedExtensions as #scrate::sp_runtime::traits::SignedExtension + >::metadata() + .into_iter() + .map(|meta| #scrate::__private::metadata_ir::SignedExtensionMetadataIR { + identifier: meta.identifier, + ty: meta.ty, + additional_signed: meta.additional_signed, + }) + .collect(), + }, + ty: #scrate::__private::scale_info::meta_type::<#runtime>(), + apis: (&rt).runtime_metadata(), + outer_enums: #scrate::__private::metadata_ir::OuterEnumsIR { + call_enum_ty: #scrate::__private::scale_info::meta_type::< + <#runtime as #system_path::Config>::RuntimeCall + >(), + event_enum_ty: #scrate::__private::scale_info::meta_type::(), + error_enum_ty: #scrate::__private::scale_info::meta_type::(), + } + } + } - pub fn metadata() -> #scrate::__private::metadata::RuntimeMetadataPrefixed { - // Note: this always returns the V14 version. The runtime API function - // must be deprecated. - #scrate::__private::metadata_ir::into_v14(#runtime::metadata_ir()) - } + pub fn metadata() -> #scrate::__private::metadata::RuntimeMetadataPrefixed { + // Note: this always returns the V14 version. The runtime API function + // must be deprecated. + #scrate::__private::metadata_ir::into_v14(#runtime::metadata_ir()) + } - pub fn metadata_at_version(version: u32) -> Option<#scrate::__private::OpaqueMetadata> { - #scrate::__private::metadata_ir::into_version(#runtime::metadata_ir(), version).map(|prefixed| { - #scrate::__private::OpaqueMetadata::new(prefixed.into()) - }) - } + pub fn metadata_at_version(version: u32) -> Option<#scrate::__private::OpaqueMetadata> { + #scrate::__private::metadata_ir::into_version(#runtime::metadata_ir(), version).map(|prefixed| { + #scrate::__private::OpaqueMetadata::new(prefixed.into()) + }) + } - pub fn metadata_versions() -> #scrate::__private::sp_std::vec::Vec { - #scrate::__private::metadata_ir::supported_versions() - } - } - } + pub fn metadata_versions() -> #scrate::__private::sp_std::vec::Vec { + #scrate::__private::metadata_ir::supported_versions() + } + } + } } fn expand_pallet_metadata_storage( - filtered_names: &[&'static str], - runtime: &Ident, - decl: &Pallet, + filtered_names: &[&'static str], + runtime: &Ident, + decl: &Pallet, ) -> TokenStream { - if filtered_names.contains(&"Storage") { - let instance = decl.instance.as_ref().into_iter(); - let path = &decl.path; + if filtered_names.contains(&"Storage") { + let instance = decl.instance.as_ref().into_iter(); + let path = &decl.path; - quote! { - Some(#path::Pallet::<#runtime #(, #path::#instance)*>::storage_metadata()) - } - } else { - quote!(None) - } + quote! { + Some(#path::Pallet::<#runtime #(, #path::#instance)*>::storage_metadata()) + } + } else { + quote!(None) + } } fn expand_pallet_metadata_calls( - filtered_names: &[&'static str], - runtime: &Ident, - decl: &Pallet, + filtered_names: &[&'static str], + runtime: &Ident, + decl: &Pallet, ) -> TokenStream { - if filtered_names.contains(&"Call") { - let instance = decl.instance.as_ref().into_iter(); - let path = &decl.path; + if filtered_names.contains(&"Call") { + let instance = decl.instance.as_ref().into_iter(); + let path = &decl.path; - quote! { - Some(#path::Pallet::<#runtime #(, #path::#instance)*>::call_functions()) - } - } else { - quote!(None) - } + quote! { + Some(#path::Pallet::<#runtime #(, #path::#instance)*>::call_functions()) + } + } else { + quote!(None) + } } fn expand_pallet_metadata_events( - filtered_names: &[&'static str], - runtime: &Ident, - scrate: &TokenStream, - decl: &Pallet, + filtered_names: &[&'static str], + runtime: &Ident, + scrate: &TokenStream, + decl: &Pallet, ) -> TokenStream { - if filtered_names.contains(&"Event") { - let path = &decl.path; - let part_is_generic = !decl - .find_part("Event") - .expect("Event part exists; qed") - .generics - .params - .is_empty(); - let pallet_event = match (decl.instance.as_ref(), part_is_generic) { - (Some(inst), true) => quote!(#path::Event::<#runtime, #path::#inst>), - (Some(inst), false) => quote!(#path::Event::<#path::#inst>), - (None, true) => quote!(#path::Event::<#runtime>), - (None, false) => quote!(#path::Event), - }; + if filtered_names.contains(&"Event") { + let path = &decl.path; + let part_is_generic = !decl + .find_part("Event") + .expect("Event part exists; qed") + .generics + .params + .is_empty(); + let pallet_event = match (decl.instance.as_ref(), part_is_generic) { + (Some(inst), true) => quote!(#path::Event::<#runtime, #path::#inst>), + (Some(inst), false) => quote!(#path::Event::<#path::#inst>), + (None, true) => quote!(#path::Event::<#runtime>), + (None, false) => quote!(#path::Event), + }; - quote! { - Some( - #scrate::__private::metadata_ir::PalletEventMetadataIR { - ty: #scrate::__private::scale_info::meta_type::<#pallet_event>() - } - ) - } - } else { - quote!(None) - } + quote! { + Some( + #scrate::__private::metadata_ir::PalletEventMetadataIR { + ty: #scrate::__private::scale_info::meta_type::<#pallet_event>() + } + ) + } + } else { + quote!(None) + } } fn expand_pallet_metadata_constants(runtime: &Ident, decl: &Pallet) -> TokenStream { - let path = &decl.path; - let instance = decl.instance.as_ref().into_iter(); + let path = &decl.path; + let instance = decl.instance.as_ref().into_iter(); - quote! { - #path::Pallet::<#runtime #(, #path::#instance)*>::pallet_constants_metadata() - } + quote! { + #path::Pallet::<#runtime #(, #path::#instance)*>::pallet_constants_metadata() + } } fn expand_pallet_metadata_errors(runtime: &Ident, decl: &Pallet) -> TokenStream { - let path = &decl.path; - let instance = decl.instance.as_ref().into_iter(); + let path = &decl.path; + let instance = decl.instance.as_ref().into_iter(); - quote! { - #path::Pallet::<#runtime #(, #path::#instance)*>::error_metadata() - } + quote! { + #path::Pallet::<#runtime #(, #path::#instance)*>::error_metadata() + } } fn expand_pallet_metadata_docs(runtime: &Ident, decl: &Pallet) -> TokenStream { - let path = &decl.path; - let instance = decl.instance.as_ref().into_iter(); + let path = &decl.path; + let instance = decl.instance.as_ref().into_iter(); - quote! { - #path::Pallet::<#runtime #(, #path::#instance)*>::pallet_documentation_metadata() - } + quote! { + #path::Pallet::<#runtime #(, #path::#instance)*>::pallet_documentation_metadata() + } } diff --git a/support/procedural-fork/src/construct_runtime/expand/origin.rs b/support/procedural-fork/src/construct_runtime/expand/origin.rs index 83049919d..2d50777bf 100644 --- a/support/procedural-fork/src/construct_runtime/expand/origin.rs +++ b/support/procedural-fork/src/construct_runtime/expand/origin.rs @@ -22,434 +22,448 @@ use std::str::FromStr; use syn::{Generics, Ident}; pub fn expand_outer_origin( - runtime: &Ident, - system_pallet: &Pallet, - pallets: &[Pallet], - scrate: &TokenStream, + runtime: &Ident, + system_pallet: &Pallet, + pallets: &[Pallet], + scrate: &TokenStream, ) -> syn::Result { - let mut caller_variants = TokenStream::new(); - let mut pallet_conversions = TokenStream::new(); - let mut query_origin_part_macros = Vec::new(); - - for pallet_decl in pallets.iter().filter(|pallet| pallet.name != SYSTEM_PALLET_NAME) { - if let Some(pallet_entry) = pallet_decl.find_part("Origin") { - let instance = pallet_decl.instance.as_ref(); - let index = pallet_decl.index; - let generics = &pallet_entry.generics; - let name = &pallet_decl.name; - let path = &pallet_decl.path; - - if instance.is_some() && generics.params.is_empty() { - let msg = format!( - "Instantiable pallet with no generic `Origin` cannot \ + let mut caller_variants = TokenStream::new(); + let mut pallet_conversions = TokenStream::new(); + let mut query_origin_part_macros = Vec::new(); + + for pallet_decl in pallets + .iter() + .filter(|pallet| pallet.name != SYSTEM_PALLET_NAME) + { + if let Some(pallet_entry) = pallet_decl.find_part("Origin") { + let instance = pallet_decl.instance.as_ref(); + let index = pallet_decl.index; + let generics = &pallet_entry.generics; + let name = &pallet_decl.name; + let path = &pallet_decl.path; + + if instance.is_some() && generics.params.is_empty() { + let msg = format!( + "Instantiable pallet with no generic `Origin` cannot \ be constructed: pallet `{}` must have generic `Origin`", - name - ); - return Err(syn::Error::new(name.span(), msg)) - } - - caller_variants.extend(expand_origin_caller_variant( - runtime, - pallet_decl, - index, - instance, - generics, - )); - pallet_conversions.extend(expand_origin_pallet_conversions( - scrate, - runtime, - pallet_decl, - instance, - generics, - )); - query_origin_part_macros.push(quote! { - #path::__substrate_origin_check::is_origin_part_defined!(#name); - }); - } - } - - let system_path = &system_pallet.path; - - let system_index = system_pallet.index; - - let system_path_name = system_path.module_name(); - - let doc_string = get_intra_doc_string( - "Origin is always created with the base filter configured in", - &system_path_name, - ); - - let doc_string_none_origin = - get_intra_doc_string("Create with system none origin and", &system_path_name); - - let doc_string_root_origin = - get_intra_doc_string("Create with system root origin and", &system_path_name); - - let doc_string_signed_origin = - get_intra_doc_string("Create with system signed origin and", &system_path_name); - - let doc_string_runtime_origin = - get_intra_doc_string("Convert to runtime origin, using as filter:", &system_path_name); - - let doc_string_runtime_origin_with_caller = get_intra_doc_string( - "Convert to runtime origin with caller being system signed or none and use filter", - &system_path_name, - ); - - Ok(quote! { - #( #query_origin_part_macros )* - - /// The runtime origin type representing the origin of a call. - /// - #[doc = #doc_string] - #[derive(Clone)] - pub struct RuntimeOrigin { - pub caller: OriginCaller, - filter: #scrate::__private::sp_std::rc::Rc::RuntimeCall) -> bool>>, - } - - #[cfg(not(feature = "std"))] - impl #scrate::__private::sp_std::fmt::Debug for RuntimeOrigin { - fn fmt( - &self, - fmt: &mut #scrate::__private::sp_std::fmt::Formatter, - ) -> #scrate::__private::sp_std::result::Result<(), #scrate::__private::sp_std::fmt::Error> { - fmt.write_str("") - } - } - - #[cfg(feature = "std")] - impl #scrate::__private::sp_std::fmt::Debug for RuntimeOrigin { - fn fmt( - &self, - fmt: &mut #scrate::__private::sp_std::fmt::Formatter, - ) -> #scrate::__private::sp_std::result::Result<(), #scrate::__private::sp_std::fmt::Error> { - fmt.debug_struct("Origin") - .field("caller", &self.caller) - .field("filter", &"[function ptr]") - .finish() - } - } - - impl #scrate::traits::OriginTrait for RuntimeOrigin { - type Call = <#runtime as #system_path::Config>::RuntimeCall; - type PalletsOrigin = OriginCaller; - type AccountId = <#runtime as #system_path::Config>::AccountId; - - fn add_filter(&mut self, filter: impl Fn(&Self::Call) -> bool + 'static) { - let f = self.filter.clone(); - - self.filter = #scrate::__private::sp_std::rc::Rc::new(Box::new(move |call| { - f(call) && filter(call) - })); - } - - fn reset_filter(&mut self) { - let filter = < - <#runtime as #system_path::Config>::BaseCallFilter - as #scrate::traits::Contains<<#runtime as #system_path::Config>::RuntimeCall> - >::contains; - - self.filter = #scrate::__private::sp_std::rc::Rc::new(Box::new(filter)); - } - - fn set_caller_from(&mut self, other: impl Into) { - self.caller = other.into().caller; - } - - fn filter_call(&self, call: &Self::Call) -> bool { - match self.caller { - // Root bypasses all filters - OriginCaller::system(#system_path::Origin::<#runtime>::Root) => true, - _ => (self.filter)(call), - } - } - - fn caller(&self) -> &Self::PalletsOrigin { - &self.caller - } - - fn into_caller(self) -> Self::PalletsOrigin { - self.caller - } - - fn try_with_caller( - mut self, - f: impl FnOnce(Self::PalletsOrigin) -> Result, - ) -> Result { - match f(self.caller) { - Ok(r) => Ok(r), - Err(caller) => { self.caller = caller; Err(self) } - } - } - - fn none() -> Self { - #system_path::RawOrigin::None.into() - } - - fn root() -> Self { - #system_path::RawOrigin::Root.into() - } - - fn signed(by: Self::AccountId) -> Self { - #system_path::RawOrigin::Signed(by).into() - } - } - - #[derive( - Clone, PartialEq, Eq, #scrate::__private::RuntimeDebug, #scrate::__private::codec::Encode, - #scrate::__private::codec::Decode, #scrate::__private::scale_info::TypeInfo, #scrate::__private::codec::MaxEncodedLen, - )] - #[allow(non_camel_case_types)] - pub enum OriginCaller { - #[codec(index = #system_index)] - system(#system_path::Origin<#runtime>), - #caller_variants - #[allow(dead_code)] - Void(#scrate::__private::Void) - } - - // For backwards compatibility and ease of accessing these functions. - #[allow(dead_code)] - impl RuntimeOrigin { - #[doc = #doc_string_none_origin] - pub fn none() -> Self { - ::none() - } - - #[doc = #doc_string_root_origin] - pub fn root() -> Self { - ::root() - } - - #[doc = #doc_string_signed_origin] - pub fn signed(by: <#runtime as #system_path::Config>::AccountId) -> Self { - ::signed(by) - } - } - - impl From<#system_path::Origin<#runtime>> for OriginCaller { - fn from(x: #system_path::Origin<#runtime>) -> Self { - OriginCaller::system(x) - } - } - - impl #scrate::traits::CallerTrait<<#runtime as #system_path::Config>::AccountId> for OriginCaller { - fn into_system(self) -> Option<#system_path::RawOrigin<<#runtime as #system_path::Config>::AccountId>> { - match self { - OriginCaller::system(x) => Some(x), - _ => None, - } - } - fn as_system_ref(&self) -> Option<&#system_path::RawOrigin<<#runtime as #system_path::Config>::AccountId>> { - match &self { - OriginCaller::system(o) => Some(o), - _ => None, - } - } - } - - impl TryFrom for #system_path::Origin<#runtime> { - type Error = OriginCaller; - fn try_from(x: OriginCaller) - -> #scrate::__private::sp_std::result::Result<#system_path::Origin<#runtime>, OriginCaller> - { - if let OriginCaller::system(l) = x { - Ok(l) - } else { - Err(x) - } - } - } - - impl From<#system_path::Origin<#runtime>> for RuntimeOrigin { - - #[doc = #doc_string_runtime_origin] - fn from(x: #system_path::Origin<#runtime>) -> Self { - let o: OriginCaller = x.into(); - o.into() - } - } - - impl From for RuntimeOrigin { - fn from(x: OriginCaller) -> Self { - let mut o = RuntimeOrigin { - caller: x, - filter: #scrate::__private::sp_std::rc::Rc::new(Box::new(|_| true)), - }; - - #scrate::traits::OriginTrait::reset_filter(&mut o); - - o - } - } - - impl From for #scrate::__private::sp_std::result::Result<#system_path::Origin<#runtime>, RuntimeOrigin> { - /// NOTE: converting to pallet origin loses the origin filter information. - fn from(val: RuntimeOrigin) -> Self { - if let OriginCaller::system(l) = val.caller { - Ok(l) - } else { - Err(val) - } - } - } - impl From::AccountId>> for RuntimeOrigin { - #[doc = #doc_string_runtime_origin_with_caller] - fn from(x: Option<<#runtime as #system_path::Config>::AccountId>) -> Self { - <#system_path::Origin<#runtime>>::from(x).into() - } - } - - #pallet_conversions - }) + name + ); + return Err(syn::Error::new(name.span(), msg)); + } + + caller_variants.extend(expand_origin_caller_variant( + runtime, + pallet_decl, + index, + instance, + generics, + )); + pallet_conversions.extend(expand_origin_pallet_conversions( + scrate, + runtime, + pallet_decl, + instance, + generics, + )); + query_origin_part_macros.push(quote! { + #path::__substrate_origin_check::is_origin_part_defined!(#name); + }); + } + } + + let system_path = &system_pallet.path; + + let system_index = system_pallet.index; + + let system_path_name = system_path.module_name(); + + let doc_string = get_intra_doc_string( + "Origin is always created with the base filter configured in", + &system_path_name, + ); + + let doc_string_none_origin = + get_intra_doc_string("Create with system none origin and", &system_path_name); + + let doc_string_root_origin = + get_intra_doc_string("Create with system root origin and", &system_path_name); + + let doc_string_signed_origin = + get_intra_doc_string("Create with system signed origin and", &system_path_name); + + let doc_string_runtime_origin = get_intra_doc_string( + "Convert to runtime origin, using as filter:", + &system_path_name, + ); + + let doc_string_runtime_origin_with_caller = get_intra_doc_string( + "Convert to runtime origin with caller being system signed or none and use filter", + &system_path_name, + ); + + Ok(quote! { + #( #query_origin_part_macros )* + + /// The runtime origin type representing the origin of a call. + /// + #[doc = #doc_string] + #[derive(Clone)] + pub struct RuntimeOrigin { + pub caller: OriginCaller, + filter: #scrate::__private::sp_std::rc::Rc::RuntimeCall) -> bool>>, + } + + #[cfg(not(feature = "std"))] + impl #scrate::__private::sp_std::fmt::Debug for RuntimeOrigin { + fn fmt( + &self, + fmt: &mut #scrate::__private::sp_std::fmt::Formatter, + ) -> #scrate::__private::sp_std::result::Result<(), #scrate::__private::sp_std::fmt::Error> { + fmt.write_str("") + } + } + + #[cfg(feature = "std")] + impl #scrate::__private::sp_std::fmt::Debug for RuntimeOrigin { + fn fmt( + &self, + fmt: &mut #scrate::__private::sp_std::fmt::Formatter, + ) -> #scrate::__private::sp_std::result::Result<(), #scrate::__private::sp_std::fmt::Error> { + fmt.debug_struct("Origin") + .field("caller", &self.caller) + .field("filter", &"[function ptr]") + .finish() + } + } + + impl #scrate::traits::OriginTrait for RuntimeOrigin { + type Call = <#runtime as #system_path::Config>::RuntimeCall; + type PalletsOrigin = OriginCaller; + type AccountId = <#runtime as #system_path::Config>::AccountId; + + fn add_filter(&mut self, filter: impl Fn(&Self::Call) -> bool + 'static) { + let f = self.filter.clone(); + + self.filter = #scrate::__private::sp_std::rc::Rc::new(Box::new(move |call| { + f(call) && filter(call) + })); + } + + fn reset_filter(&mut self) { + let filter = < + <#runtime as #system_path::Config>::BaseCallFilter + as #scrate::traits::Contains<<#runtime as #system_path::Config>::RuntimeCall> + >::contains; + + self.filter = #scrate::__private::sp_std::rc::Rc::new(Box::new(filter)); + } + + fn set_caller_from(&mut self, other: impl Into) { + self.caller = other.into().caller; + } + + fn filter_call(&self, call: &Self::Call) -> bool { + match self.caller { + // Root bypasses all filters + OriginCaller::system(#system_path::Origin::<#runtime>::Root) => true, + _ => (self.filter)(call), + } + } + + fn caller(&self) -> &Self::PalletsOrigin { + &self.caller + } + + fn into_caller(self) -> Self::PalletsOrigin { + self.caller + } + + fn try_with_caller( + mut self, + f: impl FnOnce(Self::PalletsOrigin) -> Result, + ) -> Result { + match f(self.caller) { + Ok(r) => Ok(r), + Err(caller) => { self.caller = caller; Err(self) } + } + } + + fn none() -> Self { + #system_path::RawOrigin::None.into() + } + + fn root() -> Self { + #system_path::RawOrigin::Root.into() + } + + fn signed(by: Self::AccountId) -> Self { + #system_path::RawOrigin::Signed(by).into() + } + } + + #[derive( + Clone, PartialEq, Eq, #scrate::__private::RuntimeDebug, #scrate::__private::codec::Encode, + #scrate::__private::codec::Decode, #scrate::__private::scale_info::TypeInfo, #scrate::__private::codec::MaxEncodedLen, + )] + #[allow(non_camel_case_types)] + pub enum OriginCaller { + #[codec(index = #system_index)] + system(#system_path::Origin<#runtime>), + #caller_variants + #[allow(dead_code)] + Void(#scrate::__private::Void) + } + + // For backwards compatibility and ease of accessing these functions. + #[allow(dead_code)] + impl RuntimeOrigin { + #[doc = #doc_string_none_origin] + pub fn none() -> Self { + ::none() + } + + #[doc = #doc_string_root_origin] + pub fn root() -> Self { + ::root() + } + + #[doc = #doc_string_signed_origin] + pub fn signed(by: <#runtime as #system_path::Config>::AccountId) -> Self { + ::signed(by) + } + } + + impl From<#system_path::Origin<#runtime>> for OriginCaller { + fn from(x: #system_path::Origin<#runtime>) -> Self { + OriginCaller::system(x) + } + } + + impl #scrate::traits::CallerTrait<<#runtime as #system_path::Config>::AccountId> for OriginCaller { + fn into_system(self) -> Option<#system_path::RawOrigin<<#runtime as #system_path::Config>::AccountId>> { + match self { + OriginCaller::system(x) => Some(x), + _ => None, + } + } + fn as_system_ref(&self) -> Option<&#system_path::RawOrigin<<#runtime as #system_path::Config>::AccountId>> { + match &self { + OriginCaller::system(o) => Some(o), + _ => None, + } + } + } + + impl TryFrom for #system_path::Origin<#runtime> { + type Error = OriginCaller; + fn try_from(x: OriginCaller) + -> #scrate::__private::sp_std::result::Result<#system_path::Origin<#runtime>, OriginCaller> + { + if let OriginCaller::system(l) = x { + Ok(l) + } else { + Err(x) + } + } + } + + impl From<#system_path::Origin<#runtime>> for RuntimeOrigin { + + #[doc = #doc_string_runtime_origin] + fn from(x: #system_path::Origin<#runtime>) -> Self { + let o: OriginCaller = x.into(); + o.into() + } + } + + impl From for RuntimeOrigin { + fn from(x: OriginCaller) -> Self { + let mut o = RuntimeOrigin { + caller: x, + filter: #scrate::__private::sp_std::rc::Rc::new(Box::new(|_| true)), + }; + + #scrate::traits::OriginTrait::reset_filter(&mut o); + + o + } + } + + impl From for #scrate::__private::sp_std::result::Result<#system_path::Origin<#runtime>, RuntimeOrigin> { + /// NOTE: converting to pallet origin loses the origin filter information. + fn from(val: RuntimeOrigin) -> Self { + if let OriginCaller::system(l) = val.caller { + Ok(l) + } else { + Err(val) + } + } + } + impl From::AccountId>> for RuntimeOrigin { + #[doc = #doc_string_runtime_origin_with_caller] + fn from(x: Option<<#runtime as #system_path::Config>::AccountId>) -> Self { + <#system_path::Origin<#runtime>>::from(x).into() + } + } + + #pallet_conversions + }) } fn expand_origin_caller_variant( - runtime: &Ident, - pallet: &Pallet, - index: u8, - instance: Option<&Ident>, - generics: &Generics, + runtime: &Ident, + pallet: &Pallet, + index: u8, + instance: Option<&Ident>, + generics: &Generics, ) -> TokenStream { - let part_is_generic = !generics.params.is_empty(); - let variant_name = &pallet.name; - let path = &pallet.path; - let attr = pallet.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { - let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) - .expect("was successfully parsed before; qed"); - quote! { - #acc - #attr - } - }); - - match instance { - Some(inst) if part_is_generic => quote! { - #attr - #[codec(index = #index)] - #variant_name(#path::Origin<#runtime, #path::#inst>), - }, - Some(inst) => quote! { - #attr - #[codec(index = #index)] - #variant_name(#path::Origin<#path::#inst>), - }, - None if part_is_generic => quote! { - #attr - #[codec(index = #index)] - #variant_name(#path::Origin<#runtime>), - }, - None => quote! { - #attr - #[codec(index = #index)] - #variant_name(#path::Origin), - }, - } + let part_is_generic = !generics.params.is_empty(); + let variant_name = &pallet.name; + let path = &pallet.path; + let attr = pallet + .cfg_pattern + .iter() + .fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); + + match instance { + Some(inst) if part_is_generic => quote! { + #attr + #[codec(index = #index)] + #variant_name(#path::Origin<#runtime, #path::#inst>), + }, + Some(inst) => quote! { + #attr + #[codec(index = #index)] + #variant_name(#path::Origin<#path::#inst>), + }, + None if part_is_generic => quote! { + #attr + #[codec(index = #index)] + #variant_name(#path::Origin<#runtime>), + }, + None => quote! { + #attr + #[codec(index = #index)] + #variant_name(#path::Origin), + }, + } } fn expand_origin_pallet_conversions( - scrate: &TokenStream, - runtime: &Ident, - pallet: &Pallet, - instance: Option<&Ident>, - generics: &Generics, + scrate: &TokenStream, + runtime: &Ident, + pallet: &Pallet, + instance: Option<&Ident>, + generics: &Generics, ) -> TokenStream { - let path = &pallet.path; - let variant_name = &pallet.name; - - let part_is_generic = !generics.params.is_empty(); - let pallet_origin = match instance { - Some(inst) if part_is_generic => quote!(#path::Origin<#runtime, #path::#inst>), - Some(inst) => quote!(#path::Origin<#path::#inst>), - None if part_is_generic => quote!(#path::Origin<#runtime>), - None => quote!(#path::Origin), - }; - - let doc_string = get_intra_doc_string(" Convert to runtime origin using", &path.module_name()); - let attr = pallet.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { - let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) - .expect("was successfully parsed before; qed"); - quote! { - #acc - #attr - } - }); - - quote! { - #attr - impl From<#pallet_origin> for OriginCaller { - fn from(x: #pallet_origin) -> Self { - OriginCaller::#variant_name(x) - } - } - - #attr - impl From<#pallet_origin> for RuntimeOrigin { - #[doc = #doc_string] - fn from(x: #pallet_origin) -> Self { - let x: OriginCaller = x.into(); - x.into() - } - } - - #attr - impl From for #scrate::__private::sp_std::result::Result<#pallet_origin, RuntimeOrigin> { - /// NOTE: converting to pallet origin loses the origin filter information. - fn from(val: RuntimeOrigin) -> Self { - if let OriginCaller::#variant_name(l) = val.caller { - Ok(l) - } else { - Err(val) - } - } - } - - #attr - impl TryFrom for #pallet_origin { - type Error = OriginCaller; - fn try_from( - x: OriginCaller, - ) -> #scrate::__private::sp_std::result::Result<#pallet_origin, OriginCaller> { - if let OriginCaller::#variant_name(l) = x { - Ok(l) - } else { - Err(x) - } - } - } - - #attr - impl<'a> TryFrom<&'a OriginCaller> for &'a #pallet_origin { - type Error = (); - fn try_from( - x: &'a OriginCaller, - ) -> #scrate::__private::sp_std::result::Result<&'a #pallet_origin, ()> { - if let OriginCaller::#variant_name(l) = x { - Ok(&l) - } else { - Err(()) - } - } - } - - #attr - impl<'a> TryFrom<&'a RuntimeOrigin> for &'a #pallet_origin { - type Error = (); - fn try_from( - x: &'a RuntimeOrigin, - ) -> #scrate::__private::sp_std::result::Result<&'a #pallet_origin, ()> { - if let OriginCaller::#variant_name(l) = &x.caller { - Ok(&l) - } else { - Err(()) - } - } - } - } + let path = &pallet.path; + let variant_name = &pallet.name; + + let part_is_generic = !generics.params.is_empty(); + let pallet_origin = match instance { + Some(inst) if part_is_generic => quote!(#path::Origin<#runtime, #path::#inst>), + Some(inst) => quote!(#path::Origin<#path::#inst>), + None if part_is_generic => quote!(#path::Origin<#runtime>), + None => quote!(#path::Origin), + }; + + let doc_string = get_intra_doc_string(" Convert to runtime origin using", &path.module_name()); + let attr = pallet + .cfg_pattern + .iter() + .fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); + + quote! { + #attr + impl From<#pallet_origin> for OriginCaller { + fn from(x: #pallet_origin) -> Self { + OriginCaller::#variant_name(x) + } + } + + #attr + impl From<#pallet_origin> for RuntimeOrigin { + #[doc = #doc_string] + fn from(x: #pallet_origin) -> Self { + let x: OriginCaller = x.into(); + x.into() + } + } + + #attr + impl From for #scrate::__private::sp_std::result::Result<#pallet_origin, RuntimeOrigin> { + /// NOTE: converting to pallet origin loses the origin filter information. + fn from(val: RuntimeOrigin) -> Self { + if let OriginCaller::#variant_name(l) = val.caller { + Ok(l) + } else { + Err(val) + } + } + } + + #attr + impl TryFrom for #pallet_origin { + type Error = OriginCaller; + fn try_from( + x: OriginCaller, + ) -> #scrate::__private::sp_std::result::Result<#pallet_origin, OriginCaller> { + if let OriginCaller::#variant_name(l) = x { + Ok(l) + } else { + Err(x) + } + } + } + + #attr + impl<'a> TryFrom<&'a OriginCaller> for &'a #pallet_origin { + type Error = (); + fn try_from( + x: &'a OriginCaller, + ) -> #scrate::__private::sp_std::result::Result<&'a #pallet_origin, ()> { + if let OriginCaller::#variant_name(l) = x { + Ok(&l) + } else { + Err(()) + } + } + } + + #attr + impl<'a> TryFrom<&'a RuntimeOrigin> for &'a #pallet_origin { + type Error = (); + fn try_from( + x: &'a RuntimeOrigin, + ) -> #scrate::__private::sp_std::result::Result<&'a #pallet_origin, ()> { + if let OriginCaller::#variant_name(l) = &x.caller { + Ok(&l) + } else { + Err(()) + } + } + } + } } // Get the actual documentation using the doc information and system path name fn get_intra_doc_string(doc_info: &str, system_path_name: &String) -> String { - format!(" {} [`{}::Config::BaseCallFilter`].", doc_info, system_path_name) + format!( + " {} [`{}::Config::BaseCallFilter`].", + doc_info, system_path_name + ) } diff --git a/support/procedural-fork/src/construct_runtime/expand/outer_enums.rs b/support/procedural-fork/src/construct_runtime/expand/outer_enums.rs index 80b242ccb..28e39c7a2 100644 --- a/support/procedural-fork/src/construct_runtime/expand/outer_enums.rs +++ b/support/procedural-fork/src/construct_runtime/expand/outer_enums.rs @@ -24,37 +24,37 @@ use syn::{Generics, Ident}; /// Represents the types supported for creating an outer enum. #[derive(Clone, Copy, PartialEq)] pub enum OuterEnumType { - /// Collects the Event enums from all pallets. - Event, - /// Collects the Error enums from all pallets. - Error, + /// Collects the Event enums from all pallets. + Event, + /// Collects the Error enums from all pallets. + Error, } impl OuterEnumType { - /// The name of the structure this enum represents. - fn struct_name(&self) -> &str { - match self { - OuterEnumType::Event => "RuntimeEvent", - OuterEnumType::Error => "RuntimeError", - } - } + /// The name of the structure this enum represents. + fn struct_name(&self) -> &str { + match self { + OuterEnumType::Event => "RuntimeEvent", + OuterEnumType::Error => "RuntimeError", + } + } - /// The name of the variant (ie `Event` or `Error`). - fn variant_name(&self) -> &str { - match self { - OuterEnumType::Event => "Event", - OuterEnumType::Error => "Error", - } - } + /// The name of the variant (ie `Event` or `Error`). + fn variant_name(&self) -> &str { + match self { + OuterEnumType::Event => "Event", + OuterEnumType::Error => "Error", + } + } } impl ToTokens for OuterEnumType { - fn to_tokens(&self, tokens: &mut TokenStream) { - match self { - OuterEnumType::Event => quote!(Event).to_tokens(tokens), - OuterEnumType::Error => quote!(Error).to_tokens(tokens), - } - } + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + OuterEnumType::Event => quote!(Event).to_tokens(tokens), + OuterEnumType::Error => quote!(Error).to_tokens(tokens), + } + } } /// Create an outer enum that encapsulates all pallets as variants. @@ -84,196 +84,207 @@ impl ToTokens for OuterEnumType { /// /// Notice that the pallet index is preserved using the `#[codec(index = ..)]` attribute. pub fn expand_outer_enum( - runtime: &Ident, - pallet_decls: &[Pallet], - scrate: &TokenStream, - enum_ty: OuterEnumType, + runtime: &Ident, + pallet_decls: &[Pallet], + scrate: &TokenStream, + enum_ty: OuterEnumType, ) -> syn::Result { - // Stores all pallet variants. - let mut enum_variants = TokenStream::new(); - // Generates the enum conversion between the `Runtime` outer enum and the pallet's enum. - let mut enum_conversions = TokenStream::new(); - // Specific for events to query via `is_event_part_defined!`. - let mut query_enum_part_macros = Vec::new(); + // Stores all pallet variants. + let mut enum_variants = TokenStream::new(); + // Generates the enum conversion between the `Runtime` outer enum and the pallet's enum. + let mut enum_conversions = TokenStream::new(); + // Specific for events to query via `is_event_part_defined!`. + let mut query_enum_part_macros = Vec::new(); - let enum_name_str = enum_ty.variant_name(); - let enum_name_ident = Ident::new(enum_ty.struct_name(), Span::call_site()); + let enum_name_str = enum_ty.variant_name(); + let enum_name_ident = Ident::new(enum_ty.struct_name(), Span::call_site()); - for pallet_decl in pallet_decls { - let Some(pallet_entry) = pallet_decl.find_part(enum_name_str) else { continue }; + for pallet_decl in pallet_decls { + let Some(pallet_entry) = pallet_decl.find_part(enum_name_str) else { + continue; + }; - let path = &pallet_decl.path; - let pallet_name = &pallet_decl.name; - let index = pallet_decl.index; - let instance = pallet_decl.instance.as_ref(); - let generics = &pallet_entry.generics; + let path = &pallet_decl.path; + let pallet_name = &pallet_decl.name; + let index = pallet_decl.index; + let instance = pallet_decl.instance.as_ref(); + let generics = &pallet_entry.generics; - if instance.is_some() && generics.params.is_empty() { - let msg = format!( - "Instantiable pallet with no generic `{}` cannot \ + if instance.is_some() && generics.params.is_empty() { + let msg = format!( + "Instantiable pallet with no generic `{}` cannot \ be constructed: pallet `{}` must have generic `{}`", - enum_name_str, pallet_name, enum_name_str, - ); - return Err(syn::Error::new(pallet_name.span(), msg)) - } + enum_name_str, pallet_name, enum_name_str, + ); + return Err(syn::Error::new(pallet_name.span(), msg)); + } - let part_is_generic = !generics.params.is_empty(); - let pallet_enum = match (instance, part_is_generic) { - (Some(inst), true) => quote!(#path::#enum_ty::<#runtime, #path::#inst>), - (Some(inst), false) => quote!(#path::#enum_ty::<#path::#inst>), - (None, true) => quote!(#path::#enum_ty::<#runtime>), - (None, false) => quote!(#path::#enum_ty), - }; + let part_is_generic = !generics.params.is_empty(); + let pallet_enum = match (instance, part_is_generic) { + (Some(inst), true) => quote!(#path::#enum_ty::<#runtime, #path::#inst>), + (Some(inst), false) => quote!(#path::#enum_ty::<#path::#inst>), + (None, true) => quote!(#path::#enum_ty::<#runtime>), + (None, false) => quote!(#path::#enum_ty), + }; - enum_variants.extend(expand_enum_variant( - runtime, - pallet_decl, - index, - instance, - generics, - enum_ty, - )); - enum_conversions.extend(expand_enum_conversion( - pallet_decl, - &pallet_enum, - &enum_name_ident, - )); + enum_variants.extend(expand_enum_variant( + runtime, + pallet_decl, + index, + instance, + generics, + enum_ty, + )); + enum_conversions.extend(expand_enum_conversion( + pallet_decl, + &pallet_enum, + &enum_name_ident, + )); - if enum_ty == OuterEnumType::Event { - query_enum_part_macros.push(quote! { - #path::__substrate_event_check::is_event_part_defined!(#pallet_name); - }); - } - } + if enum_ty == OuterEnumType::Event { + query_enum_part_macros.push(quote! { + #path::__substrate_event_check::is_event_part_defined!(#pallet_name); + }); + } + } - // Derives specific for the event. - let event_custom_derives = - if enum_ty == OuterEnumType::Event { quote!(Clone, PartialEq, Eq,) } else { quote!() }; + // Derives specific for the event. + let event_custom_derives = if enum_ty == OuterEnumType::Event { + quote!(Clone, PartialEq, Eq,) + } else { + quote!() + }; - // Implementation specific for errors. - let error_custom_impl = generate_error_impl(scrate, enum_ty); + // Implementation specific for errors. + let error_custom_impl = generate_error_impl(scrate, enum_ty); - Ok(quote! { - #( #query_enum_part_macros )* + Ok(quote! { + #( #query_enum_part_macros )* - #[derive( - #event_custom_derives - #scrate::__private::codec::Encode, - #scrate::__private::codec::Decode, - #scrate::__private::scale_info::TypeInfo, - #scrate::__private::RuntimeDebug, - )] - #[allow(non_camel_case_types)] - pub enum #enum_name_ident { - #enum_variants - } + #[derive( + #event_custom_derives + #scrate::__private::codec::Encode, + #scrate::__private::codec::Decode, + #scrate::__private::scale_info::TypeInfo, + #scrate::__private::RuntimeDebug, + )] + #[allow(non_camel_case_types)] + pub enum #enum_name_ident { + #enum_variants + } - #enum_conversions + #enum_conversions - #error_custom_impl - }) + #error_custom_impl + }) } fn expand_enum_variant( - runtime: &Ident, - pallet: &Pallet, - index: u8, - instance: Option<&Ident>, - generics: &Generics, - enum_ty: OuterEnumType, + runtime: &Ident, + pallet: &Pallet, + index: u8, + instance: Option<&Ident>, + generics: &Generics, + enum_ty: OuterEnumType, ) -> TokenStream { - let path = &pallet.path; - let variant_name = &pallet.name; - let part_is_generic = !generics.params.is_empty(); - let attr = pallet.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { - let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) - .expect("was successfully parsed before; qed"); - quote! { - #acc - #attr - } - }); + let path = &pallet.path; + let variant_name = &pallet.name; + let part_is_generic = !generics.params.is_empty(); + let attr = pallet + .cfg_pattern + .iter() + .fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); - match instance { - Some(inst) if part_is_generic => quote! { - #attr - #[codec(index = #index)] - #variant_name(#path::#enum_ty<#runtime, #path::#inst>), - }, - Some(inst) => quote! { - #attr - #[codec(index = #index)] - #variant_name(#path::#enum_ty<#path::#inst>), - }, - None if part_is_generic => quote! { - #attr - #[codec(index = #index)] - #variant_name(#path::#enum_ty<#runtime>), - }, - None => quote! { - #attr - #[codec(index = #index)] - #variant_name(#path::#enum_ty), - }, - } + match instance { + Some(inst) if part_is_generic => quote! { + #attr + #[codec(index = #index)] + #variant_name(#path::#enum_ty<#runtime, #path::#inst>), + }, + Some(inst) => quote! { + #attr + #[codec(index = #index)] + #variant_name(#path::#enum_ty<#path::#inst>), + }, + None if part_is_generic => quote! { + #attr + #[codec(index = #index)] + #variant_name(#path::#enum_ty<#runtime>), + }, + None => quote! { + #attr + #[codec(index = #index)] + #variant_name(#path::#enum_ty), + }, + } } fn expand_enum_conversion( - pallet: &Pallet, - pallet_enum: &TokenStream, - enum_name_ident: &Ident, + pallet: &Pallet, + pallet_enum: &TokenStream, + enum_name_ident: &Ident, ) -> TokenStream { - let variant_name = &pallet.name; - let attr = pallet.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { - let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) - .expect("was successfully parsed before; qed"); - quote! { - #acc - #attr - } - }); + let variant_name = &pallet.name; + let attr = pallet + .cfg_pattern + .iter() + .fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); - quote! { - #attr - impl From<#pallet_enum> for #enum_name_ident { - fn from(x: #pallet_enum) -> Self { - #enum_name_ident - ::#variant_name(x) - } - } - #attr - impl TryInto<#pallet_enum> for #enum_name_ident { - type Error = (); + quote! { + #attr + impl From<#pallet_enum> for #enum_name_ident { + fn from(x: #pallet_enum) -> Self { + #enum_name_ident + ::#variant_name(x) + } + } + #attr + impl TryInto<#pallet_enum> for #enum_name_ident { + type Error = (); - fn try_into(self) -> ::core::result::Result<#pallet_enum, Self::Error> { - match self { - Self::#variant_name(evt) => Ok(evt), - _ => Err(()), - } - } - } - } + fn try_into(self) -> ::core::result::Result<#pallet_enum, Self::Error> { + match self { + Self::#variant_name(evt) => Ok(evt), + _ => Err(()), + } + } + } + } } fn generate_error_impl(scrate: &TokenStream, enum_ty: OuterEnumType) -> TokenStream { - // Implementation is specific to `Error`s. - if enum_ty == OuterEnumType::Event { - return quote! {} - } + // Implementation is specific to `Error`s. + if enum_ty == OuterEnumType::Event { + return quote! {}; + } - let enum_name_ident = Ident::new(enum_ty.struct_name(), Span::call_site()); + let enum_name_ident = Ident::new(enum_ty.struct_name(), Span::call_site()); - quote! { - impl #enum_name_ident { - /// Optionally convert the `DispatchError` into the `RuntimeError`. - /// - /// Returns `Some` if the error matches the `DispatchError::Module` variant, otherwise `None`. - pub fn from_dispatch_error(err: #scrate::sp_runtime::DispatchError) -> Option { - let #scrate::sp_runtime::DispatchError::Module(module_error) = err else { return None }; + quote! { + impl #enum_name_ident { + /// Optionally convert the `DispatchError` into the `RuntimeError`. + /// + /// Returns `Some` if the error matches the `DispatchError::Module` variant, otherwise `None`. + pub fn from_dispatch_error(err: #scrate::sp_runtime::DispatchError) -> Option { + let #scrate::sp_runtime::DispatchError::Module(module_error) = err else { return None }; - let bytes = #scrate::__private::codec::Encode::encode(&module_error); - #scrate::__private::codec::Decode::decode(&mut &bytes[..]).ok() - } - } - } + let bytes = #scrate::__private::codec::Encode::encode(&module_error); + #scrate::__private::codec::Decode::decode(&mut &bytes[..]).ok() + } + } + } } diff --git a/support/procedural-fork/src/construct_runtime/expand/slash_reason.rs b/support/procedural-fork/src/construct_runtime/expand/slash_reason.rs index 892b842b1..0695d8102 100644 --- a/support/procedural-fork/src/construct_runtime/expand/slash_reason.rs +++ b/support/procedural-fork/src/construct_runtime/expand/slash_reason.rs @@ -21,44 +21,44 @@ use proc_macro2::TokenStream; use quote::quote; pub fn expand_outer_slash_reason(pallet_decls: &[Pallet], scrate: &TokenStream) -> TokenStream { - let mut conversion_fns = Vec::new(); - let mut slash_reason_variants = Vec::new(); - for decl in pallet_decls { - if let Some(_) = decl.find_part("SlashReason") { - let variant_name = &decl.name; - let path = &decl.path; - let index = decl.index; - let instance = decl.instance.as_ref(); + let mut conversion_fns = Vec::new(); + let mut slash_reason_variants = Vec::new(); + for decl in pallet_decls { + if let Some(_) = decl.find_part("SlashReason") { + let variant_name = &decl.name; + let path = &decl.path; + let index = decl.index; + let instance = decl.instance.as_ref(); - conversion_fns.push(composite_helper::expand_conversion_fn( - "SlashReason", - path, - instance, - variant_name, - )); + conversion_fns.push(composite_helper::expand_conversion_fn( + "SlashReason", + path, + instance, + variant_name, + )); - slash_reason_variants.push(composite_helper::expand_variant( - "SlashReason", - index, - path, - instance, - variant_name, - )); - } - } + slash_reason_variants.push(composite_helper::expand_variant( + "SlashReason", + index, + path, + instance, + variant_name, + )); + } + } - quote! { - /// A reason for slashing funds. - #[derive( - Copy, Clone, Eq, PartialEq, - #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, - #scrate::__private::scale_info::TypeInfo, - #scrate::__private::RuntimeDebug, - )] - pub enum RuntimeSlashReason { - #( #slash_reason_variants )* - } + quote! { + /// A reason for slashing funds. + #[derive( + Copy, Clone, Eq, PartialEq, + #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, + #scrate::__private::scale_info::TypeInfo, + #scrate::__private::RuntimeDebug, + )] + pub enum RuntimeSlashReason { + #( #slash_reason_variants )* + } - #( #conversion_fns )* - } + #( #conversion_fns )* + } } diff --git a/support/procedural-fork/src/construct_runtime/expand/task.rs b/support/procedural-fork/src/construct_runtime/expand/task.rs index 6531c0e9e..94a5f52bb 100644 --- a/support/procedural-fork/src/construct_runtime/expand/task.rs +++ b/support/procedural-fork/src/construct_runtime/expand/task.rs @@ -21,111 +21,111 @@ use quote::quote; /// Expands aggregate `RuntimeTask` enum. pub fn expand_outer_task( - runtime_name: &Ident, - pallet_decls: &[Pallet], - scrate: &TokenStream2, + runtime_name: &Ident, + pallet_decls: &[Pallet], + scrate: &TokenStream2, ) -> TokenStream2 { - let mut from_impls = Vec::new(); - let mut task_variants = Vec::new(); - let mut variant_names = Vec::new(); - let mut task_paths = Vec::new(); - for decl in pallet_decls { - if decl.find_part("Task").is_none() { - continue - } - - let variant_name = &decl.name; - let path = &decl.path; - let index = decl.index; - - from_impls.push(quote! { - impl From<#path::Task<#runtime_name>> for RuntimeTask { - fn from(hr: #path::Task<#runtime_name>) -> Self { - RuntimeTask::#variant_name(hr) - } - } - - impl TryInto<#path::Task<#runtime_name>> for RuntimeTask { - type Error = (); - - fn try_into(self) -> Result<#path::Task<#runtime_name>, Self::Error> { - match self { - RuntimeTask::#variant_name(hr) => Ok(hr), - _ => Err(()), - } - } - } - }); - - task_variants.push(quote! { - #[codec(index = #index)] - #variant_name(#path::Task<#runtime_name>), - }); - - variant_names.push(quote!(#variant_name)); - - task_paths.push(quote!(#path::Task)); - } - - let prelude = quote!(#scrate::traits::tasks::__private); - - const INCOMPLETE_MATCH_QED: &'static str = - "cannot have an instantiated RuntimeTask without some Task variant in the runtime. QED"; - - let output = quote! { - /// An aggregation of all `Task` enums across all pallets included in the current runtime. - #[derive( - Clone, Eq, PartialEq, - #scrate::__private::codec::Encode, - #scrate::__private::codec::Decode, - #scrate::__private::scale_info::TypeInfo, - #scrate::__private::RuntimeDebug, - )] - pub enum RuntimeTask { - #( #task_variants )* - } - - #[automatically_derived] - impl #scrate::traits::Task for RuntimeTask { - type Enumeration = #prelude::IntoIter; - - fn is_valid(&self) -> bool { - match self { - #(RuntimeTask::#variant_names(val) => val.is_valid(),)* - _ => unreachable!(#INCOMPLETE_MATCH_QED), - } - } - - fn run(&self) -> Result<(), #scrate::traits::tasks::__private::DispatchError> { - match self { - #(RuntimeTask::#variant_names(val) => val.run(),)* - _ => unreachable!(#INCOMPLETE_MATCH_QED), - } - } - - fn weight(&self) -> #scrate::pallet_prelude::Weight { - match self { - #(RuntimeTask::#variant_names(val) => val.weight(),)* - _ => unreachable!(#INCOMPLETE_MATCH_QED), - } - } - - fn task_index(&self) -> u32 { - match self { - #(RuntimeTask::#variant_names(val) => val.task_index(),)* - _ => unreachable!(#INCOMPLETE_MATCH_QED), - } - } - - fn iter() -> Self::Enumeration { - let mut all_tasks = Vec::new(); - #(all_tasks.extend(#task_paths::iter().map(RuntimeTask::from).collect::>());)* - all_tasks.into_iter() - } - } - - #( #from_impls )* - }; - - output + let mut from_impls = Vec::new(); + let mut task_variants = Vec::new(); + let mut variant_names = Vec::new(); + let mut task_paths = Vec::new(); + for decl in pallet_decls { + if decl.find_part("Task").is_none() { + continue; + } + + let variant_name = &decl.name; + let path = &decl.path; + let index = decl.index; + + from_impls.push(quote! { + impl From<#path::Task<#runtime_name>> for RuntimeTask { + fn from(hr: #path::Task<#runtime_name>) -> Self { + RuntimeTask::#variant_name(hr) + } + } + + impl TryInto<#path::Task<#runtime_name>> for RuntimeTask { + type Error = (); + + fn try_into(self) -> Result<#path::Task<#runtime_name>, Self::Error> { + match self { + RuntimeTask::#variant_name(hr) => Ok(hr), + _ => Err(()), + } + } + } + }); + + task_variants.push(quote! { + #[codec(index = #index)] + #variant_name(#path::Task<#runtime_name>), + }); + + variant_names.push(quote!(#variant_name)); + + task_paths.push(quote!(#path::Task)); + } + + let prelude = quote!(#scrate::traits::tasks::__private); + + const INCOMPLETE_MATCH_QED: &'static str = + "cannot have an instantiated RuntimeTask without some Task variant in the runtime. QED"; + + let output = quote! { + /// An aggregation of all `Task` enums across all pallets included in the current runtime. + #[derive( + Clone, Eq, PartialEq, + #scrate::__private::codec::Encode, + #scrate::__private::codec::Decode, + #scrate::__private::scale_info::TypeInfo, + #scrate::__private::RuntimeDebug, + )] + pub enum RuntimeTask { + #( #task_variants )* + } + + #[automatically_derived] + impl #scrate::traits::Task for RuntimeTask { + type Enumeration = #prelude::IntoIter; + + fn is_valid(&self) -> bool { + match self { + #(RuntimeTask::#variant_names(val) => val.is_valid(),)* + _ => unreachable!(#INCOMPLETE_MATCH_QED), + } + } + + fn run(&self) -> Result<(), #scrate::traits::tasks::__private::DispatchError> { + match self { + #(RuntimeTask::#variant_names(val) => val.run(),)* + _ => unreachable!(#INCOMPLETE_MATCH_QED), + } + } + + fn weight(&self) -> #scrate::pallet_prelude::Weight { + match self { + #(RuntimeTask::#variant_names(val) => val.weight(),)* + _ => unreachable!(#INCOMPLETE_MATCH_QED), + } + } + + fn task_index(&self) -> u32 { + match self { + #(RuntimeTask::#variant_names(val) => val.task_index(),)* + _ => unreachable!(#INCOMPLETE_MATCH_QED), + } + } + + fn iter() -> Self::Enumeration { + let mut all_tasks = Vec::new(); + #(all_tasks.extend(#task_paths::iter().map(RuntimeTask::from).collect::>());)* + all_tasks.into_iter() + } + } + + #( #from_impls )* + }; + + output } diff --git a/support/procedural-fork/src/construct_runtime/expand/unsigned.rs b/support/procedural-fork/src/construct_runtime/expand/unsigned.rs index 33aadba0d..109f7081c 100644 --- a/support/procedural-fork/src/construct_runtime/expand/unsigned.rs +++ b/support/procedural-fork/src/construct_runtime/expand/unsigned.rs @@ -22,68 +22,71 @@ use std::str::FromStr; use syn::Ident; pub fn expand_outer_validate_unsigned( - runtime: &Ident, - pallet_decls: &[Pallet], - scrate: &TokenStream, + runtime: &Ident, + pallet_decls: &[Pallet], + scrate: &TokenStream, ) -> TokenStream { - let mut pallet_names = Vec::new(); - let mut pallet_attrs = Vec::new(); - let mut query_validate_unsigned_part_macros = Vec::new(); + let mut pallet_names = Vec::new(); + let mut pallet_attrs = Vec::new(); + let mut query_validate_unsigned_part_macros = Vec::new(); - for pallet_decl in pallet_decls { - if pallet_decl.exists_part("ValidateUnsigned") { - let name = &pallet_decl.name; - let path = &pallet_decl.path; - let attr = pallet_decl.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { - let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) - .expect("was successfully parsed before; qed"); - quote! { - #acc - #attr - } - }); + for pallet_decl in pallet_decls { + if pallet_decl.exists_part("ValidateUnsigned") { + let name = &pallet_decl.name; + let path = &pallet_decl.path; + let attr = pallet_decl + .cfg_pattern + .iter() + .fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); - pallet_names.push(name); - pallet_attrs.push(attr); - query_validate_unsigned_part_macros.push(quote! { + pallet_names.push(name); + pallet_attrs.push(attr); + query_validate_unsigned_part_macros.push(quote! { #path::__substrate_validate_unsigned_check::is_validate_unsigned_part_defined!(#name); }); - } - } + } + } - quote! { - #( #query_validate_unsigned_part_macros )* + quote! { + #( #query_validate_unsigned_part_macros )* - impl #scrate::unsigned::ValidateUnsigned for #runtime { - type Call = RuntimeCall; + impl #scrate::unsigned::ValidateUnsigned for #runtime { + type Call = RuntimeCall; - fn pre_dispatch(call: &Self::Call) -> Result<(), #scrate::unsigned::TransactionValidityError> { - #[allow(unreachable_patterns)] - match call { - #( - #pallet_attrs - RuntimeCall::#pallet_names(inner_call) => #pallet_names::pre_dispatch(inner_call), - )* - // pre-dispatch should not stop inherent extrinsics, validation should prevent - // including arbitrary (non-inherent) extrinsics to blocks. - _ => Ok(()), - } - } + fn pre_dispatch(call: &Self::Call) -> Result<(), #scrate::unsigned::TransactionValidityError> { + #[allow(unreachable_patterns)] + match call { + #( + #pallet_attrs + RuntimeCall::#pallet_names(inner_call) => #pallet_names::pre_dispatch(inner_call), + )* + // pre-dispatch should not stop inherent extrinsics, validation should prevent + // including arbitrary (non-inherent) extrinsics to blocks. + _ => Ok(()), + } + } - fn validate_unsigned( - #[allow(unused_variables)] - source: #scrate::unsigned::TransactionSource, - call: &Self::Call, - ) -> #scrate::unsigned::TransactionValidity { - #[allow(unreachable_patterns)] - match call { - #( - #pallet_attrs - RuntimeCall::#pallet_names(inner_call) => #pallet_names::validate_unsigned(source, inner_call), - )* - _ => #scrate::unsigned::UnknownTransaction::NoUnsignedValidator.into(), - } - } - } - } + fn validate_unsigned( + #[allow(unused_variables)] + source: #scrate::unsigned::TransactionSource, + call: &Self::Call, + ) -> #scrate::unsigned::TransactionValidity { + #[allow(unreachable_patterns)] + match call { + #( + #pallet_attrs + RuntimeCall::#pallet_names(inner_call) => #pallet_names::validate_unsigned(source, inner_call), + )* + _ => #scrate::unsigned::UnknownTransaction::NoUnsignedValidator.into(), + } + } + } + } } diff --git a/support/procedural-fork/src/construct_runtime/mod.rs b/support/procedural-fork/src/construct_runtime/mod.rs index b083abbb2..de688b3d6 100644 --- a/support/procedural-fork/src/construct_runtime/mod.rs +++ b/support/procedural-fork/src/construct_runtime/mod.rs @@ -214,7 +214,7 @@ pub(crate) mod parse; use crate::pallet::parse::helper::two128_str; use cfg_expr::Predicate; use frame_support_procedural_tools::{ - generate_access_from_frame_or_crate, generate_crate_access, generate_hidden_includes, + generate_access_from_frame_or_crate, generate_crate_access, generate_hidden_includes, }; use itertools::Itertools; use parse::{ExplicitRuntimeDeclaration, ImplicitRuntimeDeclaration, Pallet, RuntimeDeclaration}; @@ -230,48 +230,48 @@ const SYSTEM_PALLET_NAME: &str = "System"; /// Implementation of `construct_runtime` macro. Either expand to some code which will call /// `construct_runtime` again, or expand to the final runtime definition. pub fn construct_runtime(input: TokenStream) -> TokenStream { - let input_copy = input.clone(); - let definition = syn::parse_macro_input!(input as RuntimeDeclaration); - - let (check_pallet_number_res, res) = match definition { - RuntimeDeclaration::Implicit(implicit_def) => ( - check_pallet_number(input_copy.clone().into(), implicit_def.pallets.len()), - construct_runtime_implicit_to_explicit(input_copy.into(), implicit_def), - ), - RuntimeDeclaration::Explicit(explicit_decl) => ( - check_pallet_number(input_copy.clone().into(), explicit_decl.pallets.len()), - construct_runtime_explicit_to_explicit_expanded(input_copy.into(), explicit_decl), - ), - RuntimeDeclaration::ExplicitExpanded(explicit_decl) => ( - check_pallet_number(input_copy.into(), explicit_decl.pallets.len()), - construct_runtime_final_expansion(explicit_decl), - ), - }; - - let res = res.unwrap_or_else(|e| e.to_compile_error()); - - // We want to provide better error messages to the user and thus, handle the error here - // separately. If there is an error, we print the error and still generate all of the code to - // get in overall less errors for the user. - let res = if let Err(error) = check_pallet_number_res { - let error = error.to_compile_error(); - - quote! { - #error - - #res - } - } else { - res - }; - - let res = expander::Expander::new("construct_runtime") - .dry(std::env::var("EXPAND_MACROS").is_err()) - .verbose(true) - .write_to_out_dir(res) - .expect("Does not fail because of IO in OUT_DIR; qed"); - - res.into() + let input_copy = input.clone(); + let definition = syn::parse_macro_input!(input as RuntimeDeclaration); + + let (check_pallet_number_res, res) = match definition { + RuntimeDeclaration::Implicit(implicit_def) => ( + check_pallet_number(input_copy.clone().into(), implicit_def.pallets.len()), + construct_runtime_implicit_to_explicit(input_copy.into(), implicit_def), + ), + RuntimeDeclaration::Explicit(explicit_decl) => ( + check_pallet_number(input_copy.clone().into(), explicit_decl.pallets.len()), + construct_runtime_explicit_to_explicit_expanded(input_copy.into(), explicit_decl), + ), + RuntimeDeclaration::ExplicitExpanded(explicit_decl) => ( + check_pallet_number(input_copy.into(), explicit_decl.pallets.len()), + construct_runtime_final_expansion(explicit_decl), + ), + }; + + let res = res.unwrap_or_else(|e| e.to_compile_error()); + + // We want to provide better error messages to the user and thus, handle the error here + // separately. If there is an error, we print the error and still generate all of the code to + // get in overall less errors for the user. + let res = if let Err(error) = check_pallet_number_res { + let error = error.to_compile_error(); + + quote! { + #error + + #res + } + } else { + res + }; + + let res = expander::Expander::new("construct_runtime") + .dry(std::env::var("EXPAND_MACROS").is_err()) + .verbose(true) + .write_to_out_dir(res) + .expect("Does not fail because of IO in OUT_DIR; qed"); + + res.into() } /// All pallets that have implicit pallet parts (ie `System: frame_system`) are @@ -282,30 +282,37 @@ pub fn construct_runtime(input: TokenStream) -> TokenStream { /// /// For more details, please refer to the root documentation. fn construct_runtime_implicit_to_explicit( - input: TokenStream2, - definition: ImplicitRuntimeDeclaration, + input: TokenStream2, + definition: ImplicitRuntimeDeclaration, ) -> Result { - let frame_support = generate_access_from_frame_or_crate("frame-support")?; - let mut expansion = quote::quote!( - #frame_support::construct_runtime! { #input } - ); - for pallet in definition.pallets.iter().filter(|pallet| pallet.pallet_parts.is_none()) { - let pallet_path = &pallet.path; - let pallet_name = &pallet.name; - let pallet_instance = pallet.instance.as_ref().map(|instance| quote::quote!(::<#instance>)); - expansion = quote::quote!( - #frame_support::__private::tt_call! { - macro = [{ #pallet_path::tt_default_parts }] - your_tt_return = [{ #frame_support::__private::tt_return }] - ~~> #frame_support::match_and_insert! { - target = [{ #expansion }] - pattern = [{ #pallet_name: #pallet_path #pallet_instance }] - } - } - ); - } - - Ok(expansion) + let frame_support = generate_access_from_frame_or_crate("frame-support")?; + let mut expansion = quote::quote!( + #frame_support::construct_runtime! { #input } + ); + for pallet in definition + .pallets + .iter() + .filter(|pallet| pallet.pallet_parts.is_none()) + { + let pallet_path = &pallet.path; + let pallet_name = &pallet.name; + let pallet_instance = pallet + .instance + .as_ref() + .map(|instance| quote::quote!(::<#instance>)); + expansion = quote::quote!( + #frame_support::__private::tt_call! { + macro = [{ #pallet_path::tt_default_parts }] + your_tt_return = [{ #frame_support::__private::tt_return }] + ~~> #frame_support::match_and_insert! { + target = [{ #expansion }] + pattern = [{ #pallet_name: #pallet_path #pallet_instance }] + } + } + ); + } + + Ok(expansion) } /// All pallets that have @@ -318,264 +325,283 @@ fn construct_runtime_implicit_to_explicit( /// /// For more details, please refer to the root documentation. fn construct_runtime_explicit_to_explicit_expanded( - input: TokenStream2, - definition: ExplicitRuntimeDeclaration, + input: TokenStream2, + definition: ExplicitRuntimeDeclaration, ) -> Result { - let frame_support = generate_access_from_frame_or_crate("frame-support")?; - let mut expansion = quote::quote!( - #frame_support::construct_runtime! { #input } - ); - for pallet in definition.pallets.iter().filter(|pallet| !pallet.is_expanded) { - let pallet_path = &pallet.path; - let pallet_name = &pallet.name; - let pallet_instance = pallet.instance.as_ref().map(|instance| quote::quote!(::<#instance>)); - expansion = quote::quote!( - #frame_support::__private::tt_call! { - macro = [{ #pallet_path::tt_extra_parts }] - your_tt_return = [{ #frame_support::__private::tt_return }] - ~~> #frame_support::match_and_insert! { - target = [{ #expansion }] - pattern = [{ #pallet_name: #pallet_path #pallet_instance }] - } - } - ); - } - - Ok(expansion) + let frame_support = generate_access_from_frame_or_crate("frame-support")?; + let mut expansion = quote::quote!( + #frame_support::construct_runtime! { #input } + ); + for pallet in definition + .pallets + .iter() + .filter(|pallet| !pallet.is_expanded) + { + let pallet_path = &pallet.path; + let pallet_name = &pallet.name; + let pallet_instance = pallet + .instance + .as_ref() + .map(|instance| quote::quote!(::<#instance>)); + expansion = quote::quote!( + #frame_support::__private::tt_call! { + macro = [{ #pallet_path::tt_extra_parts }] + your_tt_return = [{ #frame_support::__private::tt_return }] + ~~> #frame_support::match_and_insert! { + target = [{ #expansion }] + pattern = [{ #pallet_name: #pallet_path #pallet_instance }] + } + } + ); + } + + Ok(expansion) } /// All pallets have explicit definition of parts, this will expand to the runtime declaration. fn construct_runtime_final_expansion( - definition: ExplicitRuntimeDeclaration, + definition: ExplicitRuntimeDeclaration, ) -> Result { - let ExplicitRuntimeDeclaration { name, pallets, pallets_token, where_section } = definition; - - let system_pallet = - pallets.iter().find(|decl| decl.name == SYSTEM_PALLET_NAME).ok_or_else(|| { - syn::Error::new( - pallets_token.span.join(), - "`System` pallet declaration is missing. \ + let ExplicitRuntimeDeclaration { + name, + pallets, + pallets_token, + where_section, + } = definition; + + let system_pallet = pallets + .iter() + .find(|decl| decl.name == SYSTEM_PALLET_NAME) + .ok_or_else(|| { + syn::Error::new( + pallets_token.span.join(), + "`System` pallet declaration is missing. \ Please add this line: `System: frame_system,`", - ) - })?; - if !system_pallet.cfg_pattern.is_empty() { - return Err(syn::Error::new( - system_pallet.name.span(), - "`System` pallet declaration is feature gated, please remove any `#[cfg]` attributes", - )) - } - - let features = pallets - .iter() - .filter_map(|decl| { - (!decl.cfg_pattern.is_empty()).then(|| { - decl.cfg_pattern.iter().flat_map(|attr| { - attr.predicates().filter_map(|pred| match pred { - Predicate::Feature(feat) => Some(feat), - Predicate::Test => Some("test"), - _ => None, - }) - }) - }) - }) - .flatten() - .collect::>(); - - 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 frame_system = generate_access_from_frame_or_crate("frame-system")?; - let block = quote!(<#name as #frame_system::Config>::Block); - let unchecked_extrinsic = quote!(<#block as #scrate::sp_runtime::traits::Block>::Extrinsic); - - let outer_event = - expand::expand_outer_enum(&name, &pallets, &scrate, expand::OuterEnumType::Event)?; - let outer_error = - expand::expand_outer_enum(&name, &pallets, &scrate, expand::OuterEnumType::Error)?; - - let outer_origin = expand::expand_outer_origin(&name, system_pallet, &pallets, &scrate)?; - let all_pallets = decl_all_pallets(&name, pallets.iter(), &features); - let pallet_to_index = decl_pallet_runtime_setup(&name, &pallets, &scrate); - - let dispatch = expand::expand_outer_dispatch(&name, system_pallet, &pallets, &scrate); - let tasks = expand::expand_outer_task(&name, &pallets, &scrate); - let metadata = expand::expand_runtime_metadata( - &name, - &pallets, - &scrate, - &unchecked_extrinsic, - &system_pallet.path, - ); - let outer_config = expand::expand_outer_config(&name, &pallets, &scrate); - let inherent = - expand::expand_outer_inherent(&name, &block, &unchecked_extrinsic, &pallets, &scrate); - let validate_unsigned = expand::expand_outer_validate_unsigned(&name, &pallets, &scrate); - let freeze_reason = expand::expand_outer_freeze_reason(&pallets, &scrate); - let hold_reason = expand::expand_outer_hold_reason(&pallets, &scrate); - let lock_id = expand::expand_outer_lock_id(&pallets, &scrate); - let slash_reason = expand::expand_outer_slash_reason(&pallets, &scrate); - let integrity_test = decl_integrity_test(&scrate); - let static_assertions = decl_static_assertions(&name, &pallets, &scrate); - - let warning = where_section.map_or(None, |where_section| { - Some( - proc_macro_warning::Warning::new_deprecated("WhereSection") - .old("use a `where` clause in `construct_runtime`") - .new( - "use `frame_system::Config` to set the `Block` type and delete this clause. + ) + })?; + if !system_pallet.cfg_pattern.is_empty() { + return Err(syn::Error::new( + system_pallet.name.span(), + "`System` pallet declaration is feature gated, please remove any `#[cfg]` attributes", + )); + } + + let features = pallets + .iter() + .filter_map(|decl| { + (!decl.cfg_pattern.is_empty()).then(|| { + decl.cfg_pattern.iter().flat_map(|attr| { + attr.predicates().filter_map(|pred| match pred { + Predicate::Feature(feat) => Some(feat), + Predicate::Test => Some("test"), + _ => None, + }) + }) + }) + }) + .flatten() + .collect::>(); + + 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 frame_system = generate_access_from_frame_or_crate("frame-system")?; + let block = quote!(<#name as #frame_system::Config>::Block); + let unchecked_extrinsic = quote!(<#block as #scrate::sp_runtime::traits::Block>::Extrinsic); + + let outer_event = + expand::expand_outer_enum(&name, &pallets, &scrate, expand::OuterEnumType::Event)?; + let outer_error = + expand::expand_outer_enum(&name, &pallets, &scrate, expand::OuterEnumType::Error)?; + + let outer_origin = expand::expand_outer_origin(&name, system_pallet, &pallets, &scrate)?; + let all_pallets = decl_all_pallets(&name, pallets.iter(), &features); + let pallet_to_index = decl_pallet_runtime_setup(&name, &pallets, &scrate); + + let dispatch = expand::expand_outer_dispatch(&name, system_pallet, &pallets, &scrate); + let tasks = expand::expand_outer_task(&name, &pallets, &scrate); + let metadata = expand::expand_runtime_metadata( + &name, + &pallets, + &scrate, + &unchecked_extrinsic, + &system_pallet.path, + ); + let outer_config = expand::expand_outer_config(&name, &pallets, &scrate); + let inherent = + expand::expand_outer_inherent(&name, &block, &unchecked_extrinsic, &pallets, &scrate); + let validate_unsigned = expand::expand_outer_validate_unsigned(&name, &pallets, &scrate); + let freeze_reason = expand::expand_outer_freeze_reason(&pallets, &scrate); + let hold_reason = expand::expand_outer_hold_reason(&pallets, &scrate); + let lock_id = expand::expand_outer_lock_id(&pallets, &scrate); + let slash_reason = expand::expand_outer_slash_reason(&pallets, &scrate); + let integrity_test = decl_integrity_test(&scrate); + let static_assertions = decl_static_assertions(&name, &pallets, &scrate); + + let warning = where_section.map_or(None, |where_section| { + Some( + proc_macro_warning::Warning::new_deprecated("WhereSection") + .old("use a `where` clause in `construct_runtime`") + .new( + "use `frame_system::Config` to set the `Block` type and delete this clause. It is planned to be removed in December 2023", - ) - .help_links(&["https://github.com/paritytech/substrate/pull/14437"]) - .span(where_section.span) - .build_or_panic(), - ) - }); - - let res = quote!( - #warning - - #scrate_decl - - // Prevent UncheckedExtrinsic to print unused warning. - const _: () = { - #[allow(unused)] - type __hidden_use_of_unchecked_extrinsic = #unchecked_extrinsic; - }; - - #[derive( - Clone, Copy, PartialEq, Eq, #scrate::sp_runtime::RuntimeDebug, - #scrate::__private::scale_info::TypeInfo - )] - pub struct #name; - impl #scrate::sp_runtime::traits::GetRuntimeBlockType for #name { - type RuntimeBlock = #block; - } - - // Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata. - // The function is implemented by calling `impl_runtime_apis!`. - // - // However, the `construct_runtime!` may be called without calling `impl_runtime_apis!`. - // Rely on the `Deref` trait to differentiate between a runtime that implements - // APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro construct_runtime!). - // - // Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function. - // `InternalConstructRuntime` is implemented by the `construct_runtime!` for Runtime references (`& Runtime`), - // while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`). - // - // Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!` - // when both macros are called; and will resolve an empty `runtime_metadata` when only the `construct_runtime!` - // is called. - - #[doc(hidden)] - trait InternalConstructRuntime { - #[inline(always)] - fn runtime_metadata(&self) -> #scrate::__private::sp_std::vec::Vec<#scrate::__private::metadata_ir::RuntimeApiMetadataIR> { - Default::default() - } - } - #[doc(hidden)] - impl InternalConstructRuntime for &#name {} + ) + .help_links(&["https://github.com/paritytech/substrate/pull/14437"]) + .span(where_section.span) + .build_or_panic(), + ) + }); + + let res = quote!( + #warning + + #scrate_decl + + // Prevent UncheckedExtrinsic to print unused warning. + const _: () = { + #[allow(unused)] + type __hidden_use_of_unchecked_extrinsic = #unchecked_extrinsic; + }; - #outer_event + #[derive( + Clone, Copy, PartialEq, Eq, #scrate::sp_runtime::RuntimeDebug, + #scrate::__private::scale_info::TypeInfo + )] + pub struct #name; + impl #scrate::sp_runtime::traits::GetRuntimeBlockType for #name { + type RuntimeBlock = #block; + } - #outer_error + // Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata. + // The function is implemented by calling `impl_runtime_apis!`. + // + // However, the `construct_runtime!` may be called without calling `impl_runtime_apis!`. + // Rely on the `Deref` trait to differentiate between a runtime that implements + // APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro construct_runtime!). + // + // Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function. + // `InternalConstructRuntime` is implemented by the `construct_runtime!` for Runtime references (`& Runtime`), + // while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`). + // + // Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!` + // when both macros are called; and will resolve an empty `runtime_metadata` when only the `construct_runtime!` + // is called. - #outer_origin + #[doc(hidden)] + trait InternalConstructRuntime { + #[inline(always)] + fn runtime_metadata(&self) -> #scrate::__private::sp_std::vec::Vec<#scrate::__private::metadata_ir::RuntimeApiMetadataIR> { + Default::default() + } + } + #[doc(hidden)] + impl InternalConstructRuntime for &#name {} - #all_pallets + #outer_event - #pallet_to_index + #outer_error - #dispatch + #outer_origin - #tasks + #all_pallets - #metadata + #pallet_to_index - #outer_config + #dispatch - #inherent + #tasks - #validate_unsigned + #metadata - #freeze_reason + #outer_config - #hold_reason + #inherent - #lock_id + #validate_unsigned - #slash_reason + #freeze_reason - #integrity_test + #hold_reason - #static_assertions - ); + #lock_id - Ok(res) + #slash_reason + + #integrity_test + + #static_assertions + ); + + Ok(res) } pub(crate) fn decl_all_pallets<'a>( - runtime: &'a Ident, - pallet_declarations: impl Iterator, - features: &HashSet<&str>, + runtime: &'a Ident, + pallet_declarations: impl Iterator, + features: &HashSet<&str>, ) -> TokenStream2 { - let mut types = TokenStream2::new(); - - // Every feature set to the pallet names that should be included by this feature set. - let mut features_to_names = features - .iter() - .map(|f| *f) - .powerset() - .map(|feat| (HashSet::from_iter(feat), Vec::new())) - .collect::, Vec<_>)>>(); - - for pallet_declaration in pallet_declarations { - let type_name = &pallet_declaration.name; - let pallet = &pallet_declaration.path; - let mut generics = vec![quote!(#runtime)]; - generics.extend(pallet_declaration.instance.iter().map(|name| quote!(#pallet::#name))); - let mut attrs = Vec::new(); - for cfg in &pallet_declaration.cfg_pattern { - let feat = format!("#[cfg({})]\n", cfg.original()); - attrs.extend(TokenStream2::from_str(&feat).expect("was parsed successfully; qed")); - } - let type_decl = quote!( - #(#attrs)* - pub type #type_name = #pallet::Pallet <#(#generics),*>; - ); - types.extend(type_decl); - - if pallet_declaration.cfg_pattern.is_empty() { - for (_, names) in features_to_names.iter_mut() { - names.push(&pallet_declaration.name); - } - } else { - for (feature_set, names) in &mut features_to_names { - // Rust tidbit: if we have multiple `#[cfg]` feature on the same item, then the - // predicates listed in all `#[cfg]` attributes are effectively joined by `and()`, - // meaning that all of them must match in order to activate the item - let is_feature_active = pallet_declaration.cfg_pattern.iter().all(|expr| { - expr.eval(|pred| match pred { - Predicate::Feature(f) => feature_set.contains(f), - Predicate::Test => feature_set.contains(&"test"), - _ => false, - }) - }); - - if is_feature_active { - names.push(&pallet_declaration.name); - } - } - } - } - - // All possible features. This will be used below for the empty feature set. - let mut all_features = features_to_names - .iter() - .flat_map(|f| f.0.iter().cloned()) - .collect::>(); - let attribute_to_names = features_to_names + let mut types = TokenStream2::new(); + + // Every feature set to the pallet names that should be included by this feature set. + let mut features_to_names = features + .iter() + .map(|f| *f) + .powerset() + .map(|feat| (HashSet::from_iter(feat), Vec::new())) + .collect::, Vec<_>)>>(); + + for pallet_declaration in pallet_declarations { + let type_name = &pallet_declaration.name; + let pallet = &pallet_declaration.path; + let mut generics = vec![quote!(#runtime)]; + generics.extend( + pallet_declaration + .instance + .iter() + .map(|name| quote!(#pallet::#name)), + ); + let mut attrs = Vec::new(); + for cfg in &pallet_declaration.cfg_pattern { + let feat = format!("#[cfg({})]\n", cfg.original()); + attrs.extend(TokenStream2::from_str(&feat).expect("was parsed successfully; qed")); + } + let type_decl = quote!( + #(#attrs)* + pub type #type_name = #pallet::Pallet <#(#generics),*>; + ); + types.extend(type_decl); + + if pallet_declaration.cfg_pattern.is_empty() { + for (_, names) in features_to_names.iter_mut() { + names.push(&pallet_declaration.name); + } + } else { + for (feature_set, names) in &mut features_to_names { + // Rust tidbit: if we have multiple `#[cfg]` feature on the same item, then the + // predicates listed in all `#[cfg]` attributes are effectively joined by `and()`, + // meaning that all of them must match in order to activate the item + let is_feature_active = pallet_declaration.cfg_pattern.iter().all(|expr| { + expr.eval(|pred| match pred { + Predicate::Feature(f) => feature_set.contains(f), + Predicate::Test => feature_set.contains(&"test"), + _ => false, + }) + }); + + if is_feature_active { + names.push(&pallet_declaration.name); + } + } + } + } + + // All possible features. This will be used below for the empty feature set. + let mut all_features = features_to_names + .iter() + .flat_map(|f| f.0.iter().cloned()) + .collect::>(); + let attribute_to_names = features_to_names .into_iter() .map(|(mut features, names)| { // If this is the empty feature set, it needs to be changed to negate all available @@ -598,212 +624,222 @@ pub(crate) fn decl_all_pallets<'a>( }) .collect::>(); - let all_pallets_without_system = attribute_to_names.iter().map(|(attr, names)| { - let names = names.iter().filter(|n| **n != SYSTEM_PALLET_NAME); - quote! { - #attr - /// All pallets included in the runtime as a nested tuple of types. - /// Excludes the System pallet. - pub type AllPalletsWithoutSystem = ( #(#names,)* ); - } - }); - - let all_pallets_with_system = attribute_to_names.iter().map(|(attr, names)| { - quote! { - #attr - /// All pallets included in the runtime as a nested tuple of types. - pub type AllPalletsWithSystem = ( #(#names,)* ); - } - }); - - quote!( - #types - - #( #all_pallets_with_system )* - - #( #all_pallets_without_system )* - ) + let all_pallets_without_system = attribute_to_names.iter().map(|(attr, names)| { + let names = names.iter().filter(|n| **n != SYSTEM_PALLET_NAME); + quote! { + #attr + /// All pallets included in the runtime as a nested tuple of types. + /// Excludes the System pallet. + pub type AllPalletsWithoutSystem = ( #(#names,)* ); + } + }); + + let all_pallets_with_system = attribute_to_names.iter().map(|(attr, names)| { + quote! { + #attr + /// All pallets included in the runtime as a nested tuple of types. + pub type AllPalletsWithSystem = ( #(#names,)* ); + } + }); + + quote!( + #types + + #( #all_pallets_with_system )* + + #( #all_pallets_without_system )* + ) } pub(crate) fn decl_pallet_runtime_setup( - runtime: &Ident, - pallet_declarations: &[Pallet], - scrate: &TokenStream2, + runtime: &Ident, + pallet_declarations: &[Pallet], + scrate: &TokenStream2, ) -> TokenStream2 { - let names = pallet_declarations.iter().map(|d| &d.name).collect::>(); - let name_strings = pallet_declarations.iter().map(|d| d.name.to_string()); - let name_hashes = pallet_declarations.iter().map(|d| two128_str(&d.name.to_string())); - let module_names = pallet_declarations.iter().map(|d| d.path.module_name()); - let indices = pallet_declarations.iter().map(|pallet| pallet.index as usize); - let pallet_structs = pallet_declarations - .iter() - .map(|pallet| { - let path = &pallet.path; - match pallet.instance.as_ref() { - Some(inst) => quote!(#path::Pallet<#runtime, #path::#inst>), - None => quote!(#path::Pallet<#runtime>), - } - }) - .collect::>(); - let pallet_attrs = pallet_declarations - .iter() - .map(|pallet| { - pallet.cfg_pattern.iter().fold(TokenStream2::new(), |acc, pattern| { - let attr = TokenStream2::from_str(&format!("#[cfg({})]", pattern.original())) - .expect("was successfully parsed before; qed"); - quote! { - #acc - #attr - } - }) - }) - .collect::>(); - - quote!( - /// Provides an implementation of `PalletInfo` to provide information - /// about the pallet setup in the runtime. - pub struct PalletInfo; - - impl #scrate::traits::PalletInfo for PalletInfo { - - fn index() -> Option { - let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); - #( - #pallet_attrs - if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { - return Some(#indices) - } - )* - - None - } - - fn name() -> Option<&'static str> { - let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); - #( - #pallet_attrs - if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { - return Some(#name_strings) - } - )* - - None - } - - fn name_hash() -> Option<[u8; 16]> { - let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); - #( - #pallet_attrs - if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { - return Some(#name_hashes) - } - )* - - None - } - - fn module_name() -> Option<&'static str> { - let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); - #( - #pallet_attrs - if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { - return Some(#module_names) - } - )* - - None - } - - fn crate_version() -> Option<#scrate::traits::CrateVersion> { - let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); - #( - #pallet_attrs - if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { - return Some( - <#pallet_structs as #scrate::traits::PalletInfoAccess>::crate_version() - ) - } - )* - - None - } - } - ) + let names = pallet_declarations + .iter() + .map(|d| &d.name) + .collect::>(); + let name_strings = pallet_declarations.iter().map(|d| d.name.to_string()); + let name_hashes = pallet_declarations + .iter() + .map(|d| two128_str(&d.name.to_string())); + let module_names = pallet_declarations.iter().map(|d| d.path.module_name()); + let indices = pallet_declarations + .iter() + .map(|pallet| pallet.index as usize); + let pallet_structs = pallet_declarations + .iter() + .map(|pallet| { + let path = &pallet.path; + match pallet.instance.as_ref() { + Some(inst) => quote!(#path::Pallet<#runtime, #path::#inst>), + None => quote!(#path::Pallet<#runtime>), + } + }) + .collect::>(); + let pallet_attrs = pallet_declarations + .iter() + .map(|pallet| { + pallet + .cfg_pattern + .iter() + .fold(TokenStream2::new(), |acc, pattern| { + let attr = TokenStream2::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }) + }) + .collect::>(); + + quote!( + /// Provides an implementation of `PalletInfo` to provide information + /// about the pallet setup in the runtime. + pub struct PalletInfo; + + impl #scrate::traits::PalletInfo for PalletInfo { + + fn index() -> Option { + let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); + #( + #pallet_attrs + if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { + return Some(#indices) + } + )* + + None + } + + fn name() -> Option<&'static str> { + let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); + #( + #pallet_attrs + if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { + return Some(#name_strings) + } + )* + + None + } + + fn name_hash() -> Option<[u8; 16]> { + let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); + #( + #pallet_attrs + if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { + return Some(#name_hashes) + } + )* + + None + } + + fn module_name() -> Option<&'static str> { + let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); + #( + #pallet_attrs + if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { + return Some(#module_names) + } + )* + + None + } + + fn crate_version() -> Option<#scrate::traits::CrateVersion> { + let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); + #( + #pallet_attrs + if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { + return Some( + <#pallet_structs as #scrate::traits::PalletInfoAccess>::crate_version() + ) + } + )* + + None + } + } + ) } pub(crate) fn decl_integrity_test(scrate: &TokenStream2) -> TokenStream2 { - quote!( - #[cfg(test)] - mod __construct_runtime_integrity_test { - use super::*; - - #[test] - pub fn runtime_integrity_tests() { - #scrate::__private::sp_tracing::try_init_simple(); - ::integrity_test(); - } - } - ) + quote!( + #[cfg(test)] + mod __construct_runtime_integrity_test { + use super::*; + + #[test] + pub fn runtime_integrity_tests() { + #scrate::__private::sp_tracing::try_init_simple(); + ::integrity_test(); + } + } + ) } pub(crate) fn decl_static_assertions( - runtime: &Ident, - pallet_decls: &[Pallet], - scrate: &TokenStream2, + runtime: &Ident, + pallet_decls: &[Pallet], + scrate: &TokenStream2, ) -> TokenStream2 { - let error_encoded_size_check = pallet_decls.iter().map(|decl| { - let path = &decl.path; - let assert_message = format!( - "The maximum encoded size of the error type in the `{}` pallet exceeds \ + let error_encoded_size_check = pallet_decls.iter().map(|decl| { + let path = &decl.path; + let assert_message = format!( + "The maximum encoded size of the error type in the `{}` pallet exceeds \ `MAX_MODULE_ERROR_ENCODED_SIZE`", - decl.name, - ); - - quote! { - #scrate::__private::tt_call! { - macro = [{ #path::tt_error_token }] - your_tt_return = [{ #scrate::__private::tt_return }] - ~~> #scrate::assert_error_encoded_size! { - path = [{ #path }] - runtime = [{ #runtime }] - assert_message = [{ #assert_message }] - } - } - } - }); - - quote! { - #(#error_encoded_size_check)* - } + decl.name, + ); + + quote! { + #scrate::__private::tt_call! { + macro = [{ #path::tt_error_token }] + your_tt_return = [{ #scrate::__private::tt_return }] + ~~> #scrate::assert_error_encoded_size! { + path = [{ #path }] + runtime = [{ #runtime }] + assert_message = [{ #assert_message }] + } + } + } + }); + + quote! { + #(#error_encoded_size_check)* + } } pub(crate) fn check_pallet_number(input: TokenStream2, pallet_num: usize) -> Result<()> { - let max_pallet_num = { - if cfg!(feature = "tuples-96") { - 96 - } else if cfg!(feature = "tuples-128") { - 128 - } else { - 64 - } - }; - - if pallet_num > max_pallet_num { - let no_feature = max_pallet_num == 128; - return Err(syn::Error::new( - input.span(), - format!( - "{} To increase this limit, enable the tuples-{} feature of [frame_support]. {}", - "The number of pallets exceeds the maximum number of tuple elements.", - max_pallet_num + 32, - if no_feature { - "If the feature does not exist - it needs to be implemented." - } else { - "" - }, - ), - )) - } - - Ok(()) + let max_pallet_num = { + if cfg!(feature = "tuples-96") { + 96 + } else if cfg!(feature = "tuples-128") { + 128 + } else { + 64 + } + }; + + if pallet_num > max_pallet_num { + let no_feature = max_pallet_num == 128; + return Err(syn::Error::new( + input.span(), + format!( + "{} To increase this limit, enable the tuples-{} feature of [frame_support]. {}", + "The number of pallets exceeds the maximum number of tuple elements.", + max_pallet_num + 32, + if no_feature { + "If the feature does not exist - it needs to be implemented." + } else { + "" + }, + ), + )); + } + + Ok(()) } diff --git a/support/procedural-fork/src/construct_runtime/parse.rs b/support/procedural-fork/src/construct_runtime/parse.rs index 31866c787..173a8dd12 100644 --- a/support/procedural-fork/src/construct_runtime/parse.rs +++ b/support/procedural-fork/src/construct_runtime/parse.rs @@ -20,34 +20,34 @@ use proc_macro2::{Span, TokenStream}; use quote::ToTokens; use std::collections::{HashMap, HashSet}; use syn::{ - ext::IdentExt, - parse::{Parse, ParseStream}, - punctuated::Punctuated, - spanned::Spanned, - token, Attribute, Error, Ident, Path, Result, Token, + ext::IdentExt, + parse::{Parse, ParseStream}, + punctuated::Punctuated, + spanned::Spanned, + token, Attribute, Error, Ident, Path, Result, Token, }; mod keyword { - syn::custom_keyword!(Block); - syn::custom_keyword!(NodeBlock); - syn::custom_keyword!(UncheckedExtrinsic); - syn::custom_keyword!(Pallet); - syn::custom_keyword!(Call); - syn::custom_keyword!(Storage); - syn::custom_keyword!(Event); - syn::custom_keyword!(Error); - syn::custom_keyword!(Config); - syn::custom_keyword!(Origin); - syn::custom_keyword!(Inherent); - syn::custom_keyword!(ValidateUnsigned); - syn::custom_keyword!(FreezeReason); - syn::custom_keyword!(HoldReason); - syn::custom_keyword!(Task); - syn::custom_keyword!(LockId); - syn::custom_keyword!(SlashReason); - syn::custom_keyword!(exclude_parts); - syn::custom_keyword!(use_parts); - syn::custom_keyword!(expanded); + syn::custom_keyword!(Block); + syn::custom_keyword!(NodeBlock); + syn::custom_keyword!(UncheckedExtrinsic); + syn::custom_keyword!(Pallet); + syn::custom_keyword!(Call); + syn::custom_keyword!(Storage); + syn::custom_keyword!(Event); + syn::custom_keyword!(Error); + syn::custom_keyword!(Config); + syn::custom_keyword!(Origin); + syn::custom_keyword!(Inherent); + syn::custom_keyword!(ValidateUnsigned); + syn::custom_keyword!(FreezeReason); + syn::custom_keyword!(HoldReason); + syn::custom_keyword!(Task); + syn::custom_keyword!(LockId); + syn::custom_keyword!(SlashReason); + syn::custom_keyword!(exclude_parts); + syn::custom_keyword!(use_parts); + syn::custom_keyword!(expanded); } /// Declaration of a runtime. @@ -57,266 +57,298 @@ mod keyword { /// implicit. #[derive(Debug)] pub enum RuntimeDeclaration { - Implicit(ImplicitRuntimeDeclaration), - Explicit(ExplicitRuntimeDeclaration), - ExplicitExpanded(ExplicitRuntimeDeclaration), + Implicit(ImplicitRuntimeDeclaration), + Explicit(ExplicitRuntimeDeclaration), + ExplicitExpanded(ExplicitRuntimeDeclaration), } /// Declaration of a runtime with some pallet with implicit declaration of parts. #[derive(Debug)] pub struct ImplicitRuntimeDeclaration { - pub name: Ident, - pub where_section: Option, - pub pallets: Vec, + pub name: Ident, + pub where_section: Option, + pub pallets: Vec, } /// Declaration of a runtime with all pallet having explicit declaration of parts. #[derive(Debug)] pub struct ExplicitRuntimeDeclaration { - pub name: Ident, - pub where_section: Option, - pub pallets: Vec, - pub pallets_token: token::Brace, + pub name: Ident, + pub where_section: Option, + pub pallets: Vec, + pub pallets_token: token::Brace, } impl Parse for RuntimeDeclaration { - fn parse(input: ParseStream) -> Result { - input.parse::()?; - - // Support either `enum` or `struct`. - if input.peek(Token![struct]) { - input.parse::()?; - } else { - input.parse::()?; - } - - let name = input.parse::()?; - let where_section = if input.peek(token::Where) { Some(input.parse()?) } else { None }; - let pallets = - input.parse::>>()?; - let pallets_token = pallets.token; - - match convert_pallets(pallets.content.inner.into_iter().collect())? { - PalletsConversion::Implicit(pallets) => - Ok(RuntimeDeclaration::Implicit(ImplicitRuntimeDeclaration { - name, - where_section, - pallets, - })), - PalletsConversion::Explicit(pallets) => - Ok(RuntimeDeclaration::Explicit(ExplicitRuntimeDeclaration { - name, - where_section, - pallets, - pallets_token, - })), - PalletsConversion::ExplicitExpanded(pallets) => - Ok(RuntimeDeclaration::ExplicitExpanded(ExplicitRuntimeDeclaration { - name, - where_section, - pallets, - pallets_token, - })), - } - } + fn parse(input: ParseStream) -> Result { + input.parse::()?; + + // Support either `enum` or `struct`. + if input.peek(Token![struct]) { + input.parse::()?; + } else { + input.parse::()?; + } + + let name = input.parse::()?; + let where_section = if input.peek(token::Where) { + Some(input.parse()?) + } else { + None + }; + let pallets = + input.parse::>>()?; + let pallets_token = pallets.token; + + match convert_pallets(pallets.content.inner.into_iter().collect())? { + PalletsConversion::Implicit(pallets) => { + Ok(RuntimeDeclaration::Implicit(ImplicitRuntimeDeclaration { + name, + where_section, + pallets, + })) + } + PalletsConversion::Explicit(pallets) => { + Ok(RuntimeDeclaration::Explicit(ExplicitRuntimeDeclaration { + name, + where_section, + pallets, + pallets_token, + })) + } + PalletsConversion::ExplicitExpanded(pallets) => Ok( + RuntimeDeclaration::ExplicitExpanded(ExplicitRuntimeDeclaration { + name, + where_section, + pallets, + pallets_token, + }), + ), + } + } } #[derive(Debug)] pub struct WhereSection { - pub span: Span, - pub block: syn::TypePath, - pub node_block: syn::TypePath, - pub unchecked_extrinsic: syn::TypePath, + pub span: Span, + 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 declaration for `{:?}`.", - kind, kind - ); - return Err(Error::new(*kind_span, msg)) - } - Ok(Self { span: input.span(), block, node_block, unchecked_extrinsic }) - } + 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 declaration for `{:?}`.", + kind, kind + ); + return Err(Error::new(*kind_span, msg)); + } + Ok(Self { + span: input.span(), + block, + node_block, + unchecked_extrinsic, + }) + } } #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] pub enum WhereKind { - Block, - NodeBlock, - UncheckedExtrinsic, + Block, + NodeBlock, + UncheckedExtrinsic, } #[derive(Debug)] pub struct WhereDefinition { - pub kind_span: Span, - pub kind: WhereKind, - pub value: syn::TypePath, + 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()? - }, - }) - } + 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()? + }, + }) + } } /// The declaration of a pallet. #[derive(Debug, Clone)] pub struct PalletDeclaration { - /// Is this pallet fully expanded? - pub is_expanded: bool, - /// The name of the pallet, e.g.`System` in `System: frame_system`. - pub name: Ident, - /// Optional attributes tagged right above a pallet declaration. - pub attrs: Vec, - /// Optional fixed index, e.g. `MyPallet ... = 3,`. - pub index: Option, - /// The path of the pallet, e.g. `frame_system` in `System: frame_system`. - pub path: PalletPath, - /// The instance of the pallet, e.g. `Instance1` in `Council: pallet_collective::`. - pub instance: Option, - /// The declared pallet parts, - /// e.g. `Some([Pallet, Call])` for `System: system::{Pallet, Call}` - /// or `None` for `System: system`. - pub pallet_parts: Option>, - /// The specified parts, either use_parts or exclude_parts. - pub specified_parts: SpecifiedParts, + /// Is this pallet fully expanded? + pub is_expanded: bool, + /// The name of the pallet, e.g.`System` in `System: frame_system`. + pub name: Ident, + /// Optional attributes tagged right above a pallet declaration. + pub attrs: Vec, + /// Optional fixed index, e.g. `MyPallet ... = 3,`. + pub index: Option, + /// The path of the pallet, e.g. `frame_system` in `System: frame_system`. + pub path: PalletPath, + /// The instance of the pallet, e.g. `Instance1` in `Council: pallet_collective::`. + pub instance: Option, + /// The declared pallet parts, + /// e.g. `Some([Pallet, Call])` for `System: system::{Pallet, Call}` + /// or `None` for `System: system`. + pub pallet_parts: Option>, + /// The specified parts, either use_parts or exclude_parts. + pub specified_parts: SpecifiedParts, } /// The possible declaration of pallet parts to use. #[derive(Debug, Clone)] pub enum SpecifiedParts { - /// Use all the pallet parts except those specified. - Exclude(Vec), - /// Use only the specified pallet parts. - Use(Vec), - /// Use the all the pallet parts. - All, + /// Use all the pallet parts except those specified. + Exclude(Vec), + /// Use only the specified pallet parts. + Use(Vec), + /// Use the all the pallet parts. + All, } impl Parse for PalletDeclaration { - fn parse(input: ParseStream) -> Result { - let attrs = input.call(Attribute::parse_outer)?; - - let name = input.parse()?; - let _: Token![:] = input.parse()?; - let path = input.parse()?; - - // Parse for instance. - 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 if !(input.peek(Token![::]) && input.peek3(token::Brace)) && - !input.peek(keyword::expanded) && - !input.peek(keyword::exclude_parts) && - !input.peek(keyword::use_parts) && - !input.peek(Token![=]) && - !input.peek(Token![,]) && - !input.is_empty() - { - return Err(input.error( + fn parse(input: ParseStream) -> Result { + let attrs = input.call(Attribute::parse_outer)?; + + let name = input.parse()?; + let _: Token![:] = input.parse()?; + let path = input.parse()?; + + // Parse for instance. + 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 if !(input.peek(Token![::]) && input.peek3(token::Brace)) + && !input.peek(keyword::expanded) + && !input.peek(keyword::exclude_parts) + && !input.peek(keyword::use_parts) + && !input.peek(Token![=]) + && !input.peek(Token![,]) + && !input.is_empty() + { + return Err(input.error( "Unexpected tokens, expected one of `::$ident` `::{`, `exclude_parts`, `use_parts`, `=`, `,`", )); - } else { - None - }; - - // Check if the pallet is fully expanded. - let (is_expanded, extra_parts) = if input.peek(keyword::expanded) { - let _: keyword::expanded = input.parse()?; - let _: Token![::] = input.parse()?; - (true, parse_pallet_parts(input)?) - } else { - (false, vec![]) - }; - - // Parse for explicit parts - let pallet_parts = if input.peek(Token![::]) && input.peek3(token::Brace) { - let _: Token![::] = input.parse()?; - let mut parts = parse_pallet_parts(input)?; - parts.extend(extra_parts.into_iter()); - Some(parts) - } else if !input.peek(keyword::exclude_parts) && - !input.peek(keyword::use_parts) && - !input.peek(Token![=]) && - !input.peek(Token![,]) && - !input.is_empty() - { - return Err(input.error( - "Unexpected tokens, expected one of `::{`, `exclude_parts`, `use_parts`, `=`, `,`", - )) - } else { - is_expanded.then_some(extra_parts) - }; - - // Parse for specified parts - let specified_parts = if input.peek(keyword::exclude_parts) { - let _: keyword::exclude_parts = input.parse()?; - SpecifiedParts::Exclude(parse_pallet_parts_no_generic(input)?) - } else if input.peek(keyword::use_parts) { - let _: keyword::use_parts = input.parse()?; - SpecifiedParts::Use(parse_pallet_parts_no_generic(input)?) - } else if !input.peek(Token![=]) && !input.peek(Token![,]) && !input.is_empty() { - return Err(input.error("Unexpected tokens, expected one of `exclude_parts`, `=`, `,`")) - } else { - SpecifiedParts::All - }; - - // Parse for pallet index - let index = if input.peek(Token![=]) { - input.parse::()?; - let index = input.parse::()?; - let index = index.base10_parse::()?; - Some(index) - } else if !input.peek(Token![,]) && !input.is_empty() { - return Err(input.error("Unexpected tokens, expected one of `=`, `,`")) - } else { - None - }; - - Ok(Self { is_expanded, attrs, name, path, instance, pallet_parts, specified_parts, index }) - } + } else { + None + }; + + // Check if the pallet is fully expanded. + let (is_expanded, extra_parts) = if input.peek(keyword::expanded) { + let _: keyword::expanded = input.parse()?; + let _: Token![::] = input.parse()?; + (true, parse_pallet_parts(input)?) + } else { + (false, vec![]) + }; + + // Parse for explicit parts + let pallet_parts = if input.peek(Token![::]) && input.peek3(token::Brace) { + let _: Token![::] = input.parse()?; + let mut parts = parse_pallet_parts(input)?; + parts.extend(extra_parts.into_iter()); + Some(parts) + } else if !input.peek(keyword::exclude_parts) + && !input.peek(keyword::use_parts) + && !input.peek(Token![=]) + && !input.peek(Token![,]) + && !input.is_empty() + { + return Err(input.error( + "Unexpected tokens, expected one of `::{`, `exclude_parts`, `use_parts`, `=`, `,`", + )); + } else { + is_expanded.then_some(extra_parts) + }; + + // Parse for specified parts + let specified_parts = if input.peek(keyword::exclude_parts) { + let _: keyword::exclude_parts = input.parse()?; + SpecifiedParts::Exclude(parse_pallet_parts_no_generic(input)?) + } else if input.peek(keyword::use_parts) { + let _: keyword::use_parts = input.parse()?; + SpecifiedParts::Use(parse_pallet_parts_no_generic(input)?) + } else if !input.peek(Token![=]) && !input.peek(Token![,]) && !input.is_empty() { + return Err(input.error("Unexpected tokens, expected one of `exclude_parts`, `=`, `,`")); + } else { + SpecifiedParts::All + }; + + // Parse for pallet index + let index = if input.peek(Token![=]) { + input.parse::()?; + let index = input.parse::()?; + let index = index.base10_parse::()?; + Some(index) + } else if !input.peek(Token![,]) && !input.is_empty() { + return Err(input.error("Unexpected tokens, expected one of `=`, `,`")); + } else { + None + }; + + Ok(Self { + is_expanded, + attrs, + name, + path, + instance, + pallet_parts, + specified_parts, + index, + }) + } } /// A struct representing a path to a pallet. `PalletPath` is almost identical to the standard @@ -325,303 +357,312 @@ impl Parse for PalletDeclaration { /// - Path segments can only consist of identifiers separated by colons #[derive(Debug, Clone)] pub struct PalletPath { - pub inner: Path, + pub inner: Path, } impl PalletPath { - pub fn module_name(&self) -> String { - self.inner.segments.iter().fold(String::new(), |mut acc, segment| { - if !acc.is_empty() { - acc.push_str("::"); - } - acc.push_str(&segment.ident.to_string()); - acc - }) - } + pub fn module_name(&self) -> String { + self.inner + .segments + .iter() + .fold(String::new(), |mut acc, segment| { + if !acc.is_empty() { + acc.push_str("::"); + } + acc.push_str(&segment.ident.to_string()); + acc + }) + } } impl Parse for PalletPath { - fn parse(input: ParseStream) -> Result { - let mut res = - PalletPath { inner: Path { leading_colon: None, segments: Punctuated::new() } }; - - let lookahead = input.lookahead1(); - if lookahead.peek(Token![crate]) || - lookahead.peek(Token![self]) || - lookahead.peek(Token![super]) || - lookahead.peek(Ident) - { - let ident = input.call(Ident::parse_any)?; - res.inner.segments.push(ident.into()); - } else { - return Err(lookahead.error()) - } - - while input.peek(Token![::]) && input.peek3(Ident) { - input.parse::()?; - let ident = input.parse::()?; - res.inner.segments.push(ident.into()); - } - Ok(res) - } + fn parse(input: ParseStream) -> Result { + let mut res = PalletPath { + inner: Path { + leading_colon: None, + segments: Punctuated::new(), + }, + }; + + let lookahead = input.lookahead1(); + if lookahead.peek(Token![crate]) + || lookahead.peek(Token![self]) + || lookahead.peek(Token![super]) + || lookahead.peek(Ident) + { + let ident = input.call(Ident::parse_any)?; + res.inner.segments.push(ident.into()); + } else { + return Err(lookahead.error()); + } + + while input.peek(Token![::]) && input.peek3(Ident) { + input.parse::()?; + let ident = input.parse::()?; + res.inner.segments.push(ident.into()); + } + Ok(res) + } } impl quote::ToTokens for PalletPath { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.inner.to_tokens(tokens); - } + fn to_tokens(&self, tokens: &mut TokenStream) { + self.inner.to_tokens(tokens); + } } /// Parse [`PalletPart`]'s from a braces enclosed list that is split by commas, e.g. /// /// `{ Call, Event }` fn parse_pallet_parts(input: ParseStream) -> Result> { - let pallet_parts: ext::Braces> = input.parse()?; + let pallet_parts: ext::Braces> = input.parse()?; - let mut resolved = HashSet::new(); - for part in pallet_parts.content.inner.iter() { - if !resolved.insert(part.name()) { - let msg = format!( - "`{}` was already declared before. Please remove the duplicate declaration", - part.name(), - ); - return Err(Error::new(part.keyword.span(), msg)) - } - } + let mut resolved = HashSet::new(); + for part in pallet_parts.content.inner.iter() { + if !resolved.insert(part.name()) { + let msg = format!( + "`{}` was already declared before. Please remove the duplicate declaration", + part.name(), + ); + return Err(Error::new(part.keyword.span(), msg)); + } + } - Ok(pallet_parts.content.inner.into_iter().collect()) + Ok(pallet_parts.content.inner.into_iter().collect()) } #[derive(Debug, Clone)] pub enum PalletPartKeyword { - Pallet(keyword::Pallet), - Call(keyword::Call), - Storage(keyword::Storage), - Event(keyword::Event), - Error(keyword::Error), - Config(keyword::Config), - Origin(keyword::Origin), - Inherent(keyword::Inherent), - ValidateUnsigned(keyword::ValidateUnsigned), - FreezeReason(keyword::FreezeReason), - HoldReason(keyword::HoldReason), - Task(keyword::Task), - LockId(keyword::LockId), - SlashReason(keyword::SlashReason), + Pallet(keyword::Pallet), + Call(keyword::Call), + Storage(keyword::Storage), + Event(keyword::Event), + Error(keyword::Error), + Config(keyword::Config), + Origin(keyword::Origin), + Inherent(keyword::Inherent), + ValidateUnsigned(keyword::ValidateUnsigned), + FreezeReason(keyword::FreezeReason), + HoldReason(keyword::HoldReason), + Task(keyword::Task), + LockId(keyword::LockId), + SlashReason(keyword::SlashReason), } impl Parse for PalletPartKeyword { - fn parse(input: ParseStream) -> Result { - let lookahead = input.lookahead1(); - - if lookahead.peek(keyword::Pallet) { - Ok(Self::Pallet(input.parse()?)) - } else if lookahead.peek(keyword::Call) { - Ok(Self::Call(input.parse()?)) - } else if lookahead.peek(keyword::Storage) { - Ok(Self::Storage(input.parse()?)) - } else if lookahead.peek(keyword::Event) { - Ok(Self::Event(input.parse()?)) - } else if lookahead.peek(keyword::Error) { - Ok(Self::Error(input.parse()?)) - } else if lookahead.peek(keyword::Config) { - Ok(Self::Config(input.parse()?)) - } else if lookahead.peek(keyword::Origin) { - Ok(Self::Origin(input.parse()?)) - } else if lookahead.peek(keyword::Inherent) { - Ok(Self::Inherent(input.parse()?)) - } else if lookahead.peek(keyword::ValidateUnsigned) { - Ok(Self::ValidateUnsigned(input.parse()?)) - } else if lookahead.peek(keyword::FreezeReason) { - Ok(Self::FreezeReason(input.parse()?)) - } else if lookahead.peek(keyword::HoldReason) { - Ok(Self::HoldReason(input.parse()?)) - } else if lookahead.peek(keyword::Task) { - Ok(Self::Task(input.parse()?)) - } else if lookahead.peek(keyword::LockId) { - Ok(Self::LockId(input.parse()?)) - } else if lookahead.peek(keyword::SlashReason) { - Ok(Self::SlashReason(input.parse()?)) - } else { - Err(lookahead.error()) - } - } + fn parse(input: ParseStream) -> Result { + let lookahead = input.lookahead1(); + + if lookahead.peek(keyword::Pallet) { + Ok(Self::Pallet(input.parse()?)) + } else if lookahead.peek(keyword::Call) { + Ok(Self::Call(input.parse()?)) + } else if lookahead.peek(keyword::Storage) { + Ok(Self::Storage(input.parse()?)) + } else if lookahead.peek(keyword::Event) { + Ok(Self::Event(input.parse()?)) + } else if lookahead.peek(keyword::Error) { + Ok(Self::Error(input.parse()?)) + } else if lookahead.peek(keyword::Config) { + Ok(Self::Config(input.parse()?)) + } else if lookahead.peek(keyword::Origin) { + Ok(Self::Origin(input.parse()?)) + } else if lookahead.peek(keyword::Inherent) { + Ok(Self::Inherent(input.parse()?)) + } else if lookahead.peek(keyword::ValidateUnsigned) { + Ok(Self::ValidateUnsigned(input.parse()?)) + } else if lookahead.peek(keyword::FreezeReason) { + Ok(Self::FreezeReason(input.parse()?)) + } else if lookahead.peek(keyword::HoldReason) { + Ok(Self::HoldReason(input.parse()?)) + } else if lookahead.peek(keyword::Task) { + Ok(Self::Task(input.parse()?)) + } else if lookahead.peek(keyword::LockId) { + Ok(Self::LockId(input.parse()?)) + } else if lookahead.peek(keyword::SlashReason) { + Ok(Self::SlashReason(input.parse()?)) + } else { + Err(lookahead.error()) + } + } } impl PalletPartKeyword { - /// Returns the name of `Self`. - fn name(&self) -> &'static str { - match self { - Self::Pallet(_) => "Pallet", - Self::Call(_) => "Call", - Self::Storage(_) => "Storage", - Self::Event(_) => "Event", - Self::Error(_) => "Error", - Self::Config(_) => "Config", - Self::Origin(_) => "Origin", - Self::Inherent(_) => "Inherent", - Self::ValidateUnsigned(_) => "ValidateUnsigned", - Self::FreezeReason(_) => "FreezeReason", - Self::HoldReason(_) => "HoldReason", - Self::Task(_) => "Task", - Self::LockId(_) => "LockId", - Self::SlashReason(_) => "SlashReason", - } - } - - /// Returns `true` if this pallet part is allowed to have generic arguments. - fn allows_generic(&self) -> bool { - Self::all_generic_arg().iter().any(|n| *n == self.name()) - } - - /// Returns the names of all pallet parts that allow to have a generic argument. - fn all_generic_arg() -> &'static [&'static str] { - &["Event", "Error", "Origin", "Config", "Task"] - } + /// Returns the name of `Self`. + fn name(&self) -> &'static str { + match self { + Self::Pallet(_) => "Pallet", + Self::Call(_) => "Call", + Self::Storage(_) => "Storage", + Self::Event(_) => "Event", + Self::Error(_) => "Error", + Self::Config(_) => "Config", + Self::Origin(_) => "Origin", + Self::Inherent(_) => "Inherent", + Self::ValidateUnsigned(_) => "ValidateUnsigned", + Self::FreezeReason(_) => "FreezeReason", + Self::HoldReason(_) => "HoldReason", + Self::Task(_) => "Task", + Self::LockId(_) => "LockId", + Self::SlashReason(_) => "SlashReason", + } + } + + /// Returns `true` if this pallet part is allowed to have generic arguments. + fn allows_generic(&self) -> bool { + Self::all_generic_arg().iter().any(|n| *n == self.name()) + } + + /// Returns the names of all pallet parts that allow to have a generic argument. + fn all_generic_arg() -> &'static [&'static str] { + &["Event", "Error", "Origin", "Config", "Task"] + } } impl ToTokens for PalletPartKeyword { - fn to_tokens(&self, tokens: &mut TokenStream) { - match self { - Self::Pallet(inner) => inner.to_tokens(tokens), - Self::Call(inner) => inner.to_tokens(tokens), - Self::Storage(inner) => inner.to_tokens(tokens), - Self::Event(inner) => inner.to_tokens(tokens), - Self::Error(inner) => inner.to_tokens(tokens), - Self::Config(inner) => inner.to_tokens(tokens), - Self::Origin(inner) => inner.to_tokens(tokens), - Self::Inherent(inner) => inner.to_tokens(tokens), - Self::ValidateUnsigned(inner) => inner.to_tokens(tokens), - Self::FreezeReason(inner) => inner.to_tokens(tokens), - Self::HoldReason(inner) => inner.to_tokens(tokens), - Self::Task(inner) => inner.to_tokens(tokens), - Self::LockId(inner) => inner.to_tokens(tokens), - Self::SlashReason(inner) => inner.to_tokens(tokens), - } - } + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + Self::Pallet(inner) => inner.to_tokens(tokens), + Self::Call(inner) => inner.to_tokens(tokens), + Self::Storage(inner) => inner.to_tokens(tokens), + Self::Event(inner) => inner.to_tokens(tokens), + Self::Error(inner) => inner.to_tokens(tokens), + Self::Config(inner) => inner.to_tokens(tokens), + Self::Origin(inner) => inner.to_tokens(tokens), + Self::Inherent(inner) => inner.to_tokens(tokens), + Self::ValidateUnsigned(inner) => inner.to_tokens(tokens), + Self::FreezeReason(inner) => inner.to_tokens(tokens), + Self::HoldReason(inner) => inner.to_tokens(tokens), + Self::Task(inner) => inner.to_tokens(tokens), + Self::LockId(inner) => inner.to_tokens(tokens), + Self::SlashReason(inner) => inner.to_tokens(tokens), + } + } } #[derive(Debug, Clone)] pub struct PalletPart { - pub keyword: PalletPartKeyword, - pub generics: syn::Generics, + pub keyword: PalletPartKeyword, + pub generics: syn::Generics, } impl Parse for PalletPart { - fn parse(input: ParseStream) -> Result { - let keyword: PalletPartKeyword = input.parse()?; - - let generics: syn::Generics = input.parse()?; - if !generics.params.is_empty() && !keyword.allows_generic() { - let valid_generics = PalletPart::format_names(PalletPartKeyword::all_generic_arg()); - let msg = format!( - "`{}` is not allowed to have generics. \ + fn parse(input: ParseStream) -> Result { + let keyword: PalletPartKeyword = input.parse()?; + + let generics: syn::Generics = input.parse()?; + if !generics.params.is_empty() && !keyword.allows_generic() { + let valid_generics = PalletPart::format_names(PalletPartKeyword::all_generic_arg()); + let msg = format!( + "`{}` is not allowed to have generics. \ Only the following pallets are allowed to have generics: {}.", - keyword.name(), - valid_generics, - ); - return Err(syn::Error::new(keyword.span(), msg)) - } + keyword.name(), + valid_generics, + ); + return Err(syn::Error::new(keyword.span(), msg)); + } - Ok(Self { keyword, generics }) - } + Ok(Self { keyword, generics }) + } } impl PalletPart { - pub fn format_names(names: &[&'static str]) -> String { - let res: Vec<_> = names.iter().map(|s| format!("`{}`", s)).collect(); - res.join(", ") - } + pub fn format_names(names: &[&'static str]) -> String { + let res: Vec<_> = names.iter().map(|s| format!("`{}`", s)).collect(); + res.join(", ") + } - /// The name of this pallet part. - pub fn name(&self) -> &'static str { - self.keyword.name() - } + /// The name of this pallet part. + pub fn name(&self) -> &'static str { + self.keyword.name() + } } fn remove_kind( - input: ParseStream, - kind: WhereKind, - definitions: &mut Vec, + 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)) - } + 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)) + } } /// The declaration of a part without its generics #[derive(Debug, Clone)] pub struct PalletPartNoGeneric { - keyword: PalletPartKeyword, + keyword: PalletPartKeyword, } impl Parse for PalletPartNoGeneric { - fn parse(input: ParseStream) -> Result { - Ok(Self { keyword: input.parse()? }) - } + fn parse(input: ParseStream) -> Result { + Ok(Self { + keyword: input.parse()?, + }) + } } /// Parse [`PalletPartNoGeneric`]'s from a braces enclosed list that is split by commas, e.g. /// /// `{ Call, Event }` fn parse_pallet_parts_no_generic(input: ParseStream) -> Result> { - let pallet_parts: ext::Braces> = - input.parse()?; + let pallet_parts: ext::Braces> = + input.parse()?; - let mut resolved = HashSet::new(); - for part in pallet_parts.content.inner.iter() { - if !resolved.insert(part.keyword.name()) { - let msg = format!( - "`{}` was already declared before. Please remove the duplicate declaration", - part.keyword.name(), - ); - return Err(Error::new(part.keyword.span(), msg)) - } - } + let mut resolved = HashSet::new(); + for part in pallet_parts.content.inner.iter() { + if !resolved.insert(part.keyword.name()) { + let msg = format!( + "`{}` was already declared before. Please remove the duplicate declaration", + part.keyword.name(), + ); + return Err(Error::new(part.keyword.span(), msg)); + } + } - Ok(pallet_parts.content.inner.into_iter().collect()) + Ok(pallet_parts.content.inner.into_iter().collect()) } /// The final definition of a pallet with the resulting fixed index and explicit parts. #[derive(Debug, Clone)] pub struct Pallet { - /// Is this pallet fully expanded? - pub is_expanded: bool, - /// The name of the pallet, e.g.`System` in `System: frame_system`. - pub name: Ident, - /// Either automatically inferred, or defined (e.g. `MyPallet ... = 3,`). - pub index: u8, - /// The path of the pallet, e.g. `frame_system` in `System: frame_system`. - pub path: PalletPath, - /// The instance of the pallet, e.g. `Instance1` in `Council: pallet_collective::`. - pub instance: Option, - /// The pallet parts to use for the pallet. - pub pallet_parts: Vec, - /// Expressions specified inside of a #[cfg] attribute. - pub cfg_pattern: Vec, + /// Is this pallet fully expanded? + pub is_expanded: bool, + /// The name of the pallet, e.g.`System` in `System: frame_system`. + pub name: Ident, + /// Either automatically inferred, or defined (e.g. `MyPallet ... = 3,`). + pub index: u8, + /// The path of the pallet, e.g. `frame_system` in `System: frame_system`. + pub path: PalletPath, + /// The instance of the pallet, e.g. `Instance1` in `Council: pallet_collective::`. + pub instance: Option, + /// The pallet parts to use for the pallet. + pub pallet_parts: Vec, + /// Expressions specified inside of a #[cfg] attribute. + pub cfg_pattern: Vec, } impl Pallet { - /// Get resolved pallet parts - pub fn pallet_parts(&self) -> &[PalletPart] { - &self.pallet_parts - } + /// Get resolved pallet parts + pub fn pallet_parts(&self) -> &[PalletPart] { + &self.pallet_parts + } - /// Find matching parts - pub fn find_part(&self, name: &str) -> Option<&PalletPart> { - self.pallet_parts.iter().find(|part| part.name() == name) - } + /// Find matching parts + pub fn find_part(&self, name: &str) -> Option<&PalletPart> { + self.pallet_parts.iter().find(|part| part.name() == name) + } - /// Return whether pallet contains part - pub fn exists_part(&self, name: &str) -> bool { - self.find_part(name).is_some() - } + /// Return whether pallet contains part + pub fn exists_part(&self, name: &str) -> bool { + self.find_part(name).is_some() + } } /// Result of a conversion of a declaration of pallets. @@ -634,26 +675,26 @@ impl Pallet { /// +----------+ +----------+ +------------------+ /// ``` enum PalletsConversion { - /// Pallets implicitly declare parts. - /// - /// `System: frame_system`. - Implicit(Vec), - /// Pallets explicitly declare parts. - /// - /// `System: frame_system::{Pallet, Call}` - /// - /// However, for backwards compatibility with Polkadot/Kusama - /// we must propagate some other parts to the pallet by default. - Explicit(Vec), - /// Pallets explicitly declare parts that are fully expanded. - /// - /// This is the end state that contains extra parts included by - /// default by Substrate. - /// - /// `System: frame_system expanded::{Error} ::{Pallet, Call}` - /// - /// For this example, the `Pallet`, `Call` and `Error` parts are collected. - ExplicitExpanded(Vec), + /// Pallets implicitly declare parts. + /// + /// `System: frame_system`. + Implicit(Vec), + /// Pallets explicitly declare parts. + /// + /// `System: frame_system::{Pallet, Call}` + /// + /// However, for backwards compatibility with Polkadot/Kusama + /// we must propagate some other parts to the pallet by default. + Explicit(Vec), + /// Pallets explicitly declare parts that are fully expanded. + /// + /// This is the end state that contains extra parts included by + /// default by Substrate. + /// + /// `System: frame_system expanded::{Error} ::{Pallet, Call}` + /// + /// For this example, the `Pallet`, `Call` and `Error` parts are collected. + ExplicitExpanded(Vec), } /// Convert from the parsed pallet declaration to their final information. @@ -662,125 +703,137 @@ enum PalletsConversion { /// pallet using same rules as rust for fieldless enum. I.e. implicit are assigned number /// incrementally from last explicit or 0. fn convert_pallets(pallets: Vec) -> syn::Result { - if pallets.iter().any(|pallet| pallet.pallet_parts.is_none()) { - return Ok(PalletsConversion::Implicit(pallets)) - } - - let mut indices = HashMap::new(); - let mut last_index: Option = None; - let mut names = HashMap::new(); - let mut is_expanded = true; - - let pallets = pallets - .into_iter() - .map(|pallet| { - let final_index = match pallet.index { - Some(i) => i, - None => last_index.map_or(Some(0), |i| i.checked_add(1)).ok_or_else(|| { - let msg = "Pallet index doesn't fit into u8, index is 256"; - syn::Error::new(pallet.name.span(), msg) - })?, - }; - - last_index = Some(final_index); - - if let Some(used_pallet) = indices.insert(final_index, pallet.name.clone()) { - let msg = format!( - "Pallet indices are conflicting: Both pallets {} and {} are at index {}", - used_pallet, pallet.name, final_index, - ); - let mut err = syn::Error::new(used_pallet.span(), &msg); - err.combine(syn::Error::new(pallet.name.span(), msg)); - return Err(err) - } - - if let Some(used_pallet) = names.insert(pallet.name.clone(), pallet.name.span()) { - let msg = "Two pallets with the same name!"; - - let mut err = syn::Error::new(used_pallet, &msg); - err.combine(syn::Error::new(pallet.name.span(), &msg)); - return Err(err) - } - - let mut pallet_parts = pallet.pallet_parts.expect("Checked above"); - - let available_parts = - pallet_parts.iter().map(|part| part.keyword.name()).collect::>(); - - // Check parts are correctly specified - match &pallet.specified_parts { - SpecifiedParts::Exclude(parts) | SpecifiedParts::Use(parts) => - for part in parts { - if !available_parts.contains(part.keyword.name()) { - let msg = format!( - "Invalid pallet part specified, the pallet `{}` doesn't have the \ + if pallets.iter().any(|pallet| pallet.pallet_parts.is_none()) { + return Ok(PalletsConversion::Implicit(pallets)); + } + + let mut indices = HashMap::new(); + let mut last_index: Option = None; + let mut names = HashMap::new(); + let mut is_expanded = true; + + let pallets = pallets + .into_iter() + .map(|pallet| { + let final_index = match pallet.index { + Some(i) => i, + None => last_index + .map_or(Some(0), |i| i.checked_add(1)) + .ok_or_else(|| { + let msg = "Pallet index doesn't fit into u8, index is 256"; + syn::Error::new(pallet.name.span(), msg) + })?, + }; + + last_index = Some(final_index); + + if let Some(used_pallet) = indices.insert(final_index, pallet.name.clone()) { + let msg = format!( + "Pallet indices are conflicting: Both pallets {} and {} are at index {}", + used_pallet, pallet.name, final_index, + ); + let mut err = syn::Error::new(used_pallet.span(), &msg); + err.combine(syn::Error::new(pallet.name.span(), msg)); + return Err(err); + } + + if let Some(used_pallet) = names.insert(pallet.name.clone(), pallet.name.span()) { + let msg = "Two pallets with the same name!"; + + let mut err = syn::Error::new(used_pallet, &msg); + err.combine(syn::Error::new(pallet.name.span(), &msg)); + return Err(err); + } + + let mut pallet_parts = pallet.pallet_parts.expect("Checked above"); + + let available_parts = pallet_parts + .iter() + .map(|part| part.keyword.name()) + .collect::>(); + + // Check parts are correctly specified + match &pallet.specified_parts { + SpecifiedParts::Exclude(parts) | SpecifiedParts::Use(parts) => { + for part in parts { + if !available_parts.contains(part.keyword.name()) { + let msg = format!( + "Invalid pallet part specified, the pallet `{}` doesn't have the \ `{}` part. Available parts are: {}.", - pallet.name, - part.keyword.name(), - pallet_parts.iter().fold(String::new(), |fold, part| { - if fold.is_empty() { - format!("`{}`", part.keyword.name()) - } else { - format!("{}, `{}`", fold, part.keyword.name()) - } - }) - ); - return Err(syn::Error::new(part.keyword.span(), msg)) - } - }, - SpecifiedParts::All => (), - } - - // Set only specified parts. - match pallet.specified_parts { - SpecifiedParts::Exclude(excluded_parts) => pallet_parts.retain(|part| { - !excluded_parts - .iter() - .any(|excluded_part| excluded_part.keyword.name() == part.keyword.name()) - }), - SpecifiedParts::Use(used_parts) => pallet_parts.retain(|part| { - used_parts.iter().any(|use_part| use_part.keyword.name() == part.keyword.name()) - }), - SpecifiedParts::All => (), - } - - let cfg_pattern = pallet - .attrs - .iter() - .map(|attr| { - if attr.path().segments.first().map_or(false, |s| s.ident != "cfg") { - let msg = "Unsupported attribute, only #[cfg] is supported on pallet \ + pallet.name, + part.keyword.name(), + pallet_parts.iter().fold(String::new(), |fold, part| { + if fold.is_empty() { + format!("`{}`", part.keyword.name()) + } else { + format!("{}, `{}`", fold, part.keyword.name()) + } + }) + ); + return Err(syn::Error::new(part.keyword.span(), msg)); + } + } + } + SpecifiedParts::All => (), + } + + // Set only specified parts. + match pallet.specified_parts { + SpecifiedParts::Exclude(excluded_parts) => pallet_parts.retain(|part| { + !excluded_parts + .iter() + .any(|excluded_part| excluded_part.keyword.name() == part.keyword.name()) + }), + SpecifiedParts::Use(used_parts) => pallet_parts.retain(|part| { + used_parts + .iter() + .any(|use_part| use_part.keyword.name() == part.keyword.name()) + }), + SpecifiedParts::All => (), + } + + let cfg_pattern = pallet + .attrs + .iter() + .map(|attr| { + if attr + .path() + .segments + .first() + .map_or(false, |s| s.ident != "cfg") + { + let msg = "Unsupported attribute, only #[cfg] is supported on pallet \ declarations in `construct_runtime`"; - return Err(syn::Error::new(attr.span(), msg)) - } - - attr.parse_args_with(|input: syn::parse::ParseStream| { - // Required, otherwise the parse stream doesn't advance and will result in - // an error. - let input = input.parse::()?; - cfg_expr::Expression::parse(&input.to_string()) - .map_err(|e| syn::Error::new(attr.span(), e.to_string())) - }) - }) - .collect::>>()?; - - is_expanded &= pallet.is_expanded; - - Ok(Pallet { - is_expanded: pallet.is_expanded, - name: pallet.name, - index: final_index, - path: pallet.path, - instance: pallet.instance, - cfg_pattern, - pallet_parts, - }) - }) - .collect::>>()?; - - if is_expanded { - Ok(PalletsConversion::ExplicitExpanded(pallets)) - } else { - Ok(PalletsConversion::Explicit(pallets)) - } + return Err(syn::Error::new(attr.span(), msg)); + } + + attr.parse_args_with(|input: syn::parse::ParseStream| { + // Required, otherwise the parse stream doesn't advance and will result in + // an error. + let input = input.parse::()?; + cfg_expr::Expression::parse(&input.to_string()) + .map_err(|e| syn::Error::new(attr.span(), e.to_string())) + }) + }) + .collect::>>()?; + + is_expanded &= pallet.is_expanded; + + Ok(Pallet { + is_expanded: pallet.is_expanded, + name: pallet.name, + index: final_index, + path: pallet.path, + instance: pallet.instance, + cfg_pattern, + pallet_parts, + }) + }) + .collect::>>()?; + + if is_expanded { + Ok(PalletsConversion::ExplicitExpanded(pallets)) + } else { + Ok(PalletsConversion::Explicit(pallets)) + } } diff --git a/support/procedural-fork/src/crate_version.rs b/support/procedural-fork/src/crate_version.rs index 8c8975a42..63e7c7279 100644 --- a/support/procedural-fork/src/crate_version.rs +++ b/support/procedural-fork/src/crate_version.rs @@ -24,31 +24,31 @@ use syn::{Error, Result}; /// Create an error that will be shown by rustc at the call site of the macro. fn create_error(message: &str) -> Error { - Error::new(Span::call_site(), message) + Error::new(Span::call_site(), message) } /// Implementation of the `crate_to_crate_version!` macro. pub fn crate_to_crate_version(input: proc_macro::TokenStream) -> Result { - if !input.is_empty() { - return Err(create_error("No arguments expected!")) - } + if !input.is_empty() { + return Err(create_error("No arguments expected!")); + } - let major_version = get_cargo_env_var::("CARGO_PKG_VERSION_MAJOR") - .map_err(|_| create_error("Major version needs to fit into `u16`"))?; + let major_version = get_cargo_env_var::("CARGO_PKG_VERSION_MAJOR") + .map_err(|_| create_error("Major version needs to fit into `u16`"))?; - let minor_version = get_cargo_env_var::("CARGO_PKG_VERSION_MINOR") - .map_err(|_| create_error("Minor version needs to fit into `u8`"))?; + let minor_version = get_cargo_env_var::("CARGO_PKG_VERSION_MINOR") + .map_err(|_| create_error("Minor version needs to fit into `u8`"))?; - let patch_version = get_cargo_env_var::("CARGO_PKG_VERSION_PATCH") - .map_err(|_| create_error("Patch version needs to fit into `u8`"))?; + let patch_version = get_cargo_env_var::("CARGO_PKG_VERSION_PATCH") + .map_err(|_| create_error("Patch version needs to fit into `u8`"))?; - let crate_ = generate_access_from_frame_or_crate("frame-support")?; + let crate_ = generate_access_from_frame_or_crate("frame-support")?; - Ok(quote::quote! { - #crate_::traits::CrateVersion { - major: #major_version, - minor: #minor_version, - patch: #patch_version, - } - }) + Ok(quote::quote! { + #crate_::traits::CrateVersion { + major: #major_version, + minor: #minor_version, + patch: #patch_version, + } + }) } diff --git a/support/procedural-fork/src/dummy_part_checker.rs b/support/procedural-fork/src/dummy_part_checker.rs index 34d9a3e23..6bed541d1 100644 --- a/support/procedural-fork/src/dummy_part_checker.rs +++ b/support/procedural-fork/src/dummy_part_checker.rs @@ -19,61 +19,63 @@ use crate::COUNTER; use proc_macro::TokenStream; pub fn generate_dummy_part_checker(input: TokenStream) -> TokenStream { - if !input.is_empty() { - return syn::Error::new(proc_macro2::Span::call_site(), "No arguments expected") - .to_compile_error() - .into() - } + if !input.is_empty() { + return syn::Error::new(proc_macro2::Span::call_site(), "No arguments expected") + .to_compile_error() + .into(); + } - let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); - let no_op_macro_ident = - syn::Ident::new(&format!("__dummy_part_checker_{}", count), proc_macro2::Span::call_site()); + let no_op_macro_ident = syn::Ident::new( + &format!("__dummy_part_checker_{}", count), + proc_macro2::Span::call_site(), + ); - quote::quote!( - #[macro_export] - #[doc(hidden)] - macro_rules! #no_op_macro_ident { - ( $( $tt:tt )* ) => {}; - } + quote::quote!( + #[macro_export] + #[doc(hidden)] + macro_rules! #no_op_macro_ident { + ( $( $tt:tt )* ) => {}; + } - #[doc(hidden)] - pub mod __substrate_genesis_config_check { - #[doc(hidden)] - pub use #no_op_macro_ident as is_genesis_config_defined; - #[doc(hidden)] - pub use #no_op_macro_ident as is_std_enabled_for_genesis; - } + #[doc(hidden)] + pub mod __substrate_genesis_config_check { + #[doc(hidden)] + pub use #no_op_macro_ident as is_genesis_config_defined; + #[doc(hidden)] + pub use #no_op_macro_ident as is_std_enabled_for_genesis; + } - #[doc(hidden)] - pub mod __substrate_event_check { - #[doc(hidden)] - pub use #no_op_macro_ident as is_event_part_defined; - } + #[doc(hidden)] + pub mod __substrate_event_check { + #[doc(hidden)] + pub use #no_op_macro_ident as is_event_part_defined; + } - #[doc(hidden)] - pub mod __substrate_inherent_check { - #[doc(hidden)] - pub use #no_op_macro_ident as is_inherent_part_defined; - } + #[doc(hidden)] + pub mod __substrate_inherent_check { + #[doc(hidden)] + pub use #no_op_macro_ident as is_inherent_part_defined; + } - #[doc(hidden)] - pub mod __substrate_validate_unsigned_check { - #[doc(hidden)] - pub use #no_op_macro_ident as is_validate_unsigned_part_defined; - } + #[doc(hidden)] + pub mod __substrate_validate_unsigned_check { + #[doc(hidden)] + pub use #no_op_macro_ident as is_validate_unsigned_part_defined; + } - #[doc(hidden)] - pub mod __substrate_call_check { - #[doc(hidden)] - pub use #no_op_macro_ident as is_call_part_defined; - } + #[doc(hidden)] + pub mod __substrate_call_check { + #[doc(hidden)] + pub use #no_op_macro_ident as is_call_part_defined; + } - #[doc(hidden)] - pub mod __substrate_origin_check { - #[doc(hidden)] - pub use #no_op_macro_ident as is_origin_part_defined; - } - ) - .into() + #[doc(hidden)] + pub mod __substrate_origin_check { + #[doc(hidden)] + pub use #no_op_macro_ident as is_origin_part_defined; + } + ) + .into() } diff --git a/support/procedural-fork/src/dynamic_params.rs b/support/procedural-fork/src/dynamic_params.rs index 29399a885..70a18bf34 100644 --- a/support/procedural-fork/src/dynamic_params.rs +++ b/support/procedural-fork/src/dynamic_params.rs @@ -26,123 +26,132 @@ use syn::{parse2, spanned::Spanned, visit_mut, visit_mut::VisitMut, Result, Toke /// Parse and expand a `#[dynamic_params(..)]` module. pub fn dynamic_params(attr: TokenStream, item: TokenStream) -> Result { - DynamicParamModAttr::parse(attr, item).map(ToTokens::into_token_stream) + DynamicParamModAttr::parse(attr, item).map(ToTokens::into_token_stream) } /// Parse and expand `#[dynamic_pallet_params(..)]` attribute. pub fn dynamic_pallet_params(attr: TokenStream, item: TokenStream) -> Result { - DynamicPalletParamAttr::parse(attr, item).map(ToTokens::into_token_stream) + DynamicPalletParamAttr::parse(attr, item).map(ToTokens::into_token_stream) } /// Parse and expand `#[dynamic_aggregated_params_internal]` attribute. pub fn dynamic_aggregated_params_internal( - _attr: TokenStream, - item: TokenStream, + _attr: TokenStream, + item: TokenStream, ) -> Result { - parse2::(item).map(ToTokens::into_token_stream) + parse2::(item).map(ToTokens::into_token_stream) } /// A top `#[dynamic_params(..)]` attribute together with a mod. #[derive(derive_syn_parse::Parse)] pub struct DynamicParamModAttr { - params_mod: syn::ItemMod, - meta: DynamicParamModAttrMeta, + params_mod: syn::ItemMod, + meta: DynamicParamModAttrMeta, } /// The inner meta of a `#[dynamic_params(..)]` attribute. #[derive(derive_syn_parse::Parse)] pub struct DynamicParamModAttrMeta { - name: syn::Ident, - _comma: Option, - #[parse_if(_comma.is_some())] - params_pallet: Option, + name: syn::Ident, + _comma: Option, + #[parse_if(_comma.is_some())] + params_pallet: Option, } impl DynamicParamModAttr { - pub fn parse(attr: TokenStream, item: TokenStream) -> Result { - let params_mod = parse2(item)?; - let meta = parse2(attr)?; - Ok(Self { params_mod, meta }) - } - - pub fn inner_mods(&self) -> Vec { - self.params_mod.content.as_ref().map_or(Vec::new(), |(_, items)| { - items - .iter() - .filter_map(|i| match i { - syn::Item::Mod(m) => Some(m), - _ => None, - }) - .cloned() - .collect() - }) - } + pub fn parse(attr: TokenStream, item: TokenStream) -> Result { + let params_mod = parse2(item)?; + let meta = parse2(attr)?; + Ok(Self { params_mod, meta }) + } + + pub fn inner_mods(&self) -> Vec { + self.params_mod + .content + .as_ref() + .map_or(Vec::new(), |(_, items)| { + items + .iter() + .filter_map(|i| match i { + syn::Item::Mod(m) => Some(m), + _ => None, + }) + .cloned() + .collect() + }) + } } impl ToTokens for DynamicParamModAttr { - fn to_tokens(&self, tokens: &mut TokenStream) { - let scrate = match crate_access() { - Ok(path) => path, - Err(err) => return tokens.extend(err), - }; - let (mut params_mod, name) = (self.params_mod.clone(), &self.meta.name); - let dynam_params_ident = ¶ms_mod.ident; - - let mut quoted_enum = quote! {}; - for m in self.inner_mods() { - let aggregate_name = - syn::Ident::new(&m.ident.to_string().to_class_case(), m.ident.span()); - let mod_name = &m.ident; - - let mut attrs = m.attrs.clone(); - attrs.retain(|attr| !attr.path().is_ident("dynamic_pallet_params")); - if let Err(err) = ensure_codec_index(&attrs, m.span()) { - tokens.extend(err.into_compile_error()); - return - } - - quoted_enum.extend(quote! { - #(#attrs)* - #aggregate_name(#dynam_params_ident::#mod_name::Parameters), - }); - } - - // Inject the outer args into the inner `#[dynamic_pallet_params(..)]` attribute. - if let Some(params_pallet) = &self.meta.params_pallet { - MacroInjectArgs { runtime_params: name.clone(), params_pallet: params_pallet.clone() } - .visit_item_mod_mut(&mut params_mod); - } - - tokens.extend(quote! { - #params_mod - - #[#scrate::dynamic_params::dynamic_aggregated_params_internal] - pub enum #name { - #quoted_enum - } - }); - } + fn to_tokens(&self, tokens: &mut TokenStream) { + let scrate = match crate_access() { + Ok(path) => path, + Err(err) => return tokens.extend(err), + }; + let (mut params_mod, name) = (self.params_mod.clone(), &self.meta.name); + let dynam_params_ident = ¶ms_mod.ident; + + let mut quoted_enum = quote! {}; + for m in self.inner_mods() { + let aggregate_name = + syn::Ident::new(&m.ident.to_string().to_class_case(), m.ident.span()); + let mod_name = &m.ident; + + let mut attrs = m.attrs.clone(); + attrs.retain(|attr| !attr.path().is_ident("dynamic_pallet_params")); + if let Err(err) = ensure_codec_index(&attrs, m.span()) { + tokens.extend(err.into_compile_error()); + return; + } + + quoted_enum.extend(quote! { + #(#attrs)* + #aggregate_name(#dynam_params_ident::#mod_name::Parameters), + }); + } + + // Inject the outer args into the inner `#[dynamic_pallet_params(..)]` attribute. + if let Some(params_pallet) = &self.meta.params_pallet { + MacroInjectArgs { + runtime_params: name.clone(), + params_pallet: params_pallet.clone(), + } + .visit_item_mod_mut(&mut params_mod); + } + + tokens.extend(quote! { + #params_mod + + #[#scrate::dynamic_params::dynamic_aggregated_params_internal] + pub enum #name { + #quoted_enum + } + }); + } } /// Ensure there is a `#[codec(index = ..)]` attribute. fn ensure_codec_index(attrs: &Vec, span: Span) -> Result<()> { - let mut found = false; - - for attr in attrs.iter() { - if attr.path().is_ident("codec") { - let meta: syn::ExprAssign = attr.parse_args()?; - if meta.left.to_token_stream().to_string() == "index" { - found = true; - break - } - } - } - - if !found { - Err(syn::Error::new(span, "Missing explicit `#[codec(index = ..)]` attribute")) - } else { - Ok(()) - } + let mut found = false; + + for attr in attrs.iter() { + if attr.path().is_ident("codec") { + let meta: syn::ExprAssign = attr.parse_args()?; + if meta.left.to_token_stream().to_string() == "index" { + found = true; + break; + } + } + } + + if !found { + Err(syn::Error::new( + span, + "Missing explicit `#[codec(index = ..)]` attribute", + )) + } else { + Ok(()) + } } /// Used to inject arguments into the inner `#[dynamic_pallet_params(..)]` attribute. @@ -150,110 +159,125 @@ fn ensure_codec_index(attrs: &Vec, span: Span) -> Result<()> { /// This allows the outer `#[dynamic_params(..)]` attribute to specify some arguments that don't /// need to be repeated every time. struct MacroInjectArgs { - runtime_params: syn::Ident, - params_pallet: syn::Type, + runtime_params: syn::Ident, + params_pallet: syn::Type, } impl VisitMut for MacroInjectArgs { - fn visit_item_mod_mut(&mut self, item: &mut syn::ItemMod) { - // Check if the mod has a `#[dynamic_pallet_params(..)]` attribute. - let attr = item.attrs.iter_mut().find(|attr| attr.path().is_ident("dynamic_pallet_params")); - - if let Some(attr) = attr { - match &attr.meta { - syn::Meta::Path(path) => - assert_eq!(path.to_token_stream().to_string(), "dynamic_pallet_params"), - _ => (), - } - - let runtime_params = &self.runtime_params; - let params_pallet = &self.params_pallet; - - attr.meta = syn::parse2::(quote! { - dynamic_pallet_params(#runtime_params, #params_pallet) - }) - .unwrap() - .into(); - } - - visit_mut::visit_item_mod_mut(self, item); - } + fn visit_item_mod_mut(&mut self, item: &mut syn::ItemMod) { + // Check if the mod has a `#[dynamic_pallet_params(..)]` attribute. + let attr = item + .attrs + .iter_mut() + .find(|attr| attr.path().is_ident("dynamic_pallet_params")); + + if let Some(attr) = attr { + match &attr.meta { + syn::Meta::Path(path) => { + assert_eq!(path.to_token_stream().to_string(), "dynamic_pallet_params") + } + _ => (), + } + + let runtime_params = &self.runtime_params; + let params_pallet = &self.params_pallet; + + attr.meta = syn::parse2::(quote! { + dynamic_pallet_params(#runtime_params, #params_pallet) + }) + .unwrap() + .into(); + } + + visit_mut::visit_item_mod_mut(self, item); + } } /// The helper attribute of a `#[dynamic_pallet_params(runtime_params, params_pallet)]` /// attribute. #[derive(derive_syn_parse::Parse)] pub struct DynamicPalletParamAttr { - inner_mod: syn::ItemMod, - meta: DynamicPalletParamAttrMeta, + inner_mod: syn::ItemMod, + meta: DynamicPalletParamAttrMeta, } /// The inner meta of a `#[dynamic_pallet_params(..)]` attribute. #[derive(derive_syn_parse::Parse)] pub struct DynamicPalletParamAttrMeta { - runtime_params: syn::Ident, - _comma: Token![,], - parameter_pallet: syn::Type, + runtime_params: syn::Ident, + _comma: Token![,], + parameter_pallet: syn::Type, } impl DynamicPalletParamAttr { - pub fn parse(attr: TokenStream, item: TokenStream) -> Result { - Ok(Self { inner_mod: parse2(item)?, meta: parse2(attr)? }) - } - - pub fn statics(&self) -> Vec { - self.inner_mod.content.as_ref().map_or(Vec::new(), |(_, items)| { - items - .iter() - .filter_map(|i| match i { - syn::Item::Static(s) => Some(s), - _ => None, - }) - .cloned() - .collect() - }) - } + pub fn parse(attr: TokenStream, item: TokenStream) -> Result { + Ok(Self { + inner_mod: parse2(item)?, + meta: parse2(attr)?, + }) + } + + pub fn statics(&self) -> Vec { + self.inner_mod + .content + .as_ref() + .map_or(Vec::new(), |(_, items)| { + items + .iter() + .filter_map(|i| match i { + syn::Item::Static(s) => Some(s), + _ => None, + }) + .cloned() + .collect() + }) + } } impl ToTokens for DynamicPalletParamAttr { - fn to_tokens(&self, tokens: &mut TokenStream) { - let scrate = match crate_access() { - Ok(path) => path, - Err(err) => return tokens.extend(err), - }; - let (params_mod, parameter_pallet, runtime_params) = - (&self.inner_mod, &self.meta.parameter_pallet, &self.meta.runtime_params); - - let aggregate_name = - syn::Ident::new(¶ms_mod.ident.to_string().to_class_case(), params_mod.ident.span()); - let (mod_name, vis) = (¶ms_mod.ident, ¶ms_mod.vis); - let statics = self.statics(); - - let (mut key_names, mut key_values, mut defaults, mut attrs, mut value_types): ( - Vec<_>, - Vec<_>, - Vec<_>, - Vec<_>, - Vec<_>, - ) = Default::default(); - - for s in statics.iter() { - if let Err(err) = ensure_codec_index(&s.attrs, s.span()) { - tokens.extend(err.into_compile_error()); - return - } - - key_names.push(&s.ident); - key_values.push(format_ident!("{}Value", &s.ident)); - defaults.push(&s.expr); - attrs.push(&s.attrs); - value_types.push(&s.ty); - } - - let key_ident = syn::Ident::new("ParametersKey", params_mod.ident.span()); - let value_ident = syn::Ident::new("ParametersValue", params_mod.ident.span()); - let runtime_key_ident = format_ident!("{}Key", runtime_params); - let runtime_value_ident = format_ident!("{}Value", runtime_params); - - tokens.extend(quote! { + fn to_tokens(&self, tokens: &mut TokenStream) { + let scrate = match crate_access() { + Ok(path) => path, + Err(err) => return tokens.extend(err), + }; + let (params_mod, parameter_pallet, runtime_params) = ( + &self.inner_mod, + &self.meta.parameter_pallet, + &self.meta.runtime_params, + ); + + let aggregate_name = syn::Ident::new( + ¶ms_mod.ident.to_string().to_class_case(), + params_mod.ident.span(), + ); + let (mod_name, vis) = (¶ms_mod.ident, ¶ms_mod.vis); + let statics = self.statics(); + + let (mut key_names, mut key_values, mut defaults, mut attrs, mut value_types): ( + Vec<_>, + Vec<_>, + Vec<_>, + Vec<_>, + Vec<_>, + ) = Default::default(); + + for s in statics.iter() { + if let Err(err) = ensure_codec_index(&s.attrs, s.span()) { + tokens.extend(err.into_compile_error()); + return; + } + + key_names.push(&s.ident); + key_values.push(format_ident!("{}Value", &s.ident)); + defaults.push(&s.expr); + attrs.push(&s.attrs); + value_types.push(&s.ty); + } + + let key_ident = syn::Ident::new("ParametersKey", params_mod.ident.span()); + let value_ident = syn::Ident::new("ParametersValue", params_mod.ident.span()); + let runtime_key_ident = format_ident!("{}Key", runtime_params); + let runtime_value_ident = format_ident!("{}Value", runtime_params); + + tokens.extend(quote! { pub mod #mod_name { use super::*; @@ -426,44 +450,44 @@ impl ToTokens for DynamicPalletParamAttr { )* } }); - } + } } #[derive(derive_syn_parse::Parse)] pub struct DynamicParamAggregatedEnum { - aggregated_enum: syn::ItemEnum, + aggregated_enum: syn::ItemEnum, } impl ToTokens for DynamicParamAggregatedEnum { - fn to_tokens(&self, tokens: &mut TokenStream) { - let scrate = match crate_access() { - Ok(path) => path, - Err(err) => return tokens.extend(err), - }; - let params_enum = &self.aggregated_enum; - let (name, vis) = (¶ms_enum.ident, ¶ms_enum.vis); - - let (mut indices, mut param_names, mut param_types): (Vec<_>, Vec<_>, Vec<_>) = - Default::default(); - let mut attributes = Vec::new(); - for (i, variant) in params_enum.variants.iter().enumerate() { - indices.push(i); - param_names.push(&variant.ident); - attributes.push(&variant.attrs); - - param_types.push(match &variant.fields { + fn to_tokens(&self, tokens: &mut TokenStream) { + let scrate = match crate_access() { + Ok(path) => path, + Err(err) => return tokens.extend(err), + }; + let params_enum = &self.aggregated_enum; + let (name, vis) = (¶ms_enum.ident, ¶ms_enum.vis); + + let (mut indices, mut param_names, mut param_types): (Vec<_>, Vec<_>, Vec<_>) = + Default::default(); + let mut attributes = Vec::new(); + for (i, variant) in params_enum.variants.iter().enumerate() { + indices.push(i); + param_names.push(&variant.ident); + attributes.push(&variant.attrs); + + param_types.push(match &variant.fields { syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty, _ => { *tokens = quote! { compile_error!("Only unnamed enum variants with one inner item are supported") }; return }, }); - } + } - let params_key_ident = format_ident!("{}Key", params_enum.ident); - let params_value_ident = format_ident!("{}Value", params_enum.ident); + let params_key_ident = format_ident!("{}Key", params_enum.ident); + let params_value_ident = format_ident!("{}Value", params_enum.ident); - tokens.extend(quote! { + tokens.extend(quote! { #[doc(hidden)] #[derive( Clone, @@ -554,10 +578,10 @@ impl ToTokens for DynamicParamAggregatedEnum { } )* }); - } + } } /// Get access to the current crate and convert the error to a compile error. fn crate_access() -> core::result::Result { - generate_access_from_frame_or_crate("frame-support").map_err(|e| e.to_compile_error()) + generate_access_from_frame_or_crate("frame-support").map_err(|e| e.to_compile_error()) } diff --git a/support/procedural-fork/src/key_prefix.rs b/support/procedural-fork/src/key_prefix.rs index 7f1ab6866..aea60ce3b 100644 --- a/support/procedural-fork/src/key_prefix.rs +++ b/support/procedural-fork/src/key_prefix.rs @@ -23,82 +23,84 @@ use syn::{Ident, Result}; const MAX_IDENTS: usize = 18; pub fn impl_key_prefix_for_tuples(input: proc_macro::TokenStream) -> Result { - if !input.is_empty() { - return Err(syn::Error::new(Span::call_site(), "No arguments expected")) - } - - let mut all_trait_impls = TokenStream::new(); - let frame_support = generate_access_from_frame_or_crate("frame-support")?; - - for i in 2..=MAX_IDENTS { - let current_tuple = (0..i) - .map(|n| Ident::new(&format!("Tuple{}", n), Span::call_site())) - .collect::>(); - - for prefix_count in 1..i { - let (prefixes, suffixes) = current_tuple.split_at(prefix_count); - - let hashers = current_tuple - .iter() - .map(|ident| format_ident!("Hasher{}", ident)) - .collect::>(); - let kargs = - prefixes.iter().map(|ident| format_ident!("KArg{}", ident)).collect::>(); - let partial_keygen = generate_keygen(prefixes); - let suffix_keygen = generate_keygen(suffixes); - let suffix_tuple = generate_tuple(suffixes); - - let trait_impls = quote! { - impl< - #(#current_tuple: FullCodec + StaticTypeInfo,)* - #(#hashers: StorageHasher,)* - #(#kargs: EncodeLike<#prefixes>),* - > HasKeyPrefix<( #( #kargs, )* )> for ( #( Key<#hashers, #current_tuple>, )* ) { - type Suffix = #suffix_tuple; - - fn partial_key(prefix: ( #( #kargs, )* )) -> Vec { - <#partial_keygen>::final_key(prefix) - } - } - - impl< - #(#current_tuple: FullCodec + StaticTypeInfo,)* - #(#hashers: ReversibleStorageHasher,)* - #(#kargs: EncodeLike<#prefixes>),* - > HasReversibleKeyPrefix<( #( #kargs, )* )> for ( #( Key<#hashers, #current_tuple>, )* ) { - fn decode_partial_key(key_material: &[u8]) -> Result< - Self::Suffix, - #frame_support::__private::codec::Error, - > { - <#suffix_keygen>::decode_final_key(key_material).map(|k| k.0) - } - } - }; - - all_trait_impls.extend(trait_impls); - } - } - - Ok(all_trait_impls) + if !input.is_empty() { + return Err(syn::Error::new(Span::call_site(), "No arguments expected")); + } + + let mut all_trait_impls = TokenStream::new(); + let frame_support = generate_access_from_frame_or_crate("frame-support")?; + + for i in 2..=MAX_IDENTS { + let current_tuple = (0..i) + .map(|n| Ident::new(&format!("Tuple{}", n), Span::call_site())) + .collect::>(); + + for prefix_count in 1..i { + let (prefixes, suffixes) = current_tuple.split_at(prefix_count); + + let hashers = current_tuple + .iter() + .map(|ident| format_ident!("Hasher{}", ident)) + .collect::>(); + let kargs = prefixes + .iter() + .map(|ident| format_ident!("KArg{}", ident)) + .collect::>(); + let partial_keygen = generate_keygen(prefixes); + let suffix_keygen = generate_keygen(suffixes); + let suffix_tuple = generate_tuple(suffixes); + + let trait_impls = quote! { + impl< + #(#current_tuple: FullCodec + StaticTypeInfo,)* + #(#hashers: StorageHasher,)* + #(#kargs: EncodeLike<#prefixes>),* + > HasKeyPrefix<( #( #kargs, )* )> for ( #( Key<#hashers, #current_tuple>, )* ) { + type Suffix = #suffix_tuple; + + fn partial_key(prefix: ( #( #kargs, )* )) -> Vec { + <#partial_keygen>::final_key(prefix) + } + } + + impl< + #(#current_tuple: FullCodec + StaticTypeInfo,)* + #(#hashers: ReversibleStorageHasher,)* + #(#kargs: EncodeLike<#prefixes>),* + > HasReversibleKeyPrefix<( #( #kargs, )* )> for ( #( Key<#hashers, #current_tuple>, )* ) { + fn decode_partial_key(key_material: &[u8]) -> Result< + Self::Suffix, + #frame_support::__private::codec::Error, + > { + <#suffix_keygen>::decode_final_key(key_material).map(|k| k.0) + } + } + }; + + all_trait_impls.extend(trait_impls); + } + } + + Ok(all_trait_impls) } fn generate_tuple(idents: &[Ident]) -> TokenStream { - if idents.len() == 1 { - idents[0].to_token_stream() - } else { - quote!((#(#idents),*)) - } + if idents.len() == 1 { + idents[0].to_token_stream() + } else { + quote!((#(#idents),*)) + } } fn generate_keygen(idents: &[Ident]) -> TokenStream { - if idents.len() == 1 { - let key = &idents[0]; - let hasher = format_ident!("Hasher{}", key); + if idents.len() == 1 { + let key = &idents[0]; + let hasher = format_ident!("Hasher{}", key); - quote!(Key<#hasher, #key>) - } else { - let hashers = idents.iter().map(|ident| format_ident!("Hasher{}", ident)); + quote!(Key<#hasher, #key>) + } else { + let hashers = idents.iter().map(|ident| format_ident!("Hasher{}", ident)); - quote!((#(Key<#hashers, #idents>),*)) - } + quote!((#(Key<#hashers, #idents>),*)) + } } diff --git a/support/procedural-fork/src/match_and_insert.rs b/support/procedural-fork/src/match_and_insert.rs index aa9cc56d7..a80b6e95f 100644 --- a/support/procedural-fork/src/match_and_insert.rs +++ b/support/procedural-fork/src/match_and_insert.rs @@ -22,138 +22,152 @@ use std::iter::once; use syn::spanned::Spanned; mod keyword { - syn::custom_keyword!(target); - syn::custom_keyword!(pattern); - syn::custom_keyword!(tokens); + syn::custom_keyword!(target); + syn::custom_keyword!(pattern); + syn::custom_keyword!(tokens); } pub fn match_and_insert(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let MatchAndInsertDef { pattern, tokens, target } = - syn::parse_macro_input!(input as MatchAndInsertDef); - - match expand_in_stream(&pattern, &mut Some(tokens), target) { - Ok(stream) => stream.into(), - Err(err) => err.to_compile_error().into(), - } + let MatchAndInsertDef { + pattern, + tokens, + target, + } = syn::parse_macro_input!(input as MatchAndInsertDef); + + match expand_in_stream(&pattern, &mut Some(tokens), target) { + Ok(stream) => stream.into(), + Err(err) => err.to_compile_error().into(), + } } struct MatchAndInsertDef { - // Token stream to search and insert tokens into. - target: TokenStream, - // Pattern to match against, this is ensured to have no TokenTree::Group nor TokenTree::Literal - // (i.e. contains only Punct or Ident), and not being empty. - pattern: Vec, - // Token stream to insert after the match pattern. - tokens: TokenStream, + // Token stream to search and insert tokens into. + target: TokenStream, + // Pattern to match against, this is ensured to have no TokenTree::Group nor TokenTree::Literal + // (i.e. contains only Punct or Ident), and not being empty. + pattern: Vec, + // Token stream to insert after the match pattern. + tokens: TokenStream, } impl syn::parse::Parse for MatchAndInsertDef { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let mut target; - let _ = input.parse::()?; - let _ = input.parse::()?; - let _replace_with_bracket: syn::token::Bracket = syn::bracketed!(target in input); - let _replace_with_brace: syn::token::Brace = syn::braced!(target in target); - let target = target.parse()?; - - let mut pattern; - let _ = input.parse::()?; - let _ = input.parse::()?; - let _replace_with_bracket: syn::token::Bracket = syn::bracketed!(pattern in input); - let _replace_with_brace: syn::token::Brace = syn::braced!(pattern in pattern); - let pattern = pattern.parse::()?.into_iter().collect::>(); - - if let Some(t) = pattern.iter().find(|t| matches!(t, TokenTree::Group(_))) { - return Err(syn::Error::new(t.span(), "Unexpected group token tree")) - } - if let Some(t) = pattern.iter().find(|t| matches!(t, TokenTree::Literal(_))) { - return Err(syn::Error::new(t.span(), "Unexpected literal token tree")) - } - - if pattern.is_empty() { - return Err(syn::Error::new(Span::call_site(), "empty match pattern is invalid")) - } - - let mut tokens; - let _ = input.parse::()?; - let _ = input.parse::()?; - let _replace_with_bracket: syn::token::Bracket = syn::bracketed!(tokens in input); - let _replace_with_brace: syn::token::Brace = syn::braced!(tokens in tokens); - let tokens = tokens.parse()?; - - Ok(Self { tokens, pattern, target }) - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut target; + let _ = input.parse::()?; + let _ = input.parse::()?; + let _replace_with_bracket: syn::token::Bracket = syn::bracketed!(target in input); + let _replace_with_brace: syn::token::Brace = syn::braced!(target in target); + let target = target.parse()?; + + let mut pattern; + let _ = input.parse::()?; + let _ = input.parse::()?; + let _replace_with_bracket: syn::token::Bracket = syn::bracketed!(pattern in input); + let _replace_with_brace: syn::token::Brace = syn::braced!(pattern in pattern); + let pattern = pattern + .parse::()? + .into_iter() + .collect::>(); + + if let Some(t) = pattern.iter().find(|t| matches!(t, TokenTree::Group(_))) { + return Err(syn::Error::new(t.span(), "Unexpected group token tree")); + } + if let Some(t) = pattern.iter().find(|t| matches!(t, TokenTree::Literal(_))) { + return Err(syn::Error::new(t.span(), "Unexpected literal token tree")); + } + + if pattern.is_empty() { + return Err(syn::Error::new( + Span::call_site(), + "empty match pattern is invalid", + )); + } + + let mut tokens; + let _ = input.parse::()?; + let _ = input.parse::()?; + let _replace_with_bracket: syn::token::Bracket = syn::bracketed!(tokens in input); + let _replace_with_brace: syn::token::Brace = syn::braced!(tokens in tokens); + let tokens = tokens.parse()?; + + Ok(Self { + tokens, + pattern, + target, + }) + } } // Insert `tokens` after the first matching `pattern`. // `tokens` must be some (Option is used for internal simplification). // `pattern` must not be empty and should only contain Ident or Punct. fn expand_in_stream( - pattern: &[TokenTree], - tokens: &mut Option, - stream: TokenStream, + pattern: &[TokenTree], + tokens: &mut Option, + stream: TokenStream, ) -> syn::Result { - assert!( - tokens.is_some(), - "`tokens` must be some, Option is used because `tokens` is used only once" - ); - assert!( - !pattern.is_empty(), - "`pattern` must not be empty, otherwise there is nothing to match against" - ); - - let stream_span = stream.span(); - let mut stream = stream.into_iter(); - let mut extended = TokenStream::new(); - let mut match_cursor = 0; - - while let Some(token) = stream.next() { - match token { - TokenTree::Group(group) => { - match_cursor = 0; - let group_stream = group.stream(); - match expand_in_stream(pattern, tokens, group_stream) { - Ok(s) => { - extended.extend(once(TokenTree::Group(Group::new(group.delimiter(), s)))); - extended.extend(stream); - return Ok(extended) - }, - Err(_) => { - extended.extend(once(TokenTree::Group(group))); - }, - } - }, - other => { - advance_match_cursor(&other, pattern, &mut match_cursor); - - extended.extend(once(other)); - - if match_cursor == pattern.len() { - extended - .extend(once(tokens.take().expect("tokens is used to replace only once"))); - extended.extend(stream); - return Ok(extended) - } - }, - } - } - // if we reach this point, it means the stream is empty and we haven't found a matching pattern - let msg = format!("Cannot find pattern `{:?}` in given token stream", pattern); - Err(syn::Error::new(stream_span, msg)) + assert!( + tokens.is_some(), + "`tokens` must be some, Option is used because `tokens` is used only once" + ); + assert!( + !pattern.is_empty(), + "`pattern` must not be empty, otherwise there is nothing to match against" + ); + + let stream_span = stream.span(); + let mut stream = stream.into_iter(); + let mut extended = TokenStream::new(); + let mut match_cursor = 0; + + while let Some(token) = stream.next() { + match token { + TokenTree::Group(group) => { + match_cursor = 0; + let group_stream = group.stream(); + match expand_in_stream(pattern, tokens, group_stream) { + Ok(s) => { + extended.extend(once(TokenTree::Group(Group::new(group.delimiter(), s)))); + extended.extend(stream); + return Ok(extended); + } + Err(_) => { + extended.extend(once(TokenTree::Group(group))); + } + } + } + other => { + advance_match_cursor(&other, pattern, &mut match_cursor); + + extended.extend(once(other)); + + if match_cursor == pattern.len() { + extended.extend(once( + tokens.take().expect("tokens is used to replace only once"), + )); + extended.extend(stream); + return Ok(extended); + } + } + } + } + // if we reach this point, it means the stream is empty and we haven't found a matching pattern + let msg = format!("Cannot find pattern `{:?}` in given token stream", pattern); + Err(syn::Error::new(stream_span, msg)) } fn advance_match_cursor(other: &TokenTree, pattern: &[TokenTree], match_cursor: &mut usize) { - use TokenTree::{Ident, Punct}; - - let does_match_other_pattern = match (other, &pattern[*match_cursor]) { - (Ident(i1), Ident(i2)) => i1 == i2, - (Punct(p1), Punct(p2)) => p1.as_char() == p2.as_char(), - _ => false, - }; - - if does_match_other_pattern { - *match_cursor += 1; - } else { - *match_cursor = 0; - } + use TokenTree::{Ident, Punct}; + + let does_match_other_pattern = match (other, &pattern[*match_cursor]) { + (Ident(i1), Ident(i2)) => i1 == i2, + (Punct(p1), Punct(p2)) => p1.as_char() == p2.as_char(), + _ => false, + }; + + if does_match_other_pattern { + *match_cursor += 1; + } else { + *match_cursor = 0; + } } diff --git a/support/procedural-fork/src/no_bound/clone.rs b/support/procedural-fork/src/no_bound/clone.rs index 346bf450f..17039bdc8 100644 --- a/support/procedural-fork/src/no_bound/clone.rs +++ b/support/procedural-fork/src/no_bound/clone.rs @@ -19,89 +19,93 @@ use syn::spanned::Spanned; /// Derive Clone but do not bound any generic. pub fn derive_clone_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input = syn::parse_macro_input!(input as syn::DeriveInput); + let input = syn::parse_macro_input!(input as syn::DeriveInput); - let name = &input.ident; - let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + let name = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - let impl_ = match input.data { - syn::Data::Struct(struct_) => match struct_.fields { - syn::Fields::Named(named) => { - let fields = named.named.iter().map(|i| &i.ident).map(|i| { - quote::quote_spanned!(i.span() => - #i: ::core::clone::Clone::clone(&self.#i) - ) - }); + let impl_ = match input.data { + syn::Data::Struct(struct_) => match struct_.fields { + syn::Fields::Named(named) => { + let fields = named.named.iter().map(|i| &i.ident).map(|i| { + quote::quote_spanned!(i.span() => + #i: ::core::clone::Clone::clone(&self.#i) + ) + }); - quote::quote!( Self { #( #fields, )* } ) - }, - syn::Fields::Unnamed(unnamed) => { - let fields = - unnamed.unnamed.iter().enumerate().map(|(i, _)| syn::Index::from(i)).map(|i| { - quote::quote_spanned!(i.span() => - ::core::clone::Clone::clone(&self.#i) - ) - }); + quote::quote!( Self { #( #fields, )* } ) + } + syn::Fields::Unnamed(unnamed) => { + let fields = unnamed + .unnamed + .iter() + .enumerate() + .map(|(i, _)| syn::Index::from(i)) + .map(|i| { + quote::quote_spanned!(i.span() => + ::core::clone::Clone::clone(&self.#i) + ) + }); - quote::quote!( Self ( #( #fields, )* ) ) - }, - syn::Fields::Unit => { - quote::quote!(Self) - }, - }, - syn::Data::Enum(enum_) => { - let variants = enum_.variants.iter().map(|variant| { - let ident = &variant.ident; - match &variant.fields { - syn::Fields::Named(named) => { - let captured = named.named.iter().map(|i| &i.ident); - let cloned = captured.clone().map(|i| { - ::quote::quote_spanned!(i.span() => - #i: ::core::clone::Clone::clone(#i) - ) - }); - quote::quote!( - Self::#ident { #( ref #captured, )* } => Self::#ident { #( #cloned, )*} - ) - }, - syn::Fields::Unnamed(unnamed) => { - let captured = unnamed - .unnamed - .iter() - .enumerate() - .map(|(i, f)| syn::Ident::new(&format!("_{}", i), f.span())); - let cloned = captured.clone().map(|i| { - quote::quote_spanned!(i.span() => - ::core::clone::Clone::clone(#i) - ) - }); - quote::quote!( - Self::#ident ( #( ref #captured, )* ) => Self::#ident ( #( #cloned, )*) - ) - }, - syn::Fields::Unit => quote::quote!( Self::#ident => Self::#ident ), - } - }); + quote::quote!( Self ( #( #fields, )* ) ) + } + syn::Fields::Unit => { + quote::quote!(Self) + } + }, + syn::Data::Enum(enum_) => { + let variants = enum_.variants.iter().map(|variant| { + let ident = &variant.ident; + match &variant.fields { + syn::Fields::Named(named) => { + let captured = named.named.iter().map(|i| &i.ident); + let cloned = captured.clone().map(|i| { + ::quote::quote_spanned!(i.span() => + #i: ::core::clone::Clone::clone(#i) + ) + }); + quote::quote!( + Self::#ident { #( ref #captured, )* } => Self::#ident { #( #cloned, )*} + ) + } + syn::Fields::Unnamed(unnamed) => { + let captured = unnamed + .unnamed + .iter() + .enumerate() + .map(|(i, f)| syn::Ident::new(&format!("_{}", i), f.span())); + let cloned = captured.clone().map(|i| { + quote::quote_spanned!(i.span() => + ::core::clone::Clone::clone(#i) + ) + }); + quote::quote!( + Self::#ident ( #( ref #captured, )* ) => Self::#ident ( #( #cloned, )*) + ) + } + syn::Fields::Unit => quote::quote!( Self::#ident => Self::#ident ), + } + }); - quote::quote!(match self { - #( #variants, )* - }) - }, - syn::Data::Union(_) => { - let msg = "Union type not supported by `derive(CloneNoBound)`"; - return syn::Error::new(input.span(), msg).to_compile_error().into() - }, - }; + quote::quote!(match self { + #( #variants, )* + }) + } + syn::Data::Union(_) => { + let msg = "Union type not supported by `derive(CloneNoBound)`"; + return syn::Error::new(input.span(), msg).to_compile_error().into(); + } + }; - quote::quote!( - const _: () = { - #[automatically_derived] - impl #impl_generics ::core::clone::Clone for #name #ty_generics #where_clause { - fn clone(&self) -> Self { - #impl_ - } - } - }; - ) - .into() + quote::quote!( + const _: () = { + #[automatically_derived] + impl #impl_generics ::core::clone::Clone for #name #ty_generics #where_clause { + fn clone(&self) -> Self { + #impl_ + } + } + }; + ) + .into() } diff --git a/support/procedural-fork/src/no_bound/debug.rs b/support/procedural-fork/src/no_bound/debug.rs index a1b3f4f0d..8034bb5ec 100644 --- a/support/procedural-fork/src/no_bound/debug.rs +++ b/support/procedural-fork/src/no_bound/debug.rs @@ -19,103 +19,103 @@ use syn::spanned::Spanned; /// Derive Debug but do not bound any generics. pub fn derive_debug_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input = syn::parse_macro_input!(input as syn::DeriveInput); + let input = syn::parse_macro_input!(input as syn::DeriveInput); - let input_ident = &input.ident; - let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + let input_ident = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - let impl_ = match input.data { - syn::Data::Struct(struct_) => match struct_.fields { - syn::Fields::Named(named) => { - let fields = - named.named.iter().map(|i| &i.ident).map( - |i| quote::quote_spanned!(i.span() => .field(stringify!(#i), &self.#i) ), - ); + let impl_ = match input.data { + syn::Data::Struct(struct_) => match struct_.fields { + syn::Fields::Named(named) => { + let fields = + named.named.iter().map(|i| &i.ident).map( + |i| quote::quote_spanned!(i.span() => .field(stringify!(#i), &self.#i) ), + ); - quote::quote!( - fmt.debug_struct(stringify!(#input_ident)) - #( #fields )* - .finish() - ) - }, - syn::Fields::Unnamed(unnamed) => { - let fields = unnamed - .unnamed - .iter() - .enumerate() - .map(|(i, _)| syn::Index::from(i)) - .map(|i| quote::quote_spanned!(i.span() => .field(&self.#i) )); + quote::quote!( + fmt.debug_struct(stringify!(#input_ident)) + #( #fields )* + .finish() + ) + } + syn::Fields::Unnamed(unnamed) => { + let fields = unnamed + .unnamed + .iter() + .enumerate() + .map(|(i, _)| syn::Index::from(i)) + .map(|i| quote::quote_spanned!(i.span() => .field(&self.#i) )); - quote::quote!( - fmt.debug_tuple(stringify!(#input_ident)) - #( #fields )* - .finish() - ) - }, - syn::Fields::Unit => quote::quote!(fmt.write_str(stringify!(#input_ident))), - }, - syn::Data::Enum(enum_) => { - let variants = enum_.variants.iter().map(|variant| { - let ident = &variant.ident; - let full_variant_str = format!("{}::{}", input_ident, ident); - match &variant.fields { - syn::Fields::Named(named) => { - let captured = named.named.iter().map(|i| &i.ident); - let debugged = captured.clone().map(|i| { - quote::quote_spanned!(i.span() => - .field(stringify!(#i), &#i) - ) - }); - quote::quote!( - Self::#ident { #( ref #captured, )* } => { - fmt.debug_struct(#full_variant_str) - #( #debugged )* - .finish() - } - ) - }, - syn::Fields::Unnamed(unnamed) => { - let captured = unnamed - .unnamed - .iter() - .enumerate() - .map(|(i, f)| syn::Ident::new(&format!("_{}", i), f.span())); - let debugged = captured - .clone() - .map(|i| quote::quote_spanned!(i.span() => .field(&#i))); - quote::quote!( - Self::#ident ( #( ref #captured, )* ) => { - fmt.debug_tuple(#full_variant_str) - #( #debugged )* - .finish() - } - ) - }, - syn::Fields::Unit => quote::quote!( - Self::#ident => fmt.write_str(#full_variant_str) - ), - } - }); + quote::quote!( + fmt.debug_tuple(stringify!(#input_ident)) + #( #fields )* + .finish() + ) + } + syn::Fields::Unit => quote::quote!(fmt.write_str(stringify!(#input_ident))), + }, + syn::Data::Enum(enum_) => { + let variants = enum_.variants.iter().map(|variant| { + let ident = &variant.ident; + let full_variant_str = format!("{}::{}", input_ident, ident); + match &variant.fields { + syn::Fields::Named(named) => { + let captured = named.named.iter().map(|i| &i.ident); + let debugged = captured.clone().map(|i| { + quote::quote_spanned!(i.span() => + .field(stringify!(#i), &#i) + ) + }); + quote::quote!( + Self::#ident { #( ref #captured, )* } => { + fmt.debug_struct(#full_variant_str) + #( #debugged )* + .finish() + } + ) + } + syn::Fields::Unnamed(unnamed) => { + let captured = unnamed + .unnamed + .iter() + .enumerate() + .map(|(i, f)| syn::Ident::new(&format!("_{}", i), f.span())); + let debugged = captured + .clone() + .map(|i| quote::quote_spanned!(i.span() => .field(&#i))); + quote::quote!( + Self::#ident ( #( ref #captured, )* ) => { + fmt.debug_tuple(#full_variant_str) + #( #debugged )* + .finish() + } + ) + } + syn::Fields::Unit => quote::quote!( + Self::#ident => fmt.write_str(#full_variant_str) + ), + } + }); - quote::quote!(match *self { - #( #variants, )* - }) - }, - syn::Data::Union(_) => { - let msg = "Union type not supported by `derive(DebugNoBound)`"; - return syn::Error::new(input.span(), msg).to_compile_error().into() - }, - }; + quote::quote!(match *self { + #( #variants, )* + }) + } + syn::Data::Union(_) => { + let msg = "Union type not supported by `derive(DebugNoBound)`"; + return syn::Error::new(input.span(), msg).to_compile_error().into(); + } + }; - quote::quote!( - const _: () = { - #[automatically_derived] - impl #impl_generics ::core::fmt::Debug for #input_ident #ty_generics #where_clause { - fn fmt(&self, fmt: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - #impl_ - } - } - }; - ) - .into() + quote::quote!( + const _: () = { + #[automatically_derived] + impl #impl_generics ::core::fmt::Debug for #input_ident #ty_generics #where_clause { + fn fmt(&self, fmt: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + #impl_ + } + } + }; + ) + .into() } diff --git a/support/procedural-fork/src/no_bound/default.rs b/support/procedural-fork/src/no_bound/default.rs index 0524247d2..1c0d90531 100644 --- a/support/procedural-fork/src/no_bound/default.rs +++ b/support/procedural-fork/src/no_bound/default.rs @@ -21,51 +21,57 @@ use syn::{spanned::Spanned, Data, DeriveInput, Fields}; /// Derive Default but do not bound any generic. pub fn derive_default_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input = syn::parse_macro_input!(input as DeriveInput); - - let name = &input.ident; - - let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - - let impl_ = match input.data { - Data::Struct(struct_) => match struct_.fields { - Fields::Named(named) => { - let fields = named.named.iter().map(|field| &field.ident).map(|ident| { - quote_spanned! {ident.span() => - #ident: ::core::default::Default::default() - } - }); - - quote!(Self { #( #fields, )* }) - }, - Fields::Unnamed(unnamed) => { - let fields = unnamed.unnamed.iter().map(|field| { - quote_spanned! {field.span()=> - ::core::default::Default::default() - } - }); - - quote!(Self( #( #fields, )* )) - }, - Fields::Unit => { - quote!(Self) - }, - }, - Data::Enum(enum_) => { - if enum_.variants.is_empty() { - return syn::Error::new_spanned(name, "cannot derive Default for an empty enum") - .to_compile_error() - .into() - } - - // all #[default] attrs with the variant they're on; i.e. a var - let default_variants = enum_ - .variants - .into_iter() - .filter(|variant| variant.attrs.iter().any(|attr| attr.path().is_ident("default"))) - .collect::>(); - - match &*default_variants { + let input = syn::parse_macro_input!(input as DeriveInput); + + let name = &input.ident; + + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + + let impl_ = + match input.data { + Data::Struct(struct_) => match struct_.fields { + Fields::Named(named) => { + let fields = named.named.iter().map(|field| &field.ident).map(|ident| { + quote_spanned! {ident.span() => + #ident: ::core::default::Default::default() + } + }); + + quote!(Self { #( #fields, )* }) + } + Fields::Unnamed(unnamed) => { + let fields = unnamed.unnamed.iter().map(|field| { + quote_spanned! {field.span()=> + ::core::default::Default::default() + } + }); + + quote!(Self( #( #fields, )* )) + } + Fields::Unit => { + quote!(Self) + } + }, + Data::Enum(enum_) => { + if enum_.variants.is_empty() { + return syn::Error::new_spanned(name, "cannot derive Default for an empty enum") + .to_compile_error() + .into(); + } + + // all #[default] attrs with the variant they're on; i.e. a var + let default_variants = enum_ + .variants + .into_iter() + .filter(|variant| { + variant + .attrs + .iter() + .any(|attr| attr.path().is_ident("default")) + }) + .collect::>(); + + match &*default_variants { [] => return syn::Error::new( name.clone().span(), "no default declared, make a variant default by placing `#[default]` above it", @@ -137,25 +143,26 @@ pub fn derive_default_no_bound(input: proc_macro::TokenStream) -> proc_macro::To return err.into_compile_error().into() }, } - }, - Data::Union(union_) => - return syn::Error::new_spanned( - union_.union_token, - "Union type not supported by `derive(DefaultNoBound)`", - ) - .to_compile_error() - .into(), - }; - - quote!( - const _: () = { - #[automatically_derived] - impl #impl_generics ::core::default::Default for #name #ty_generics #where_clause { - fn default() -> Self { - #impl_ - } - } - }; - ) - .into() + } + Data::Union(union_) => { + return syn::Error::new_spanned( + union_.union_token, + "Union type not supported by `derive(DefaultNoBound)`", + ) + .to_compile_error() + .into() + } + }; + + quote!( + const _: () = { + #[automatically_derived] + impl #impl_generics ::core::default::Default for #name #ty_generics #where_clause { + fn default() -> Self { + #impl_ + } + } + }; + ) + .into() } diff --git a/support/procedural-fork/src/no_bound/ord.rs b/support/procedural-fork/src/no_bound/ord.rs index b24d27c04..20f30eb9d 100644 --- a/support/procedural-fork/src/no_bound/ord.rs +++ b/support/procedural-fork/src/no_bound/ord.rs @@ -19,57 +19,57 @@ use syn::spanned::Spanned; /// Derive Ord but do not bound any generic. pub fn derive_ord_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input: syn::DeriveInput = match syn::parse(input) { - Ok(input) => input, - Err(e) => return e.to_compile_error().into(), - }; + let input: syn::DeriveInput = match syn::parse(input) { + Ok(input) => input, + Err(e) => return e.to_compile_error().into(), + }; - let name = &input.ident; - let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + let name = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - let impl_ = match input.data { - syn::Data::Struct(struct_) => match struct_.fields { - syn::Fields::Named(named) => { - let fields = named - .named - .iter() - .map(|i| &i.ident) - .map(|i| quote::quote_spanned!(i.span() => self.#i.cmp(&other.#i) )); + let impl_ = match input.data { + syn::Data::Struct(struct_) => match struct_.fields { + syn::Fields::Named(named) => { + let fields = named + .named + .iter() + .map(|i| &i.ident) + .map(|i| quote::quote_spanned!(i.span() => self.#i.cmp(&other.#i) )); - quote::quote!( core::cmp::Ordering::Equal #( .then_with(|| #fields) )* ) - }, - syn::Fields::Unnamed(unnamed) => { - let fields = unnamed - .unnamed - .iter() - .enumerate() - .map(|(i, _)| syn::Index::from(i)) - .map(|i| quote::quote_spanned!(i.span() => self.#i.cmp(&other.#i) )); + quote::quote!( core::cmp::Ordering::Equal #( .then_with(|| #fields) )* ) + } + syn::Fields::Unnamed(unnamed) => { + let fields = unnamed + .unnamed + .iter() + .enumerate() + .map(|(i, _)| syn::Index::from(i)) + .map(|i| quote::quote_spanned!(i.span() => self.#i.cmp(&other.#i) )); - quote::quote!( core::cmp::Ordering::Equal #( .then_with(|| #fields) )* ) - }, - syn::Fields::Unit => { - quote::quote!(core::cmp::Ordering::Equal) - }, - }, - syn::Data::Enum(_) => { - let msg = "Enum type not supported by `derive(OrdNoBound)`"; - return syn::Error::new(input.span(), msg).to_compile_error().into() - }, - syn::Data::Union(_) => { - let msg = "Union type not supported by `derive(OrdNoBound)`"; - return syn::Error::new(input.span(), msg).to_compile_error().into() - }, - }; + quote::quote!( core::cmp::Ordering::Equal #( .then_with(|| #fields) )* ) + } + syn::Fields::Unit => { + quote::quote!(core::cmp::Ordering::Equal) + } + }, + syn::Data::Enum(_) => { + let msg = "Enum type not supported by `derive(OrdNoBound)`"; + return syn::Error::new(input.span(), msg).to_compile_error().into(); + } + syn::Data::Union(_) => { + let msg = "Union type not supported by `derive(OrdNoBound)`"; + return syn::Error::new(input.span(), msg).to_compile_error().into(); + } + }; - quote::quote!( - const _: () = { - impl #impl_generics core::cmp::Ord for #name #ty_generics #where_clause { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - #impl_ - } - } - }; - ) - .into() + quote::quote!( + const _: () = { + impl #impl_generics core::cmp::Ord for #name #ty_generics #where_clause { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + #impl_ + } + } + }; + ) + .into() } diff --git a/support/procedural-fork/src/no_bound/partial_eq.rs b/support/procedural-fork/src/no_bound/partial_eq.rs index a1be71a96..8833f6e5f 100644 --- a/support/procedural-fork/src/no_bound/partial_eq.rs +++ b/support/procedural-fork/src/no_bound/partial_eq.rs @@ -19,119 +19,119 @@ use syn::spanned::Spanned; /// Derive PartialEq but do not bound any generic. pub fn derive_partial_eq_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input = syn::parse_macro_input!(input as syn::DeriveInput); + let input = syn::parse_macro_input!(input as syn::DeriveInput); - let name = &input.ident; - let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + let name = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - let impl_ = match input.data { - syn::Data::Struct(struct_) => match struct_.fields { - syn::Fields::Named(named) => { - let fields = named - .named - .iter() - .map(|i| &i.ident) - .map(|i| quote::quote_spanned!(i.span() => self.#i == other.#i )); + let impl_ = match input.data { + syn::Data::Struct(struct_) => match struct_.fields { + syn::Fields::Named(named) => { + let fields = named + .named + .iter() + .map(|i| &i.ident) + .map(|i| quote::quote_spanned!(i.span() => self.#i == other.#i )); - quote::quote!( true #( && #fields )* ) - }, - syn::Fields::Unnamed(unnamed) => { - let fields = unnamed - .unnamed - .iter() - .enumerate() - .map(|(i, _)| syn::Index::from(i)) - .map(|i| quote::quote_spanned!(i.span() => self.#i == other.#i )); + quote::quote!( true #( && #fields )* ) + } + syn::Fields::Unnamed(unnamed) => { + let fields = unnamed + .unnamed + .iter() + .enumerate() + .map(|(i, _)| syn::Index::from(i)) + .map(|i| quote::quote_spanned!(i.span() => self.#i == other.#i )); - quote::quote!( true #( && #fields )* ) - }, - syn::Fields::Unit => { - quote::quote!(true) - }, - }, - syn::Data::Enum(enum_) => { - let variants = - enum_.variants.iter().map(|variant| { - let ident = &variant.ident; - match &variant.fields { - syn::Fields::Named(named) => { - let names = named.named.iter().map(|i| &i.ident); - let other_names = names.clone().enumerate().map(|(n, ident)| { - syn::Ident::new(&format!("_{}", n), ident.span()) - }); + quote::quote!( true #( && #fields )* ) + } + syn::Fields::Unit => { + quote::quote!(true) + } + }, + syn::Data::Enum(enum_) => { + let variants = + enum_.variants.iter().map(|variant| { + let ident = &variant.ident; + match &variant.fields { + syn::Fields::Named(named) => { + let names = named.named.iter().map(|i| &i.ident); + let other_names = names.clone().enumerate().map(|(n, ident)| { + syn::Ident::new(&format!("_{}", n), ident.span()) + }); - let capture = names.clone(); - let other_capture = names - .clone() - .zip(other_names.clone()) - .map(|(i, other_i)| quote::quote!(#i: #other_i)); - let eq = names.zip(other_names).map( - |(i, other_i)| quote::quote_spanned!(i.span() => #i == #other_i), - ); - quote::quote!( - ( - Self::#ident { #( #capture, )* }, - Self::#ident { #( #other_capture, )* }, - ) => true #( && #eq )* - ) - }, - syn::Fields::Unnamed(unnamed) => { - let names = unnamed - .unnamed - .iter() - .enumerate() - .map(|(i, f)| syn::Ident::new(&format!("_{}", i), f.span())); - let other_names = - unnamed.unnamed.iter().enumerate().map(|(i, f)| { - syn::Ident::new(&format!("_{}_other", i), f.span()) - }); - let eq = names.clone().zip(other_names.clone()).map( - |(i, other_i)| quote::quote_spanned!(i.span() => #i == #other_i), - ); - quote::quote!( - ( - Self::#ident ( #( #names, )* ), - Self::#ident ( #( #other_names, )* ), - ) => true #( && #eq )* - ) - }, - syn::Fields::Unit => quote::quote!( (Self::#ident, Self::#ident) => true ), - } - }); + let capture = names.clone(); + let other_capture = names + .clone() + .zip(other_names.clone()) + .map(|(i, other_i)| quote::quote!(#i: #other_i)); + let eq = names.zip(other_names).map( + |(i, other_i)| quote::quote_spanned!(i.span() => #i == #other_i), + ); + quote::quote!( + ( + Self::#ident { #( #capture, )* }, + Self::#ident { #( #other_capture, )* }, + ) => true #( && #eq )* + ) + } + syn::Fields::Unnamed(unnamed) => { + let names = unnamed + .unnamed + .iter() + .enumerate() + .map(|(i, f)| syn::Ident::new(&format!("_{}", i), f.span())); + let other_names = + unnamed.unnamed.iter().enumerate().map(|(i, f)| { + syn::Ident::new(&format!("_{}_other", i), f.span()) + }); + let eq = names.clone().zip(other_names.clone()).map( + |(i, other_i)| quote::quote_spanned!(i.span() => #i == #other_i), + ); + quote::quote!( + ( + Self::#ident ( #( #names, )* ), + Self::#ident ( #( #other_names, )* ), + ) => true #( && #eq )* + ) + } + syn::Fields::Unit => quote::quote!( (Self::#ident, Self::#ident) => true ), + } + }); - let mut different_variants = vec![]; - for (i, i_variant) in enum_.variants.iter().enumerate() { - for (j, j_variant) in enum_.variants.iter().enumerate() { - if i != j { - let i_ident = &i_variant.ident; - let j_ident = &j_variant.ident; - different_variants.push(quote::quote!( - (Self::#i_ident { .. }, Self::#j_ident { .. }) => false - )) - } - } - } + let mut different_variants = vec![]; + for (i, i_variant) in enum_.variants.iter().enumerate() { + for (j, j_variant) in enum_.variants.iter().enumerate() { + if i != j { + let i_ident = &i_variant.ident; + let j_ident = &j_variant.ident; + different_variants.push(quote::quote!( + (Self::#i_ident { .. }, Self::#j_ident { .. }) => false + )) + } + } + } - quote::quote!( match (self, other) { - #( #variants, )* - #( #different_variants, )* - }) - }, - syn::Data::Union(_) => { - let msg = "Union type not supported by `derive(PartialEqNoBound)`"; - return syn::Error::new(input.span(), msg).to_compile_error().into() - }, - }; + quote::quote!( match (self, other) { + #( #variants, )* + #( #different_variants, )* + }) + } + syn::Data::Union(_) => { + let msg = "Union type not supported by `derive(PartialEqNoBound)`"; + return syn::Error::new(input.span(), msg).to_compile_error().into(); + } + }; - quote::quote!( - const _: () = { - #[automatically_derived] - impl #impl_generics ::core::cmp::PartialEq for #name #ty_generics #where_clause { - fn eq(&self, other: &Self) -> bool { - #impl_ - } - } - }; - ) - .into() + quote::quote!( + const _: () = { + #[automatically_derived] + impl #impl_generics ::core::cmp::PartialEq for #name #ty_generics #where_clause { + fn eq(&self, other: &Self) -> bool { + #impl_ + } + } + }; + ) + .into() } diff --git a/support/procedural-fork/src/no_bound/partial_ord.rs b/support/procedural-fork/src/no_bound/partial_ord.rs index 86aa42be9..c73199d4e 100644 --- a/support/procedural-fork/src/no_bound/partial_ord.rs +++ b/support/procedural-fork/src/no_bound/partial_ord.rs @@ -19,71 +19,72 @@ use syn::spanned::Spanned; /// Derive PartialOrd but do not bound any generic. pub fn derive_partial_ord_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input: syn::DeriveInput = match syn::parse(input) { - Ok(input) => input, - Err(e) => return e.to_compile_error().into(), - }; + let input: syn::DeriveInput = match syn::parse(input) { + Ok(input) => input, + Err(e) => return e.to_compile_error().into(), + }; - let name = &input.ident; - let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + let name = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - let impl_ = match input.data { - syn::Data::Struct(struct_) => - match struct_.fields { - syn::Fields::Named(named) => { - let fields = - named.named.iter().map(|i| &i.ident).map( - |i| quote::quote_spanned!(i.span() => self.#i.partial_cmp(&other.#i)), - ); + let impl_ = + match input.data { + syn::Data::Struct(struct_) => { + match struct_.fields { + syn::Fields::Named(named) => { + let fields = named.named.iter().map(|i| &i.ident).map( + |i| quote::quote_spanned!(i.span() => self.#i.partial_cmp(&other.#i)), + ); - quote::quote!( - Some(core::cmp::Ordering::Equal) - #( - .and_then(|order| { - let next_order = #fields?; - Some(order.then(next_order)) - }) - )* - ) - }, - syn::Fields::Unnamed(unnamed) => { - let fields = + quote::quote!( + Some(core::cmp::Ordering::Equal) + #( + .and_then(|order| { + let next_order = #fields?; + Some(order.then(next_order)) + }) + )* + ) + } + syn::Fields::Unnamed(unnamed) => { + let fields = unnamed.unnamed.iter().enumerate().map(|(i, _)| syn::Index::from(i)).map( |i| quote::quote_spanned!(i.span() => self.#i.partial_cmp(&other.#i)), ); - quote::quote!( - Some(core::cmp::Ordering::Equal) - #( - .and_then(|order| { - let next_order = #fields?; - Some(order.then(next_order)) - }) - )* - ) - }, - syn::Fields::Unit => { - quote::quote!(Some(core::cmp::Ordering::Equal)) - }, - }, - syn::Data::Enum(_) => { - let msg = "Enum type not supported by `derive(PartialOrdNoBound)`"; - return syn::Error::new(input.span(), msg).to_compile_error().into() - }, - syn::Data::Union(_) => { - let msg = "Union type not supported by `derive(PartialOrdNoBound)`"; - return syn::Error::new(input.span(), msg).to_compile_error().into() - }, - }; + quote::quote!( + Some(core::cmp::Ordering::Equal) + #( + .and_then(|order| { + let next_order = #fields?; + Some(order.then(next_order)) + }) + )* + ) + } + syn::Fields::Unit => { + quote::quote!(Some(core::cmp::Ordering::Equal)) + } + } + } + syn::Data::Enum(_) => { + let msg = "Enum type not supported by `derive(PartialOrdNoBound)`"; + return syn::Error::new(input.span(), msg).to_compile_error().into(); + } + syn::Data::Union(_) => { + let msg = "Union type not supported by `derive(PartialOrdNoBound)`"; + return syn::Error::new(input.span(), msg).to_compile_error().into(); + } + }; - quote::quote!( - const _: () = { - impl #impl_generics core::cmp::PartialOrd for #name #ty_generics #where_clause { - fn partial_cmp(&self, other: &Self) -> Option { - #impl_ - } - } - }; - ) - .into() + quote::quote!( + const _: () = { + impl #impl_generics core::cmp::PartialOrd for #name #ty_generics #where_clause { + fn partial_cmp(&self, other: &Self) -> Option { + #impl_ + } + } + }; + ) + .into() } diff --git a/support/procedural-fork/src/pallet/expand/call.rs b/support/procedural-fork/src/pallet/expand/call.rs index f395872c8..a39e81fd1 100644 --- a/support/procedural-fork/src/pallet/expand/call.rs +++ b/support/procedural-fork/src/pallet/expand/call.rs @@ -16,12 +16,12 @@ // limitations under the License. use crate::{ - pallet::{ - expand::warnings::{weight_constant_warning, weight_witness_warning}, - parse::call::CallWeightDef, - Def, - }, - COUNTER, + pallet::{ + expand::warnings::{weight_constant_warning, weight_witness_warning}, + parse::call::CallWeightDef, + Def, + }, + COUNTER, }; use proc_macro2::TokenStream as TokenStream2; use proc_macro_warning::Warning; @@ -32,45 +32,56 @@ use syn::spanned::Spanned; /// * Generate enum call and implement various trait on it. /// * Implement Callable and call_function on `Pallet` pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream { - let (span, where_clause, methods, docs) = match def.call.as_ref() { - Some(call) => { - let span = call.attr_span; - let where_clause = call.where_clause.clone(); - let methods = call.methods.clone(); - let docs = call.docs.clone(); - - (span, where_clause, methods, docs) - }, - None => (def.item.span(), def.config.where_clause.clone(), Vec::new(), Vec::new()), - }; - let frame_support = &def.frame_support; - let frame_system = &def.frame_system; - let type_impl_gen = &def.type_impl_generics(span); - let type_decl_bounded_gen = &def.type_decl_bounded_generics(span); - let type_use_gen = &def.type_use_generics(span); - let call_ident = syn::Ident::new("Call", span); - let pallet_ident = &def.pallet_struct.pallet; - - let fn_name = methods.iter().map(|method| &method.name).collect::>(); - let call_index = methods.iter().map(|method| method.call_index).collect::>(); - let new_call_variant_fn_name = fn_name - .iter() - .map(|fn_name| quote::format_ident!("new_call_variant_{}", fn_name)) - .collect::>(); - - let new_call_variant_doc = fn_name - .iter() - .map(|fn_name| format!("Create a call with the variant `{}`.", fn_name)) - .collect::>(); - - let mut call_index_warnings = Vec::new(); - // Emit a warning for each call that is missing `call_index` when not in dev-mode. - for method in &methods { - if method.explicit_call_index || def.dev_mode { - continue - } - - let warning = Warning::new_deprecated("ImplicitCallIndex") + let (span, where_clause, methods, docs) = match def.call.as_ref() { + Some(call) => { + let span = call.attr_span; + let where_clause = call.where_clause.clone(); + let methods = call.methods.clone(); + let docs = call.docs.clone(); + + (span, where_clause, methods, docs) + } + None => ( + def.item.span(), + def.config.where_clause.clone(), + Vec::new(), + Vec::new(), + ), + }; + let frame_support = &def.frame_support; + let frame_system = &def.frame_system; + let type_impl_gen = &def.type_impl_generics(span); + let type_decl_bounded_gen = &def.type_decl_bounded_generics(span); + let type_use_gen = &def.type_use_generics(span); + let call_ident = syn::Ident::new("Call", span); + let pallet_ident = &def.pallet_struct.pallet; + + let fn_name = methods + .iter() + .map(|method| &method.name) + .collect::>(); + let call_index = methods + .iter() + .map(|method| method.call_index) + .collect::>(); + let new_call_variant_fn_name = fn_name + .iter() + .map(|fn_name| quote::format_ident!("new_call_variant_{}", fn_name)) + .collect::>(); + + let new_call_variant_doc = fn_name + .iter() + .map(|fn_name| format!("Create a call with the variant `{}`.", fn_name)) + .collect::>(); + + let mut call_index_warnings = Vec::new(); + // Emit a warning for each call that is missing `call_index` when not in dev-mode. + for method in &methods { + if method.explicit_call_index || def.dev_mode { + continue; + } + + let warning = Warning::new_deprecated("ImplicitCallIndex") .index(call_index_warnings.len()) .old("use implicit call indices") .new("ensure that all calls have a `pallet::call_index` attribute or put the pallet into `dev` mode") @@ -80,373 +91,408 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream { ]) .span(method.name.span()) .build_or_panic(); - call_index_warnings.push(warning); - } - - let mut fn_weight = Vec::::new(); - let mut weight_warnings = Vec::new(); - for method in &methods { - match &method.weight { - CallWeightDef::DevModeDefault => fn_weight.push(syn::parse_quote!(0)), - CallWeightDef::Immediate(e) => { - weight_constant_warning(e, def.dev_mode, &mut weight_warnings); - weight_witness_warning(method, def.dev_mode, &mut weight_warnings); - - fn_weight.push(e.into_token_stream()); - }, - CallWeightDef::Inherited => { - let pallet_weight = def - .call - .as_ref() - .expect("we have methods; we have calls; qed") - .inherited_call_weight - .as_ref() - .expect("the parser prevents this"); - - // Expand `<::WeightInfo>::call_name()`. - let t = &pallet_weight.typename; - let n = &method.name; - fn_weight.push(quote!({ < #t > :: #n () })); - }, - } - } - debug_assert_eq!(fn_weight.len(), methods.len()); - - let fn_doc = methods.iter().map(|method| &method.docs).collect::>(); - - let args_name = methods - .iter() - .map(|method| method.args.iter().map(|(_, name, _)| name.clone()).collect::>()) - .collect::>(); - - let args_name_stripped = methods - .iter() - .map(|method| { - method - .args - .iter() - .map(|(_, name, _)| { - syn::Ident::new(name.to_string().trim_start_matches('_'), name.span()) - }) - .collect::>() - }) - .collect::>(); - - let make_args_name_pattern = |ref_tok| { - args_name - .iter() - .zip(args_name_stripped.iter()) - .map(|(args_name, args_name_stripped)| { - args_name - .iter() - .zip(args_name_stripped) - .map(|(args_name, args_name_stripped)| { - if args_name == args_name_stripped { - quote::quote!( #ref_tok #args_name ) - } else { - quote::quote!( #args_name_stripped: #ref_tok #args_name ) - } - }) - .collect::>() - }) - .collect::>() - }; - - let args_name_pattern = make_args_name_pattern(None); - let args_name_pattern_ref = make_args_name_pattern(Some(quote::quote!(ref))); - - let args_type = methods - .iter() - .map(|method| method.args.iter().map(|(_, _, type_)| type_.clone()).collect::>()) - .collect::>(); - - let args_compact_attr = methods.iter().map(|method| { - method - .args - .iter() - .map(|(is_compact, _, type_)| { - if *is_compact { - quote::quote_spanned!(type_.span() => #[codec(compact)] ) - } else { - quote::quote!() - } - }) - .collect::>() - }); - - let default_docs = - [syn::parse_quote!(r"Contains a variant per dispatchable extrinsic that this pallet has.")]; - let docs = if docs.is_empty() { &default_docs[..] } else { &docs[..] }; - - let maybe_compile_error = if def.call.is_none() { - quote::quote! { - compile_error!(concat!( - "`", - stringify!($pallet_name), - "` does not have #[pallet::call] defined, perhaps you should remove `Call` from \ - construct_runtime?", - )); - } - } else { - proc_macro2::TokenStream::new() - }; - - let count = COUNTER.with(|counter| counter.borrow_mut().inc()); - let macro_ident = syn::Ident::new(&format!("__is_call_part_defined_{}", count), span); - - let capture_docs = if cfg!(feature = "no-metadata-docs") { "never" } else { "always" }; - - // Wrap all calls inside of storage layers - if let Some(syn::Item::Impl(item_impl)) = def - .call - .as_ref() - .map(|c| &mut def.item.content.as_mut().expect("Checked by def parser").1[c.index]) - { - item_impl.items.iter_mut().for_each(|i| { - if let syn::ImplItem::Fn(method) = i { - let block = &method.block; - method.block = syn::parse_quote! {{ - // We execute all dispatchable in a new storage layer, allowing them - // to return an error at any point, and undoing any storage changes. - #frame_support::storage::with_storage_layer(|| #block) - }}; - } - }); - } - - // Extracts #[allow] attributes, necessary so that we don't run into compiler warnings - let maybe_allow_attrs = methods - .iter() - .map(|method| { - method - .attrs - .iter() - .find(|attr| attr.path().is_ident("allow")) - .map_or(proc_macro2::TokenStream::new(), |attr| attr.to_token_stream()) - }) - .collect::>(); - - let cfg_attrs = methods - .iter() - .map(|method| { - let attrs = - method.cfg_attrs.iter().map(|attr| attr.to_token_stream()).collect::>(); - quote::quote!( #( #attrs )* ) - }) - .collect::>(); - - let feeless_check = methods.iter().map(|method| &method.feeless_check).collect::>(); - let feeless_check_result = - feeless_check.iter().zip(args_name.iter()).map(|(feeless_check, arg_name)| { - if let Some(feeless_check) = feeless_check { - quote::quote!(#feeless_check(origin, #( #arg_name, )*)) - } else { - quote::quote!(false) - } - }); - - quote::quote_spanned!(span => - #[doc(hidden)] - mod warnings { - #( - #call_index_warnings - )* - #( - #weight_warnings - )* - } - - #[allow(unused_imports)] - #[doc(hidden)] - pub mod __substrate_call_check { - #[macro_export] - #[doc(hidden)] - macro_rules! #macro_ident { - ($pallet_name:ident) => { - #maybe_compile_error - }; - } - - #[doc(hidden)] - pub use #macro_ident as is_call_part_defined; - } - - #( #[doc = #docs] )* - #[derive( - #frame_support::RuntimeDebugNoBound, - #frame_support::CloneNoBound, - #frame_support::EqNoBound, - #frame_support::PartialEqNoBound, - #frame_support::__private::codec::Encode, - #frame_support::__private::codec::Decode, - #frame_support::__private::scale_info::TypeInfo, - )] - #[codec(encode_bound())] - #[codec(decode_bound())] - #[scale_info(skip_type_params(#type_use_gen), capture_docs = #capture_docs)] - #[allow(non_camel_case_types)] - pub enum #call_ident<#type_decl_bounded_gen> #where_clause { - #[doc(hidden)] - #[codec(skip)] - __Ignore( - ::core::marker::PhantomData<(#type_use_gen,)>, - #frame_support::Never, - ), - #( - #cfg_attrs - #( #[doc = #fn_doc] )* - #[codec(index = #call_index)] - #fn_name { - #( - #[allow(missing_docs)] - #args_compact_attr #args_name_stripped: #args_type - ),* - }, - )* - } - - impl<#type_impl_gen> #call_ident<#type_use_gen> #where_clause { - #( - #cfg_attrs - #[doc = #new_call_variant_doc] - pub fn #new_call_variant_fn_name( - #( #args_name_stripped: #args_type ),* - ) -> Self { - Self::#fn_name { - #( #args_name_stripped ),* - } - } - )* - } - - impl<#type_impl_gen> #frame_support::dispatch::GetDispatchInfo - for #call_ident<#type_use_gen> - #where_clause - { - fn get_dispatch_info(&self) -> #frame_support::dispatch::DispatchInfo { - match *self { - #( - #cfg_attrs - Self::#fn_name { #( #args_name_pattern_ref, )* } => { - let __pallet_base_weight = #fn_weight; - - let __pallet_weight = < - dyn #frame_support::dispatch::WeighData<( #( & #args_type, )* )> - >::weigh_data(&__pallet_base_weight, ( #( #args_name, )* )); - - let __pallet_class = < - dyn #frame_support::dispatch::ClassifyDispatch< - ( #( & #args_type, )* ) - > - >::classify_dispatch(&__pallet_base_weight, ( #( #args_name, )* )); - - let __pallet_pays_fee = < - dyn #frame_support::dispatch::PaysFee<( #( & #args_type, )* )> - >::pays_fee(&__pallet_base_weight, ( #( #args_name, )* )); - - #frame_support::dispatch::DispatchInfo { - weight: __pallet_weight, - class: __pallet_class, - pays_fee: __pallet_pays_fee, - } - }, - )* - Self::__Ignore(_, _) => unreachable!("__Ignore cannot be used"), - } - } - } - - impl<#type_impl_gen> #frame_support::dispatch::CheckIfFeeless for #call_ident<#type_use_gen> - #where_clause - { - type Origin = #frame_system::pallet_prelude::OriginFor; - #[allow(unused_variables)] - fn is_feeless(&self, origin: &Self::Origin) -> bool { - match *self { - #( - #cfg_attrs - Self::#fn_name { #( #args_name_pattern_ref, )* } => { - #feeless_check_result - }, - )* - Self::__Ignore(_, _) => unreachable!("__Ignore cannot be used"), - } - } - } - - impl<#type_impl_gen> #frame_support::traits::GetCallName for #call_ident<#type_use_gen> - #where_clause - { - fn get_call_name(&self) -> &'static str { - match *self { - #( #cfg_attrs Self::#fn_name { .. } => stringify!(#fn_name), )* - Self::__Ignore(_, _) => unreachable!("__PhantomItem cannot be used."), - } - } - - fn get_call_names() -> &'static [&'static str] { - &[ #( #cfg_attrs stringify!(#fn_name), )* ] - } - } - - impl<#type_impl_gen> #frame_support::traits::GetCallIndex for #call_ident<#type_use_gen> - #where_clause - { - fn get_call_index(&self) -> u8 { - match *self { - #( #cfg_attrs Self::#fn_name { .. } => #call_index, )* - Self::__Ignore(_, _) => unreachable!("__PhantomItem cannot be used."), - } - } - - fn get_call_indices() -> &'static [u8] { - &[ #( #cfg_attrs #call_index, )* ] - } - } - - impl<#type_impl_gen> #frame_support::traits::UnfilteredDispatchable - for #call_ident<#type_use_gen> - #where_clause - { - type RuntimeOrigin = #frame_system::pallet_prelude::OriginFor; - fn dispatch_bypass_filter( - self, - origin: Self::RuntimeOrigin - ) -> #frame_support::dispatch::DispatchResultWithPostInfo { - #frame_support::dispatch_context::run_in_context(|| { - match self { - #( - #cfg_attrs - Self::#fn_name { #( #args_name_pattern, )* } => { - #frame_support::__private::sp_tracing::enter_span!( - #frame_support::__private::sp_tracing::trace_span!(stringify!(#fn_name)) - ); - #maybe_allow_attrs - <#pallet_ident<#type_use_gen>>::#fn_name(origin, #( #args_name, )* ) - .map(Into::into).map_err(Into::into) - }, - )* - Self::__Ignore(_, _) => { - let _ = origin; // Use origin for empty Call enum - unreachable!("__PhantomItem cannot be used."); - }, - } - }) - } - } - - impl<#type_impl_gen> #frame_support::dispatch::Callable for #pallet_ident<#type_use_gen> - #where_clause - { - type RuntimeCall = #call_ident<#type_use_gen>; - } - - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #where_clause { - #[allow(dead_code)] - #[doc(hidden)] - pub fn call_functions() -> #frame_support::__private::metadata_ir::PalletCallMetadataIR { - #frame_support::__private::scale_info::meta_type::<#call_ident<#type_use_gen>>().into() - } - } - ) + call_index_warnings.push(warning); + } + + let mut fn_weight = Vec::::new(); + let mut weight_warnings = Vec::new(); + for method in &methods { + match &method.weight { + CallWeightDef::DevModeDefault => fn_weight.push(syn::parse_quote!(0)), + CallWeightDef::Immediate(e) => { + weight_constant_warning(e, def.dev_mode, &mut weight_warnings); + weight_witness_warning(method, def.dev_mode, &mut weight_warnings); + + fn_weight.push(e.into_token_stream()); + } + CallWeightDef::Inherited => { + let pallet_weight = def + .call + .as_ref() + .expect("we have methods; we have calls; qed") + .inherited_call_weight + .as_ref() + .expect("the parser prevents this"); + + // Expand `<::WeightInfo>::call_name()`. + let t = &pallet_weight.typename; + let n = &method.name; + fn_weight.push(quote!({ < #t > :: #n () })); + } + } + } + debug_assert_eq!(fn_weight.len(), methods.len()); + + let fn_doc = methods + .iter() + .map(|method| &method.docs) + .collect::>(); + + let args_name = methods + .iter() + .map(|method| { + method + .args + .iter() + .map(|(_, name, _)| name.clone()) + .collect::>() + }) + .collect::>(); + + let args_name_stripped = methods + .iter() + .map(|method| { + method + .args + .iter() + .map(|(_, name, _)| { + syn::Ident::new(name.to_string().trim_start_matches('_'), name.span()) + }) + .collect::>() + }) + .collect::>(); + + let make_args_name_pattern = |ref_tok| { + args_name + .iter() + .zip(args_name_stripped.iter()) + .map(|(args_name, args_name_stripped)| { + args_name + .iter() + .zip(args_name_stripped) + .map(|(args_name, args_name_stripped)| { + if args_name == args_name_stripped { + quote::quote!( #ref_tok #args_name ) + } else { + quote::quote!( #args_name_stripped: #ref_tok #args_name ) + } + }) + .collect::>() + }) + .collect::>() + }; + + let args_name_pattern = make_args_name_pattern(None); + let args_name_pattern_ref = make_args_name_pattern(Some(quote::quote!(ref))); + + let args_type = methods + .iter() + .map(|method| { + method + .args + .iter() + .map(|(_, _, type_)| type_.clone()) + .collect::>() + }) + .collect::>(); + + let args_compact_attr = methods.iter().map(|method| { + method + .args + .iter() + .map(|(is_compact, _, type_)| { + if *is_compact { + quote::quote_spanned!(type_.span() => #[codec(compact)] ) + } else { + quote::quote!() + } + }) + .collect::>() + }); + + let default_docs = [syn::parse_quote!( + r"Contains a variant per dispatchable extrinsic that this pallet has." + )]; + let docs = if docs.is_empty() { + &default_docs[..] + } else { + &docs[..] + }; + + let maybe_compile_error = if def.call.is_none() { + quote::quote! { + compile_error!(concat!( + "`", + stringify!($pallet_name), + "` does not have #[pallet::call] defined, perhaps you should remove `Call` from \ + construct_runtime?", + )); + } + } else { + proc_macro2::TokenStream::new() + }; + + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + let macro_ident = syn::Ident::new(&format!("__is_call_part_defined_{}", count), span); + + let capture_docs = if cfg!(feature = "no-metadata-docs") { + "never" + } else { + "always" + }; + + // Wrap all calls inside of storage layers + if let Some(syn::Item::Impl(item_impl)) = def + .call + .as_ref() + .map(|c| &mut def.item.content.as_mut().expect("Checked by def parser").1[c.index]) + { + item_impl.items.iter_mut().for_each(|i| { + if let syn::ImplItem::Fn(method) = i { + let block = &method.block; + method.block = syn::parse_quote! {{ + // We execute all dispatchable in a new storage layer, allowing them + // to return an error at any point, and undoing any storage changes. + #frame_support::storage::with_storage_layer(|| #block) + }}; + } + }); + } + + // Extracts #[allow] attributes, necessary so that we don't run into compiler warnings + let maybe_allow_attrs = methods + .iter() + .map(|method| { + method + .attrs + .iter() + .find(|attr| attr.path().is_ident("allow")) + .map_or(proc_macro2::TokenStream::new(), |attr| { + attr.to_token_stream() + }) + }) + .collect::>(); + + let cfg_attrs = methods + .iter() + .map(|method| { + let attrs = method + .cfg_attrs + .iter() + .map(|attr| attr.to_token_stream()) + .collect::>(); + quote::quote!( #( #attrs )* ) + }) + .collect::>(); + + let feeless_check = methods + .iter() + .map(|method| &method.feeless_check) + .collect::>(); + let feeless_check_result = + feeless_check + .iter() + .zip(args_name.iter()) + .map(|(feeless_check, arg_name)| { + if let Some(feeless_check) = feeless_check { + quote::quote!(#feeless_check(origin, #( #arg_name, )*)) + } else { + quote::quote!(false) + } + }); + + quote::quote_spanned!(span => + #[doc(hidden)] + mod warnings { + #( + #call_index_warnings + )* + #( + #weight_warnings + )* + } + + #[allow(unused_imports)] + #[doc(hidden)] + pub mod __substrate_call_check { + #[macro_export] + #[doc(hidden)] + macro_rules! #macro_ident { + ($pallet_name:ident) => { + #maybe_compile_error + }; + } + + #[doc(hidden)] + pub use #macro_ident as is_call_part_defined; + } + + #( #[doc = #docs] )* + #[derive( + #frame_support::RuntimeDebugNoBound, + #frame_support::CloneNoBound, + #frame_support::EqNoBound, + #frame_support::PartialEqNoBound, + #frame_support::__private::codec::Encode, + #frame_support::__private::codec::Decode, + #frame_support::__private::scale_info::TypeInfo, + )] + #[codec(encode_bound())] + #[codec(decode_bound())] + #[scale_info(skip_type_params(#type_use_gen), capture_docs = #capture_docs)] + #[allow(non_camel_case_types)] + pub enum #call_ident<#type_decl_bounded_gen> #where_clause { + #[doc(hidden)] + #[codec(skip)] + __Ignore( + ::core::marker::PhantomData<(#type_use_gen,)>, + #frame_support::Never, + ), + #( + #cfg_attrs + #( #[doc = #fn_doc] )* + #[codec(index = #call_index)] + #fn_name { + #( + #[allow(missing_docs)] + #args_compact_attr #args_name_stripped: #args_type + ),* + }, + )* + } + + impl<#type_impl_gen> #call_ident<#type_use_gen> #where_clause { + #( + #cfg_attrs + #[doc = #new_call_variant_doc] + pub fn #new_call_variant_fn_name( + #( #args_name_stripped: #args_type ),* + ) -> Self { + Self::#fn_name { + #( #args_name_stripped ),* + } + } + )* + } + + impl<#type_impl_gen> #frame_support::dispatch::GetDispatchInfo + for #call_ident<#type_use_gen> + #where_clause + { + fn get_dispatch_info(&self) -> #frame_support::dispatch::DispatchInfo { + match *self { + #( + #cfg_attrs + Self::#fn_name { #( #args_name_pattern_ref, )* } => { + let __pallet_base_weight = #fn_weight; + + let __pallet_weight = < + dyn #frame_support::dispatch::WeighData<( #( & #args_type, )* )> + >::weigh_data(&__pallet_base_weight, ( #( #args_name, )* )); + + let __pallet_class = < + dyn #frame_support::dispatch::ClassifyDispatch< + ( #( & #args_type, )* ) + > + >::classify_dispatch(&__pallet_base_weight, ( #( #args_name, )* )); + + let __pallet_pays_fee = < + dyn #frame_support::dispatch::PaysFee<( #( & #args_type, )* )> + >::pays_fee(&__pallet_base_weight, ( #( #args_name, )* )); + + #frame_support::dispatch::DispatchInfo { + weight: __pallet_weight, + class: __pallet_class, + pays_fee: __pallet_pays_fee, + } + }, + )* + Self::__Ignore(_, _) => unreachable!("__Ignore cannot be used"), + } + } + } + + impl<#type_impl_gen> #frame_support::dispatch::CheckIfFeeless for #call_ident<#type_use_gen> + #where_clause + { + type Origin = #frame_system::pallet_prelude::OriginFor; + #[allow(unused_variables)] + fn is_feeless(&self, origin: &Self::Origin) -> bool { + match *self { + #( + #cfg_attrs + Self::#fn_name { #( #args_name_pattern_ref, )* } => { + #feeless_check_result + }, + )* + Self::__Ignore(_, _) => unreachable!("__Ignore cannot be used"), + } + } + } + + impl<#type_impl_gen> #frame_support::traits::GetCallName for #call_ident<#type_use_gen> + #where_clause + { + fn get_call_name(&self) -> &'static str { + match *self { + #( #cfg_attrs Self::#fn_name { .. } => stringify!(#fn_name), )* + Self::__Ignore(_, _) => unreachable!("__PhantomItem cannot be used."), + } + } + + fn get_call_names() -> &'static [&'static str] { + &[ #( #cfg_attrs stringify!(#fn_name), )* ] + } + } + + impl<#type_impl_gen> #frame_support::traits::GetCallIndex for #call_ident<#type_use_gen> + #where_clause + { + fn get_call_index(&self) -> u8 { + match *self { + #( #cfg_attrs Self::#fn_name { .. } => #call_index, )* + Self::__Ignore(_, _) => unreachable!("__PhantomItem cannot be used."), + } + } + + fn get_call_indices() -> &'static [u8] { + &[ #( #cfg_attrs #call_index, )* ] + } + } + + impl<#type_impl_gen> #frame_support::traits::UnfilteredDispatchable + for #call_ident<#type_use_gen> + #where_clause + { + type RuntimeOrigin = #frame_system::pallet_prelude::OriginFor; + fn dispatch_bypass_filter( + self, + origin: Self::RuntimeOrigin + ) -> #frame_support::dispatch::DispatchResultWithPostInfo { + #frame_support::dispatch_context::run_in_context(|| { + match self { + #( + #cfg_attrs + Self::#fn_name { #( #args_name_pattern, )* } => { + #frame_support::__private::sp_tracing::enter_span!( + #frame_support::__private::sp_tracing::trace_span!(stringify!(#fn_name)) + ); + #maybe_allow_attrs + <#pallet_ident<#type_use_gen>>::#fn_name(origin, #( #args_name, )* ) + .map(Into::into).map_err(Into::into) + }, + )* + Self::__Ignore(_, _) => { + let _ = origin; // Use origin for empty Call enum + unreachable!("__PhantomItem cannot be used."); + }, + } + }) + } + } + + impl<#type_impl_gen> #frame_support::dispatch::Callable for #pallet_ident<#type_use_gen> + #where_clause + { + type RuntimeCall = #call_ident<#type_use_gen>; + } + + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #where_clause { + #[allow(dead_code)] + #[doc(hidden)] + pub fn call_functions() -> #frame_support::__private::metadata_ir::PalletCallMetadataIR { + #frame_support::__private::scale_info::meta_type::<#call_ident<#type_use_gen>>().into() + } + } + ) } diff --git a/support/procedural-fork/src/pallet/expand/composite.rs b/support/procedural-fork/src/pallet/expand/composite.rs index d449afe8f..49c0ad675 100644 --- a/support/procedural-fork/src/pallet/expand/composite.rs +++ b/support/procedural-fork/src/pallet/expand/composite.rs @@ -20,21 +20,21 @@ use proc_macro2::TokenStream; /// Expands `composite_enum` and adds the `VariantCount` implementation for it. pub fn expand_composites(def: &mut Def) -> TokenStream { - let mut expand = quote::quote!(); - let frame_support = &def.frame_support; + let mut expand = quote::quote!(); + let frame_support = &def.frame_support; - for composite in &def.composites { - let name = &composite.ident; - let (impl_generics, ty_generics, where_clause) = composite.generics.split_for_impl(); - let variants_count = composite.variant_count; + for composite in &def.composites { + let name = &composite.ident; + let (impl_generics, ty_generics, where_clause) = composite.generics.split_for_impl(); + let variants_count = composite.variant_count; - // add `VariantCount` implementation for `composite_enum` - expand.extend(quote::quote_spanned!(composite.attr_span => + // add `VariantCount` implementation for `composite_enum` + expand.extend(quote::quote_spanned!(composite.attr_span => impl #impl_generics #frame_support::traits::VariantCount for #name #ty_generics #where_clause { const VARIANT_COUNT: u32 = #variants_count; } )); - } + } - expand + expand } diff --git a/support/procedural-fork/src/pallet/expand/config.rs b/support/procedural-fork/src/pallet/expand/config.rs index 5cf4035a8..836c74ae7 100644 --- a/support/procedural-fork/src/pallet/expand/config.rs +++ b/support/procedural-fork/src/pallet/expand/config.rs @@ -23,20 +23,20 @@ use syn::{parse_quote, Item}; /// /// * Generate default rust doc pub fn expand_config(def: &mut Def) -> TokenStream { - let config = &def.config; - let config_item = { - let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[config.index]; - if let Item::Trait(item) = item { - item - } else { - unreachable!("Checked by config parser") - } - }; + let config = &def.config; + let config_item = { + let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[config.index]; + if let Item::Trait(item) = item { + item + } else { + unreachable!("Checked by config parser") + } + }; - config_item.attrs.insert( - 0, - parse_quote!( - #[doc = r" + config_item.attrs.insert( + 0, + parse_quote!( + #[doc = r" Configuration trait of this pallet. The main purpose of this trait is to act as an interface between this pallet and the runtime in @@ -44,54 +44,54 @@ which it is embedded in. A type, function, or constant in this trait is essentia configured by the runtime that includes this pallet. Consequently, a runtime that wants to include this pallet must implement this trait." - ] - ), - ); + ] + ), + ); - // we only emit `DefaultConfig` if there are trait items, so an empty `DefaultConfig` is - // impossible consequently. - match &config.default_sub_trait { - Some(default_sub_trait) if default_sub_trait.items.len() > 0 => { - let trait_items = &default_sub_trait - .items - .iter() - .map(|item| { - if item.1 { - if let syn::TraitItem::Type(item) = item.0.clone() { - let mut item = item.clone(); - item.bounds.clear(); - syn::TraitItem::Type(item) - } else { - item.0.clone() - } - } else { - item.0.clone() - } - }) - .collect::>(); + // we only emit `DefaultConfig` if there are trait items, so an empty `DefaultConfig` is + // impossible consequently. + match &config.default_sub_trait { + Some(default_sub_trait) if default_sub_trait.items.len() > 0 => { + let trait_items = &default_sub_trait + .items + .iter() + .map(|item| { + if item.1 { + if let syn::TraitItem::Type(item) = item.0.clone() { + let mut item = item.clone(); + item.bounds.clear(); + syn::TraitItem::Type(item) + } else { + item.0.clone() + } + } else { + item.0.clone() + } + }) + .collect::>(); - let type_param_bounds = if default_sub_trait.has_system { - let system = &def.frame_system; - quote::quote!(: #system::DefaultConfig) - } else { - quote::quote!() - }; + let type_param_bounds = if default_sub_trait.has_system { + let system = &def.frame_system; + quote::quote!(: #system::DefaultConfig) + } else { + quote::quote!() + }; - quote!( - /// Based on [`Config`]. Auto-generated by - /// [`#[pallet::config(with_default)]`](`frame_support::pallet_macros::config`). - /// Can be used in tandem with - /// [`#[register_default_config]`](`frame_support::register_default_config`) and - /// [`#[derive_impl]`](`frame_support::derive_impl`) to derive test config traits - /// based on existing pallet config traits in a safe and developer-friendly way. - /// - /// See [here](`frame_support::pallet_macros::config`) for more information and caveats about - /// the auto-generated `DefaultConfig` trait and how it is generated. - pub trait DefaultConfig #type_param_bounds { - #(#trait_items)* - } - ) - }, - _ => Default::default(), - } + quote!( + /// Based on [`Config`]. Auto-generated by + /// [`#[pallet::config(with_default)]`](`frame_support::pallet_macros::config`). + /// Can be used in tandem with + /// [`#[register_default_config]`](`frame_support::register_default_config`) and + /// [`#[derive_impl]`](`frame_support::derive_impl`) to derive test config traits + /// based on existing pallet config traits in a safe and developer-friendly way. + /// + /// See [here](`frame_support::pallet_macros::config`) for more information and caveats about + /// the auto-generated `DefaultConfig` trait and how it is generated. + pub trait DefaultConfig #type_param_bounds { + #(#trait_items)* + } + ) + } + _ => Default::default(), + } } diff --git a/support/procedural-fork/src/pallet/expand/constants.rs b/support/procedural-fork/src/pallet/expand/constants.rs index 57fa8b7f3..5153ccf49 100644 --- a/support/procedural-fork/src/pallet/expand/constants.rs +++ b/support/procedural-fork/src/pallet/expand/constants.rs @@ -18,91 +18,99 @@ use crate::pallet::Def; struct ConstDef { - /// Name of the associated type. - pub ident: syn::Ident, - /// The type in Get, e.g. `u32` in `type Foo: Get;`, but `Self` is replaced by `T` - pub type_: syn::Type, - /// The doc associated - pub doc: Vec, - /// default_byte implementation - pub default_byte_impl: proc_macro2::TokenStream, - /// Constant name for Metadata (optional) - pub metadata_name: Option, + /// Name of the associated type. + pub ident: syn::Ident, + /// The type in Get, e.g. `u32` in `type Foo: Get;`, but `Self` is replaced by `T` + pub type_: syn::Type, + /// The doc associated + pub doc: Vec, + /// default_byte implementation + pub default_byte_impl: proc_macro2::TokenStream, + /// Constant name for Metadata (optional) + pub metadata_name: Option, } /// /// * Impl fn module_constant_metadata for pallet. pub fn expand_constants(def: &mut Def) -> proc_macro2::TokenStream { - let frame_support = &def.frame_support; - let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site()); - let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site()); - let pallet_ident = &def.pallet_struct.pallet; - let trait_use_gen = &def.trait_use_generics(proc_macro2::Span::call_site()); - - let mut where_clauses = vec![&def.config.where_clause]; - where_clauses.extend(def.extra_constants.iter().map(|d| &d.where_clause)); - let completed_where_clause = super::merge_where_clauses(&where_clauses); - - let config_consts = def.config.consts_metadata.iter().map(|const_| { - let ident = &const_.ident; - let const_type = &const_.type_; - - ConstDef { - ident: const_.ident.clone(), - type_: const_.type_.clone(), - doc: const_.doc.clone(), - default_byte_impl: quote::quote!( - let value = <::#ident as - #frame_support::traits::Get<#const_type>>::get(); - #frame_support::__private::codec::Encode::encode(&value) - ), - metadata_name: None, - } - }); - - let extra_consts = def.extra_constants.iter().flat_map(|d| &d.extra_constants).map(|const_| { - let ident = &const_.ident; - - ConstDef { - ident: const_.ident.clone(), - type_: const_.type_.clone(), - doc: const_.doc.clone(), - default_byte_impl: quote::quote!( - let value = >::#ident(); - #frame_support::__private::codec::Encode::encode(&value) - ), - metadata_name: const_.metadata_name.clone(), - } - }); - - let consts = config_consts.chain(extra_consts).map(|const_| { - let const_type = &const_.type_; - let ident_str = format!("{}", const_.metadata_name.unwrap_or(const_.ident)); - - let no_docs = vec![]; - let doc = if cfg!(feature = "no-metadata-docs") { &no_docs } else { &const_.doc }; - - let default_byte_impl = &const_.default_byte_impl; - - quote::quote!({ - #frame_support::__private::metadata_ir::PalletConstantMetadataIR { - name: #ident_str, - ty: #frame_support::__private::scale_info::meta_type::<#const_type>(), - value: { #default_byte_impl }, - docs: #frame_support::__private::sp_std::vec![ #( #doc ),* ], - } - }) - }); - - quote::quote!( - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause{ - - #[doc(hidden)] - pub fn pallet_constants_metadata() - -> #frame_support::__private::sp_std::vec::Vec<#frame_support::__private::metadata_ir::PalletConstantMetadataIR> - { - #frame_support::__private::sp_std::vec![ #( #consts ),* ] - } - } - ) + let frame_support = &def.frame_support; + let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site()); + let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site()); + let pallet_ident = &def.pallet_struct.pallet; + let trait_use_gen = &def.trait_use_generics(proc_macro2::Span::call_site()); + + let mut where_clauses = vec![&def.config.where_clause]; + where_clauses.extend(def.extra_constants.iter().map(|d| &d.where_clause)); + let completed_where_clause = super::merge_where_clauses(&where_clauses); + + let config_consts = def.config.consts_metadata.iter().map(|const_| { + let ident = &const_.ident; + let const_type = &const_.type_; + + ConstDef { + ident: const_.ident.clone(), + type_: const_.type_.clone(), + doc: const_.doc.clone(), + default_byte_impl: quote::quote!( + let value = <::#ident as + #frame_support::traits::Get<#const_type>>::get(); + #frame_support::__private::codec::Encode::encode(&value) + ), + metadata_name: None, + } + }); + + let extra_consts = def + .extra_constants + .iter() + .flat_map(|d| &d.extra_constants) + .map(|const_| { + let ident = &const_.ident; + + ConstDef { + ident: const_.ident.clone(), + type_: const_.type_.clone(), + doc: const_.doc.clone(), + default_byte_impl: quote::quote!( + let value = >::#ident(); + #frame_support::__private::codec::Encode::encode(&value) + ), + metadata_name: const_.metadata_name.clone(), + } + }); + + let consts = config_consts.chain(extra_consts).map(|const_| { + let const_type = &const_.type_; + let ident_str = format!("{}", const_.metadata_name.unwrap_or(const_.ident)); + + let no_docs = vec![]; + let doc = if cfg!(feature = "no-metadata-docs") { + &no_docs + } else { + &const_.doc + }; + + let default_byte_impl = &const_.default_byte_impl; + + quote::quote!({ + #frame_support::__private::metadata_ir::PalletConstantMetadataIR { + name: #ident_str, + ty: #frame_support::__private::scale_info::meta_type::<#const_type>(), + value: { #default_byte_impl }, + docs: #frame_support::__private::sp_std::vec![ #( #doc ),* ], + } + }) + }); + + quote::quote!( + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause{ + + #[doc(hidden)] + pub fn pallet_constants_metadata() + -> #frame_support::__private::sp_std::vec::Vec<#frame_support::__private::metadata_ir::PalletConstantMetadataIR> + { + #frame_support::__private::sp_std::vec![ #( #consts ),* ] + } + } + ) } diff --git a/support/procedural-fork/src/pallet/expand/doc_only.rs b/support/procedural-fork/src/pallet/expand/doc_only.rs index 621a051ac..3e60e9a9b 100644 --- a/support/procedural-fork/src/pallet/expand/doc_only.rs +++ b/support/procedural-fork/src/pallet/expand/doc_only.rs @@ -20,84 +20,84 @@ use proc_macro2::Span; use crate::pallet::Def; pub fn expand_doc_only(def: &mut Def) -> proc_macro2::TokenStream { - let dispatchables = if let Some(call_def) = &def.call { - let type_impl_generics = def.type_impl_generics(Span::call_site()); - call_def - .methods - .iter() - .map(|method| { - let name = &method.name; - let args = &method - .args - .iter() - .map(|(_, arg_name, arg_type)| quote::quote!( #arg_name: #arg_type, )) - .collect::(); - let docs = &method.docs; + let dispatchables = if let Some(call_def) = &def.call { + let type_impl_generics = def.type_impl_generics(Span::call_site()); + call_def + .methods + .iter() + .map(|method| { + let name = &method.name; + let args = &method + .args + .iter() + .map(|(_, arg_name, arg_type)| quote::quote!( #arg_name: #arg_type, )) + .collect::(); + let docs = &method.docs; - let real = format!(" [`Pallet::{}`].", name); - quote::quote!( - #( #[doc = #docs] )* - /// - /// # Warning: Doc-Only - /// - /// This function is an automatically generated, and is doc-only, uncallable - /// stub. See the real version in - #[ doc = #real ] - pub fn #name<#type_impl_generics>(#args) { unreachable!(); } - ) - }) - .collect::() - } else { - quote::quote!() - }; + let real = format!(" [`Pallet::{}`].", name); + quote::quote!( + #( #[doc = #docs] )* + /// + /// # Warning: Doc-Only + /// + /// This function is an automatically generated, and is doc-only, uncallable + /// stub. See the real version in + #[ doc = #real ] + pub fn #name<#type_impl_generics>(#args) { unreachable!(); } + ) + }) + .collect::() + } else { + quote::quote!() + }; - let storage_types = def - .storages - .iter() - .map(|storage| { - let storage_name = &storage.ident; - let storage_type_docs = &storage.docs; - let real = format!("[`pallet::{}`].", storage_name); - quote::quote!( - #( #[doc = #storage_type_docs] )* - /// - /// # Warning: Doc-Only - /// - /// This type is automatically generated, and is doc-only. See the real version in - #[ doc = #real ] - pub struct #storage_name(); - ) - }) - .collect::(); + let storage_types = def + .storages + .iter() + .map(|storage| { + let storage_name = &storage.ident; + let storage_type_docs = &storage.docs; + let real = format!("[`pallet::{}`].", storage_name); + quote::quote!( + #( #[doc = #storage_type_docs] )* + /// + /// # Warning: Doc-Only + /// + /// This type is automatically generated, and is doc-only. See the real version in + #[ doc = #real ] + pub struct #storage_name(); + ) + }) + .collect::(); - quote::quote!( - /// Auto-generated docs-only module listing all (public and private) defined storage types - /// for this pallet. - /// - /// # Warning: Doc-Only - /// - /// Members of this module cannot be used directly and are only provided for documentation - /// purposes. - /// - /// To see the actual storage type, find a struct with the same name at the root of the - /// pallet, in the list of [*Type Definitions*](../index.html#types). - #[cfg(doc)] - pub mod storage_types { - use super::*; - #storage_types - } + quote::quote!( + /// Auto-generated docs-only module listing all (public and private) defined storage types + /// for this pallet. + /// + /// # Warning: Doc-Only + /// + /// Members of this module cannot be used directly and are only provided for documentation + /// purposes. + /// + /// To see the actual storage type, find a struct with the same name at the root of the + /// pallet, in the list of [*Type Definitions*](../index.html#types). + #[cfg(doc)] + pub mod storage_types { + use super::*; + #storage_types + } - /// Auto-generated docs-only module listing all defined dispatchables for this pallet. - /// - /// # Warning: Doc-Only - /// - /// Members of this module cannot be used directly and are only provided for documentation - /// purposes. To see the real version of each dispatchable, look for them in [`Pallet`] or - /// [`Call`]. - #[cfg(doc)] - pub mod dispatchables { - use super::*; - #dispatchables - } - ) + /// Auto-generated docs-only module listing all defined dispatchables for this pallet. + /// + /// # Warning: Doc-Only + /// + /// Members of this module cannot be used directly and are only provided for documentation + /// purposes. To see the real version of each dispatchable, look for them in [`Pallet`] or + /// [`Call`]. + #[cfg(doc)] + pub mod dispatchables { + use super::*; + #dispatchables + } + ) } diff --git a/support/procedural-fork/src/pallet/expand/documentation.rs b/support/procedural-fork/src/pallet/expand/documentation.rs index ec19f889a..adc4f7ce9 100644 --- a/support/procedural-fork/src/pallet/expand/documentation.rs +++ b/support/procedural-fork/src/pallet/expand/documentation.rs @@ -28,12 +28,12 @@ const PALLET_DOC: &'static str = "pallet_doc"; /// Supported format: /// `#[pallet_doc(PATH)]`: The path of the file from which the documentation is loaded fn parse_pallet_doc_value(attr: &Attribute) -> syn::Result { - let lit: syn::LitStr = attr.parse_args().map_err(|_| { + let lit: syn::LitStr = attr.parse_args().map_err(|_| { let msg = "The `pallet_doc` received an unsupported argument. Supported format: `pallet_doc(\"PATH\")`"; syn::Error::new(attr.span(), msg) })?; - Ok(DocMetaValue::Path(lit)) + Ok(DocMetaValue::Path(lit)) } /// Get the value from the `doc` comment attribute: @@ -42,46 +42,49 @@ fn parse_pallet_doc_value(attr: &Attribute) -> syn::Result { /// - `#[doc = "A doc string"]`: Documentation as a string literal /// - `#[doc = include_str!(PATH)]`: Documentation obtained from a path fn parse_doc_value(attr: &Attribute) -> syn::Result> { - if !attr.path().is_ident(DOC) { - return Ok(None) - } - - let meta = attr.meta.require_name_value()?; - - match &meta.value { - syn::Expr::Lit(lit) => Ok(Some(DocMetaValue::Lit(lit.lit.clone()))), - syn::Expr::Macro(mac) if mac.mac.path.is_ident("include_str") => - Ok(Some(DocMetaValue::Path(mac.mac.parse_body()?))), - _ => - Err(syn::Error::new(attr.span(), "Expected `= \"docs\"` or `= include_str!(\"PATH\")`")), - } + if !attr.path().is_ident(DOC) { + return Ok(None); + } + + let meta = attr.meta.require_name_value()?; + + match &meta.value { + syn::Expr::Lit(lit) => Ok(Some(DocMetaValue::Lit(lit.lit.clone()))), + syn::Expr::Macro(mac) if mac.mac.path.is_ident("include_str") => { + Ok(Some(DocMetaValue::Path(mac.mac.parse_body()?))) + } + _ => Err(syn::Error::new( + attr.span(), + "Expected `= \"docs\"` or `= include_str!(\"PATH\")`", + )), + } } /// Supported documentation tokens. #[derive(Debug)] enum DocMetaValue { - /// Documentation with string literals. - /// - /// `#[doc = "Lit"]` - Lit(Lit), - /// Documentation with `include_str!` macro. - /// - /// The string literal represents the file `PATH`. - /// - /// `#[doc = include_str!(PATH)]` - Path(LitStr), + /// Documentation with string literals. + /// + /// `#[doc = "Lit"]` + Lit(Lit), + /// Documentation with `include_str!` macro. + /// + /// The string literal represents the file `PATH`. + /// + /// `#[doc = include_str!(PATH)]` + Path(LitStr), } impl ToTokens for DocMetaValue { - fn to_tokens(&self, tokens: &mut TokenStream) { - match self { - DocMetaValue::Lit(lit) => lit.to_tokens(tokens), - DocMetaValue::Path(path_lit) => { - let decl = quote::quote!(include_str!(#path_lit)); - tokens.extend(decl) - }, - } - } + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + DocMetaValue::Lit(lit) => lit.to_tokens(tokens), + DocMetaValue::Path(path_lit) => { + let decl = quote::quote!(include_str!(#path_lit)); + tokens.extend(decl) + } + } + } } /// Extract the documentation from the given pallet definition @@ -110,63 +113,63 @@ impl ToTokens for DocMetaValue { /// Unlike the `doc` attribute, the documentation provided to the `proc_macro` attribute is /// not added to the pallet. pub fn expand_documentation(def: &mut Def) -> proc_macro2::TokenStream { - let frame_support = &def.frame_support; - let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site()); - let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site()); - let pallet_ident = &def.pallet_struct.pallet; - let where_clauses = &def.config.where_clause; - - // TODO: Use [drain_filter](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.drain_filter) when it is stable. - - // The `pallet_doc` attributes are excluded from the generation of the pallet, - // but they are included in the runtime metadata. - let mut pallet_docs = Vec::with_capacity(def.item.attrs.len()); - let mut index = 0; - while index < def.item.attrs.len() { - let attr = &def.item.attrs[index]; - if attr.path().get_ident().map_or(false, |i| *i == PALLET_DOC) { - pallet_docs.push(def.item.attrs.remove(index)); - // Do not increment the index, we have just removed the - // element from the attributes. - continue - } - - index += 1; - } - - // Capture the `#[doc = include_str!("../README.md")]` and `#[doc = "Documentation"]`. - let docs = match def - .item - .attrs - .iter() - .filter_map(|v| parse_doc_value(v).transpose()) - .collect::>>() - { - Ok(r) => r, - Err(err) => return err.into_compile_error(), - }; - - // Capture the `#[pallet_doc("../README.md")]`. - let pallet_docs = match pallet_docs - .into_iter() - .map(|attr| parse_pallet_doc_value(&attr)) - .collect::>>() - { - Ok(docs) => docs, - Err(err) => return err.into_compile_error(), - }; - - let docs = docs.iter().chain(pallet_docs.iter()); - - quote::quote!( - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #where_clauses{ - - #[doc(hidden)] - pub fn pallet_documentation_metadata() - -> #frame_support::__private::sp_std::vec::Vec<&'static str> - { - #frame_support::__private::sp_std::vec![ #( #docs ),* ] - } - } - ) + let frame_support = &def.frame_support; + let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site()); + let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site()); + let pallet_ident = &def.pallet_struct.pallet; + let where_clauses = &def.config.where_clause; + + // TODO: Use [drain_filter](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.drain_filter) when it is stable. + + // The `pallet_doc` attributes are excluded from the generation of the pallet, + // but they are included in the runtime metadata. + let mut pallet_docs = Vec::with_capacity(def.item.attrs.len()); + let mut index = 0; + while index < def.item.attrs.len() { + let attr = &def.item.attrs[index]; + if attr.path().get_ident().map_or(false, |i| *i == PALLET_DOC) { + pallet_docs.push(def.item.attrs.remove(index)); + // Do not increment the index, we have just removed the + // element from the attributes. + continue; + } + + index += 1; + } + + // Capture the `#[doc = include_str!("../README.md")]` and `#[doc = "Documentation"]`. + let docs = match def + .item + .attrs + .iter() + .filter_map(|v| parse_doc_value(v).transpose()) + .collect::>>() + { + Ok(r) => r, + Err(err) => return err.into_compile_error(), + }; + + // Capture the `#[pallet_doc("../README.md")]`. + let pallet_docs = match pallet_docs + .into_iter() + .map(|attr| parse_pallet_doc_value(&attr)) + .collect::>>() + { + Ok(docs) => docs, + Err(err) => return err.into_compile_error(), + }; + + let docs = docs.iter().chain(pallet_docs.iter()); + + quote::quote!( + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #where_clauses{ + + #[doc(hidden)] + pub fn pallet_documentation_metadata() + -> #frame_support::__private::sp_std::vec::Vec<&'static str> + { + #frame_support::__private::sp_std::vec![ #( #docs ),* ] + } + } + ) } diff --git a/support/procedural-fork/src/pallet/expand/error.rs b/support/procedural-fork/src/pallet/expand/error.rs index 72fb6e923..e2c3f680c 100644 --- a/support/procedural-fork/src/pallet/expand/error.rs +++ b/support/procedural-fork/src/pallet/expand/error.rs @@ -16,11 +16,11 @@ // limitations under the License. use crate::{ - pallet::{ - parse::error::{VariantDef, VariantField}, - Def, - }, - COUNTER, + pallet::{ + parse::error::{VariantDef, VariantField}, + Def, + }, + COUNTER, }; use frame_support_procedural_tools::get_doc_literals; use quote::ToTokens; @@ -29,49 +29,49 @@ use syn::spanned::Spanned; /// /// * impl various trait on Error pub fn expand_error(def: &mut Def) -> proc_macro2::TokenStream { - let count = COUNTER.with(|counter| counter.borrow_mut().inc()); - let error_token_unique_id = - syn::Ident::new(&format!("__tt_error_token_{}", count), def.item.span()); - - let frame_support = &def.frame_support; - let frame_system = &def.frame_system; - let config_where_clause = &def.config.where_clause; - - let error = if let Some(error) = &def.error { - error - } else { - return quote::quote! { - #[macro_export] - #[doc(hidden)] - macro_rules! #error_token_unique_id { - { - $caller:tt - your_tt_return = [{ $my_tt_return:path }] - } => { - $my_tt_return! { - $caller - } - }; - } - - pub use #error_token_unique_id as tt_error_token; - } - }; - - let error_ident = &error.error; - let type_impl_gen = &def.type_impl_generics(error.attr_span); - let type_use_gen = &def.type_use_generics(error.attr_span); - - let phantom_variant: syn::Variant = syn::parse_quote!( - #[doc(hidden)] - #[codec(skip)] - __Ignore( - #frame_support::__private::sp_std::marker::PhantomData<(#type_use_gen)>, - #frame_support::Never, - ) - ); - - let as_str_matches = error.variants.iter().map( + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + let error_token_unique_id = + syn::Ident::new(&format!("__tt_error_token_{}", count), def.item.span()); + + let frame_support = &def.frame_support; + let frame_system = &def.frame_system; + let config_where_clause = &def.config.where_clause; + + let error = if let Some(error) = &def.error { + error + } else { + return quote::quote! { + #[macro_export] + #[doc(hidden)] + macro_rules! #error_token_unique_id { + { + $caller:tt + your_tt_return = [{ $my_tt_return:path }] + } => { + $my_tt_return! { + $caller + } + }; + } + + pub use #error_token_unique_id as tt_error_token; + }; + }; + + let error_ident = &error.error; + let type_impl_gen = &def.type_impl_generics(error.attr_span); + let type_use_gen = &def.type_use_generics(error.attr_span); + + let phantom_variant: syn::Variant = syn::parse_quote!( + #[doc(hidden)] + #[codec(skip)] + __Ignore( + #frame_support::__private::sp_std::marker::PhantomData<(#type_use_gen)>, + #frame_support::Never, + ) + ); + + let as_str_matches = error.variants.iter().map( |VariantDef { ident: variant, field: field_ty, docs: _, cfg_attrs }| { let variant_str = variant.to_string(); let cfg_attrs = cfg_attrs.iter().map(|attr| attr.to_token_stream()); @@ -89,103 +89,107 @@ pub fn expand_error(def: &mut Def) -> proc_macro2::TokenStream { }, ); - let error_item = { - let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[error.index]; - if let syn::Item::Enum(item) = item { - item - } else { - unreachable!("Checked by error parser") - } - }; - - error_item.variants.insert(0, phantom_variant); - - let capture_docs = if cfg!(feature = "no-metadata-docs") { "never" } else { "always" }; - - // derive TypeInfo for error metadata - error_item.attrs.push(syn::parse_quote! { - #[derive( - #frame_support::__private::codec::Encode, - #frame_support::__private::codec::Decode, - #frame_support::__private::scale_info::TypeInfo, - #frame_support::PalletError, - )] - }); - error_item.attrs.push(syn::parse_quote!( - #[scale_info(skip_type_params(#type_use_gen), capture_docs = #capture_docs)] - )); - - if get_doc_literals(&error_item.attrs).is_empty() { - error_item.attrs.push(syn::parse_quote!( - #[doc = "The `Error` enum of this pallet."] - )); - } - - quote::quote_spanned!(error.attr_span => - impl<#type_impl_gen> #frame_support::__private::sp_std::fmt::Debug for #error_ident<#type_use_gen> - #config_where_clause - { - fn fmt(&self, f: &mut #frame_support::__private::sp_std::fmt::Formatter<'_>) - -> #frame_support::__private::sp_std::fmt::Result - { - f.write_str(self.as_str()) - } - } - - impl<#type_impl_gen> #error_ident<#type_use_gen> #config_where_clause { - #[doc(hidden)] - pub fn as_str(&self) -> &'static str { - match &self { - Self::__Ignore(_, _) => unreachable!("`__Ignore` can never be constructed"), - #( #as_str_matches )* - } - } - } - - impl<#type_impl_gen> From<#error_ident<#type_use_gen>> for &'static str - #config_where_clause - { - fn from(err: #error_ident<#type_use_gen>) -> &'static str { - err.as_str() - } - } - - impl<#type_impl_gen> From<#error_ident<#type_use_gen>> - for #frame_support::sp_runtime::DispatchError - #config_where_clause - { - fn from(err: #error_ident<#type_use_gen>) -> Self { - use #frame_support::__private::codec::Encode; - let index = < - ::PalletInfo - as #frame_support::traits::PalletInfo - >::index::>() - .expect("Every active module has an index in the runtime; qed") as u8; - let mut encoded = err.encode(); - encoded.resize(#frame_support::MAX_MODULE_ERROR_ENCODED_SIZE, 0); - - #frame_support::sp_runtime::DispatchError::Module(#frame_support::sp_runtime::ModuleError { - index, - error: TryInto::try_into(encoded).expect("encoded error is resized to be equal to the maximum encoded error size; qed"), - message: Some(err.as_str()), - }) - } - } - - #[macro_export] - #[doc(hidden)] - macro_rules! #error_token_unique_id { - { - $caller:tt - your_tt_return = [{ $my_tt_return:path }] - } => { - $my_tt_return! { - $caller - error = [{ #error_ident }] - } - }; - } - - pub use #error_token_unique_id as tt_error_token; - ) + let error_item = { + let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[error.index]; + if let syn::Item::Enum(item) = item { + item + } else { + unreachable!("Checked by error parser") + } + }; + + error_item.variants.insert(0, phantom_variant); + + let capture_docs = if cfg!(feature = "no-metadata-docs") { + "never" + } else { + "always" + }; + + // derive TypeInfo for error metadata + error_item.attrs.push(syn::parse_quote! { + #[derive( + #frame_support::__private::codec::Encode, + #frame_support::__private::codec::Decode, + #frame_support::__private::scale_info::TypeInfo, + #frame_support::PalletError, + )] + }); + error_item.attrs.push(syn::parse_quote!( + #[scale_info(skip_type_params(#type_use_gen), capture_docs = #capture_docs)] + )); + + if get_doc_literals(&error_item.attrs).is_empty() { + error_item.attrs.push(syn::parse_quote!( + #[doc = "The `Error` enum of this pallet."] + )); + } + + quote::quote_spanned!(error.attr_span => + impl<#type_impl_gen> #frame_support::__private::sp_std::fmt::Debug for #error_ident<#type_use_gen> + #config_where_clause + { + fn fmt(&self, f: &mut #frame_support::__private::sp_std::fmt::Formatter<'_>) + -> #frame_support::__private::sp_std::fmt::Result + { + f.write_str(self.as_str()) + } + } + + impl<#type_impl_gen> #error_ident<#type_use_gen> #config_where_clause { + #[doc(hidden)] + pub fn as_str(&self) -> &'static str { + match &self { + Self::__Ignore(_, _) => unreachable!("`__Ignore` can never be constructed"), + #( #as_str_matches )* + } + } + } + + impl<#type_impl_gen> From<#error_ident<#type_use_gen>> for &'static str + #config_where_clause + { + fn from(err: #error_ident<#type_use_gen>) -> &'static str { + err.as_str() + } + } + + impl<#type_impl_gen> From<#error_ident<#type_use_gen>> + for #frame_support::sp_runtime::DispatchError + #config_where_clause + { + fn from(err: #error_ident<#type_use_gen>) -> Self { + use #frame_support::__private::codec::Encode; + let index = < + ::PalletInfo + as #frame_support::traits::PalletInfo + >::index::>() + .expect("Every active module has an index in the runtime; qed") as u8; + let mut encoded = err.encode(); + encoded.resize(#frame_support::MAX_MODULE_ERROR_ENCODED_SIZE, 0); + + #frame_support::sp_runtime::DispatchError::Module(#frame_support::sp_runtime::ModuleError { + index, + error: TryInto::try_into(encoded).expect("encoded error is resized to be equal to the maximum encoded error size; qed"), + message: Some(err.as_str()), + }) + } + } + + #[macro_export] + #[doc(hidden)] + macro_rules! #error_token_unique_id { + { + $caller:tt + your_tt_return = [{ $my_tt_return:path }] + } => { + $my_tt_return! { + $caller + error = [{ #error_ident }] + } + }; + } + + pub use #error_token_unique_id as tt_error_token; + ) } diff --git a/support/procedural-fork/src/pallet/expand/event.rs b/support/procedural-fork/src/pallet/expand/event.rs index 655fc5507..931dcd95a 100644 --- a/support/procedural-fork/src/pallet/expand/event.rs +++ b/support/procedural-fork/src/pallet/expand/event.rs @@ -16,8 +16,8 @@ // limitations under the License. use crate::{ - pallet::{parse::event::PalletEventDepositAttr, Def}, - COUNTER, + pallet::{parse::event::PalletEventDepositAttr, Def}, + COUNTER, }; use frame_support_procedural_tools::get_doc_literals; use syn::{spanned::Spanned, Ident}; @@ -27,148 +27,159 @@ use syn::{spanned::Spanned, Ident}; /// * Impl various trait on Event including metadata /// * if deposit_event is defined, implement deposit_event on module. pub fn expand_event(def: &mut Def) -> proc_macro2::TokenStream { - let count = COUNTER.with(|counter| counter.borrow_mut().inc()); - - let (event, macro_ident) = if let Some(event) = &def.event { - let ident = Ident::new(&format!("__is_event_part_defined_{}", count), event.attr_span); - (event, ident) - } else { - let macro_ident = - Ident::new(&format!("__is_event_part_defined_{}", count), def.item.span()); - - return quote::quote! { - #[doc(hidden)] - pub mod __substrate_event_check { - #[macro_export] - #[doc(hidden)] - macro_rules! #macro_ident { - ($pallet_name:ident) => { - compile_error!(concat!( - "`", - stringify!($pallet_name), - "` does not have #[pallet::event] defined, perhaps you should \ - remove `Event` from construct_runtime?", - )); - } - } - - #[doc(hidden)] - pub use #macro_ident as is_event_part_defined; - } - } - }; - - let event_where_clause = &event.where_clause; - - // NOTE: actually event where clause must be a subset of config where clause because of - // `type RuntimeEvent: From>`. But we merge either way for potential better error - // message - let completed_where_clause = - super::merge_where_clauses(&[&event.where_clause, &def.config.where_clause]); - - let event_ident = &event.event; - let frame_system = &def.frame_system; - let frame_support = &def.frame_support; - let event_use_gen = &event.gen_kind.type_use_gen(event.attr_span); - let event_impl_gen = &event.gen_kind.type_impl_gen(event.attr_span); - - let event_item = { - let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[event.index]; - if let syn::Item::Enum(item) = item { - item - } else { - unreachable!("Checked by event parser") - } - }; - - // Phantom data is added for generic event. - if event.gen_kind.is_generic() { - let variant = syn::parse_quote!( - #[doc(hidden)] - #[codec(skip)] - __Ignore( - ::core::marker::PhantomData<(#event_use_gen)>, - #frame_support::Never, - ) - ); - - // Push ignore variant at the end. - event_item.variants.push(variant); - } - - if get_doc_literals(&event_item.attrs).is_empty() { - event_item - .attrs - .push(syn::parse_quote!(#[doc = "The `Event` enum of this pallet"])); - } - - // derive some traits because system event require Clone, FullCodec, Eq, PartialEq and Debug - event_item.attrs.push(syn::parse_quote!( - #[derive( - #frame_support::CloneNoBound, - #frame_support::EqNoBound, - #frame_support::PartialEqNoBound, - #frame_support::RuntimeDebugNoBound, - #frame_support::__private::codec::Encode, - #frame_support::__private::codec::Decode, - #frame_support::__private::scale_info::TypeInfo, - )] - )); - - let capture_docs = if cfg!(feature = "no-metadata-docs") { "never" } else { "always" }; - - // skip requirement for type params to implement `TypeInfo`, and set docs capture - event_item.attrs.push(syn::parse_quote!( - #[scale_info(skip_type_params(#event_use_gen), capture_docs = #capture_docs)] - )); - - let deposit_event = if let Some(deposit_event) = &event.deposit_event { - let event_use_gen = &event.gen_kind.type_use_gen(event.attr_span); - let trait_use_gen = &def.trait_use_generics(event.attr_span); - let type_impl_gen = &def.type_impl_generics(event.attr_span); - let type_use_gen = &def.type_use_generics(event.attr_span); - let pallet_ident = &def.pallet_struct.pallet; - - let PalletEventDepositAttr { fn_vis, fn_span, .. } = deposit_event; - - quote::quote_spanned!(*fn_span => - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { - #fn_vis fn deposit_event(event: Event<#event_use_gen>) { - let event = < - ::RuntimeEvent as - From> - >::from(event); - - let event = < - ::RuntimeEvent as - Into<::RuntimeEvent> - >::into(event); - - <#frame_system::Pallet>::deposit_event(event) - } - } - ) - } else { - Default::default() - }; - - quote::quote_spanned!(event.attr_span => - #[doc(hidden)] - pub mod __substrate_event_check { - #[macro_export] - #[doc(hidden)] - macro_rules! #macro_ident { - ($pallet_name:ident) => {}; - } - - #[doc(hidden)] - pub use #macro_ident as is_event_part_defined; - } - - #deposit_event - - impl<#event_impl_gen> From<#event_ident<#event_use_gen>> for () #event_where_clause { - fn from(_: #event_ident<#event_use_gen>) {} - } - ) + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + + let (event, macro_ident) = if let Some(event) = &def.event { + let ident = Ident::new( + &format!("__is_event_part_defined_{}", count), + event.attr_span, + ); + (event, ident) + } else { + let macro_ident = Ident::new( + &format!("__is_event_part_defined_{}", count), + def.item.span(), + ); + + return quote::quote! { + #[doc(hidden)] + pub mod __substrate_event_check { + #[macro_export] + #[doc(hidden)] + macro_rules! #macro_ident { + ($pallet_name:ident) => { + compile_error!(concat!( + "`", + stringify!($pallet_name), + "` does not have #[pallet::event] defined, perhaps you should \ + remove `Event` from construct_runtime?", + )); + } + } + + #[doc(hidden)] + pub use #macro_ident as is_event_part_defined; + } + }; + }; + + let event_where_clause = &event.where_clause; + + // NOTE: actually event where clause must be a subset of config where clause because of + // `type RuntimeEvent: From>`. But we merge either way for potential better error + // message + let completed_where_clause = + super::merge_where_clauses(&[&event.where_clause, &def.config.where_clause]); + + let event_ident = &event.event; + let frame_system = &def.frame_system; + let frame_support = &def.frame_support; + let event_use_gen = &event.gen_kind.type_use_gen(event.attr_span); + let event_impl_gen = &event.gen_kind.type_impl_gen(event.attr_span); + + let event_item = { + let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[event.index]; + if let syn::Item::Enum(item) = item { + item + } else { + unreachable!("Checked by event parser") + } + }; + + // Phantom data is added for generic event. + if event.gen_kind.is_generic() { + let variant = syn::parse_quote!( + #[doc(hidden)] + #[codec(skip)] + __Ignore( + ::core::marker::PhantomData<(#event_use_gen)>, + #frame_support::Never, + ) + ); + + // Push ignore variant at the end. + event_item.variants.push(variant); + } + + if get_doc_literals(&event_item.attrs).is_empty() { + event_item + .attrs + .push(syn::parse_quote!(#[doc = "The `Event` enum of this pallet"])); + } + + // derive some traits because system event require Clone, FullCodec, Eq, PartialEq and Debug + event_item.attrs.push(syn::parse_quote!( + #[derive( + #frame_support::CloneNoBound, + #frame_support::EqNoBound, + #frame_support::PartialEqNoBound, + #frame_support::RuntimeDebugNoBound, + #frame_support::__private::codec::Encode, + #frame_support::__private::codec::Decode, + #frame_support::__private::scale_info::TypeInfo, + )] + )); + + let capture_docs = if cfg!(feature = "no-metadata-docs") { + "never" + } else { + "always" + }; + + // skip requirement for type params to implement `TypeInfo`, and set docs capture + event_item.attrs.push(syn::parse_quote!( + #[scale_info(skip_type_params(#event_use_gen), capture_docs = #capture_docs)] + )); + + let deposit_event = if let Some(deposit_event) = &event.deposit_event { + let event_use_gen = &event.gen_kind.type_use_gen(event.attr_span); + let trait_use_gen = &def.trait_use_generics(event.attr_span); + let type_impl_gen = &def.type_impl_generics(event.attr_span); + let type_use_gen = &def.type_use_generics(event.attr_span); + let pallet_ident = &def.pallet_struct.pallet; + + let PalletEventDepositAttr { + fn_vis, fn_span, .. + } = deposit_event; + + quote::quote_spanned!(*fn_span => + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { + #fn_vis fn deposit_event(event: Event<#event_use_gen>) { + let event = < + ::RuntimeEvent as + From> + >::from(event); + + let event = < + ::RuntimeEvent as + Into<::RuntimeEvent> + >::into(event); + + <#frame_system::Pallet>::deposit_event(event) + } + } + ) + } else { + Default::default() + }; + + quote::quote_spanned!(event.attr_span => + #[doc(hidden)] + pub mod __substrate_event_check { + #[macro_export] + #[doc(hidden)] + macro_rules! #macro_ident { + ($pallet_name:ident) => {}; + } + + #[doc(hidden)] + pub use #macro_ident as is_event_part_defined; + } + + #deposit_event + + impl<#event_impl_gen> From<#event_ident<#event_use_gen>> for () #event_where_clause { + fn from(_: #event_ident<#event_use_gen>) {} + } + ) } diff --git a/support/procedural-fork/src/pallet/expand/genesis_build.rs b/support/procedural-fork/src/pallet/expand/genesis_build.rs index 248e83469..c6089550d 100644 --- a/support/procedural-fork/src/pallet/expand/genesis_build.rs +++ b/support/procedural-fork/src/pallet/expand/genesis_build.rs @@ -20,30 +20,34 @@ use crate::pallet::Def; /// /// * implement the trait `sp_runtime::BuildStorage` pub fn expand_genesis_build(def: &mut Def) -> proc_macro2::TokenStream { - let genesis_config = if let Some(genesis_config) = &def.genesis_config { - genesis_config - } else { - return Default::default() - }; - let genesis_build = def.genesis_build.as_ref().expect("Checked by def parser"); + let genesis_config = if let Some(genesis_config) = &def.genesis_config { + genesis_config + } else { + return Default::default(); + }; + let genesis_build = def.genesis_build.as_ref().expect("Checked by def parser"); - let frame_support = &def.frame_support; - let type_impl_gen = &genesis_config.gen_kind.type_impl_gen(genesis_build.attr_span); - let gen_cfg_ident = &genesis_config.genesis_config; - let gen_cfg_use_gen = &genesis_config.gen_kind.type_use_gen(genesis_build.attr_span); + let frame_support = &def.frame_support; + let type_impl_gen = &genesis_config + .gen_kind + .type_impl_gen(genesis_build.attr_span); + let gen_cfg_ident = &genesis_config.genesis_config; + let gen_cfg_use_gen = &genesis_config + .gen_kind + .type_use_gen(genesis_build.attr_span); - let where_clause = &genesis_build.where_clause; + let where_clause = &genesis_build.where_clause; - quote::quote_spanned!(genesis_build.attr_span => - #[cfg(feature = "std")] - impl<#type_impl_gen> #frame_support::sp_runtime::BuildStorage for #gen_cfg_ident<#gen_cfg_use_gen> #where_clause - { - fn assimilate_storage(&self, storage: &mut #frame_support::sp_runtime::Storage) -> std::result::Result<(), std::string::String> { - #frame_support::__private::BasicExternalities::execute_with_storage(storage, || { - self.build(); - Ok(()) - }) - } - } - ) + quote::quote_spanned!(genesis_build.attr_span => + #[cfg(feature = "std")] + impl<#type_impl_gen> #frame_support::sp_runtime::BuildStorage for #gen_cfg_ident<#gen_cfg_use_gen> #where_clause + { + fn assimilate_storage(&self, storage: &mut #frame_support::sp_runtime::Storage) -> std::result::Result<(), std::string::String> { + #frame_support::__private::BasicExternalities::execute_with_storage(storage, || { + self.build(); + Ok(()) + }) + } + } + ) } diff --git a/support/procedural-fork/src/pallet/expand/genesis_config.rs b/support/procedural-fork/src/pallet/expand/genesis_config.rs index 31d519ef2..e171e2468 100644 --- a/support/procedural-fork/src/pallet/expand/genesis_config.rs +++ b/support/procedural-fork/src/pallet/expand/genesis_config.rs @@ -23,125 +23,130 @@ use syn::{spanned::Spanned, Ident}; /// /// * add various derive trait on GenesisConfig struct. pub fn expand_genesis_config(def: &mut Def) -> proc_macro2::TokenStream { - let count = COUNTER.with(|counter| counter.borrow_mut().inc()); - - let (genesis_config, def_macro_ident, std_macro_ident) = - if let Some(genesis_config) = &def.genesis_config { - let def_macro_ident = Ident::new( - &format!("__is_genesis_config_defined_{}", count), - genesis_config.genesis_config.span(), - ); - - let std_macro_ident = Ident::new( - &format!("__is_std_macro_defined_for_genesis_{}", count), - genesis_config.genesis_config.span(), - ); - - (genesis_config, def_macro_ident, std_macro_ident) - } else { - let def_macro_ident = - Ident::new(&format!("__is_genesis_config_defined_{}", count), def.item.span()); - - let std_macro_ident = - Ident::new(&format!("__is_std_enabled_for_genesis_{}", count), def.item.span()); - - return quote::quote! { - #[doc(hidden)] - pub mod __substrate_genesis_config_check { - #[macro_export] - #[doc(hidden)] - macro_rules! #def_macro_ident { - ($pallet_name:ident) => { - compile_error!(concat!( - "`", - stringify!($pallet_name), - "` does not have #[pallet::genesis_config] defined, perhaps you should \ - remove `Config` from construct_runtime?", - )); - } - } - - #[macro_export] - #[doc(hidden)] - macro_rules! #std_macro_ident { - ($pallet_name:ident, $pallet_path:expr) => {}; - } - - #[doc(hidden)] - pub use #def_macro_ident as is_genesis_config_defined; - #[doc(hidden)] - pub use #std_macro_ident as is_std_enabled_for_genesis; - } - } - }; - - let frame_support = &def.frame_support; - - let genesis_config_item = - &mut def.item.content.as_mut().expect("Checked by def parser").1[genesis_config.index]; - - let serde_crate = format!("{}::__private::serde", frame_support.to_token_stream()); - - match genesis_config_item { - syn::Item::Enum(syn::ItemEnum { attrs, .. }) | - syn::Item::Struct(syn::ItemStruct { attrs, .. }) | - syn::Item::Type(syn::ItemType { attrs, .. }) => { - if get_doc_literals(attrs).is_empty() { - attrs.push(syn::parse_quote!( - #[doc = r" + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + + let (genesis_config, def_macro_ident, std_macro_ident) = if let Some(genesis_config) = + &def.genesis_config + { + let def_macro_ident = Ident::new( + &format!("__is_genesis_config_defined_{}", count), + genesis_config.genesis_config.span(), + ); + + let std_macro_ident = Ident::new( + &format!("__is_std_macro_defined_for_genesis_{}", count), + genesis_config.genesis_config.span(), + ); + + (genesis_config, def_macro_ident, std_macro_ident) + } else { + let def_macro_ident = Ident::new( + &format!("__is_genesis_config_defined_{}", count), + def.item.span(), + ); + + let std_macro_ident = Ident::new( + &format!("__is_std_enabled_for_genesis_{}", count), + def.item.span(), + ); + + return quote::quote! { + #[doc(hidden)] + pub mod __substrate_genesis_config_check { + #[macro_export] + #[doc(hidden)] + macro_rules! #def_macro_ident { + ($pallet_name:ident) => { + compile_error!(concat!( + "`", + stringify!($pallet_name), + "` does not have #[pallet::genesis_config] defined, perhaps you should \ + remove `Config` from construct_runtime?", + )); + } + } + + #[macro_export] + #[doc(hidden)] + macro_rules! #std_macro_ident { + ($pallet_name:ident, $pallet_path:expr) => {}; + } + + #[doc(hidden)] + pub use #def_macro_ident as is_genesis_config_defined; + #[doc(hidden)] + pub use #std_macro_ident as is_std_enabled_for_genesis; + } + }; + }; + + let frame_support = &def.frame_support; + + let genesis_config_item = + &mut def.item.content.as_mut().expect("Checked by def parser").1[genesis_config.index]; + + let serde_crate = format!("{}::__private::serde", frame_support.to_token_stream()); + + match genesis_config_item { + syn::Item::Enum(syn::ItemEnum { attrs, .. }) + | syn::Item::Struct(syn::ItemStruct { attrs, .. }) + | syn::Item::Type(syn::ItemType { attrs, .. }) => { + if get_doc_literals(attrs).is_empty() { + attrs.push(syn::parse_quote!( + #[doc = r" Can be used to configure the [genesis state](https://docs.substrate.io/build/genesis-configuration/) of this pallet. "] - )); - } - attrs.push(syn::parse_quote!( - #[derive(#frame_support::Serialize, #frame_support::Deserialize)] - )); - attrs.push(syn::parse_quote!( #[serde(rename_all = "camelCase")] )); - attrs.push(syn::parse_quote!( #[serde(deny_unknown_fields)] )); - attrs.push(syn::parse_quote!( #[serde(bound(serialize = ""))] )); - attrs.push(syn::parse_quote!( #[serde(bound(deserialize = ""))] )); - attrs.push(syn::parse_quote!( #[serde(crate = #serde_crate)] )); - }, - _ => unreachable!("Checked by genesis_config parser"), - } - - quote::quote! { - #[doc(hidden)] - pub mod __substrate_genesis_config_check { - #[macro_export] - #[doc(hidden)] - macro_rules! #def_macro_ident { - ($pallet_name:ident) => {}; - } - - #[cfg(not(feature = "std"))] - #[macro_export] - #[doc(hidden)] - macro_rules! #std_macro_ident { - ($pallet_name:ident, $pallet_path:expr) => { - compile_error!(concat!( - "`", - stringify!($pallet_name), - "` does not have the std feature enabled, this will cause the `", - $pallet_path, - "::GenesisConfig` type to not implement serde traits." - )); - }; - } - - #[cfg(feature = "std")] - #[macro_export] - #[doc(hidden)] - macro_rules! #std_macro_ident { - ($pallet_name:ident, $pallet_path:expr) => {}; - } - - #[doc(hidden)] - pub use #def_macro_ident as is_genesis_config_defined; - #[doc(hidden)] - pub use #std_macro_ident as is_std_enabled_for_genesis; - } - } + )); + } + attrs.push(syn::parse_quote!( + #[derive(#frame_support::Serialize, #frame_support::Deserialize)] + )); + attrs.push(syn::parse_quote!( #[serde(rename_all = "camelCase")] )); + attrs.push(syn::parse_quote!( #[serde(deny_unknown_fields)] )); + attrs.push(syn::parse_quote!( #[serde(bound(serialize = ""))] )); + attrs.push(syn::parse_quote!( #[serde(bound(deserialize = ""))] )); + attrs.push(syn::parse_quote!( #[serde(crate = #serde_crate)] )); + } + _ => unreachable!("Checked by genesis_config parser"), + } + + quote::quote! { + #[doc(hidden)] + pub mod __substrate_genesis_config_check { + #[macro_export] + #[doc(hidden)] + macro_rules! #def_macro_ident { + ($pallet_name:ident) => {}; + } + + #[cfg(not(feature = "std"))] + #[macro_export] + #[doc(hidden)] + macro_rules! #std_macro_ident { + ($pallet_name:ident, $pallet_path:expr) => { + compile_error!(concat!( + "`", + stringify!($pallet_name), + "` does not have the std feature enabled, this will cause the `", + $pallet_path, + "::GenesisConfig` type to not implement serde traits." + )); + }; + } + + #[cfg(feature = "std")] + #[macro_export] + #[doc(hidden)] + macro_rules! #std_macro_ident { + ($pallet_name:ident, $pallet_path:expr) => {}; + } + + #[doc(hidden)] + pub use #def_macro_ident as is_genesis_config_defined; + #[doc(hidden)] + pub use #std_macro_ident as is_std_enabled_for_genesis; + } + } } diff --git a/support/procedural-fork/src/pallet/expand/hooks.rs b/support/procedural-fork/src/pallet/expand/hooks.rs index 3623b5952..6967f4c08 100644 --- a/support/procedural-fork/src/pallet/expand/hooks.rs +++ b/support/procedural-fork/src/pallet/expand/hooks.rs @@ -19,322 +19,326 @@ use crate::pallet::Def; /// * implement the individual traits using the Hooks trait pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { - let (where_clause, span, has_runtime_upgrade) = match def.hooks.as_ref() { - Some(hooks) => { - let where_clause = hooks.where_clause.clone(); - let span = hooks.attr_span; - let has_runtime_upgrade = hooks.has_runtime_upgrade; - (where_clause, span, has_runtime_upgrade) - }, - None => (def.config.where_clause.clone(), def.pallet_struct.attr_span, false), - }; + let (where_clause, span, has_runtime_upgrade) = match def.hooks.as_ref() { + Some(hooks) => { + let where_clause = hooks.where_clause.clone(); + let span = hooks.attr_span; + let has_runtime_upgrade = hooks.has_runtime_upgrade; + (where_clause, span, has_runtime_upgrade) + } + None => ( + def.config.where_clause.clone(), + def.pallet_struct.attr_span, + false, + ), + }; - let frame_support = &def.frame_support; - let type_impl_gen = &def.type_impl_generics(span); - let type_use_gen = &def.type_use_generics(span); - let pallet_ident = &def.pallet_struct.pallet; - let frame_system = &def.frame_system; - let pallet_name = quote::quote! { - < - ::PalletInfo - as - #frame_support::traits::PalletInfo - >::name::().unwrap_or("") - }; + let frame_support = &def.frame_support; + let type_impl_gen = &def.type_impl_generics(span); + let type_use_gen = &def.type_use_generics(span); + let pallet_ident = &def.pallet_struct.pallet; + let frame_system = &def.frame_system; + let pallet_name = quote::quote! { + < + ::PalletInfo + as + #frame_support::traits::PalletInfo + >::name::().unwrap_or("") + }; - let initialize_on_chain_storage_version = if let Some(in_code_version) = - &def.pallet_struct.storage_version - { - quote::quote! { - #frame_support::__private::log::info!( - target: #frame_support::LOG_TARGET, - "🐥 New pallet {:?} detected in the runtime. Initializing the on-chain storage version to match the storage version defined in the pallet: {:?}", - #pallet_name, - #in_code_version - ); - #in_code_version.put::(); - } - } else { - quote::quote! { - let default_version = #frame_support::traits::StorageVersion::new(0); - #frame_support::__private::log::info!( - target: #frame_support::LOG_TARGET, - "🐥 New pallet {:?} detected in the runtime. The pallet has no defined storage version, so the on-chain version is being initialized to {:?}.", - #pallet_name, - default_version - ); - default_version.put::(); - } - }; + let initialize_on_chain_storage_version = if let Some(in_code_version) = + &def.pallet_struct.storage_version + { + quote::quote! { + #frame_support::__private::log::info!( + target: #frame_support::LOG_TARGET, + "🐥 New pallet {:?} detected in the runtime. Initializing the on-chain storage version to match the storage version defined in the pallet: {:?}", + #pallet_name, + #in_code_version + ); + #in_code_version.put::(); + } + } else { + quote::quote! { + let default_version = #frame_support::traits::StorageVersion::new(0); + #frame_support::__private::log::info!( + target: #frame_support::LOG_TARGET, + "🐥 New pallet {:?} detected in the runtime. The pallet has no defined storage version, so the on-chain version is being initialized to {:?}.", + #pallet_name, + default_version + ); + default_version.put::(); + } + }; - let log_runtime_upgrade = if has_runtime_upgrade { - // a migration is defined here. - quote::quote! { - #frame_support::__private::log::info!( - target: #frame_support::LOG_TARGET, - "⚠️ {} declares internal migrations (which *might* execute). \ - On-chain `{:?}` vs in-code storage version `{:?}`", - #pallet_name, - ::on_chain_storage_version(), - ::in_code_storage_version(), - ); - } - } else { - // default. - quote::quote! { - #frame_support::__private::log::debug!( - target: #frame_support::LOG_TARGET, - "✅ no migration for {}", - #pallet_name, - ); - } - }; + let log_runtime_upgrade = if has_runtime_upgrade { + // a migration is defined here. + quote::quote! { + #frame_support::__private::log::info!( + target: #frame_support::LOG_TARGET, + "⚠️ {} declares internal migrations (which *might* execute). \ + On-chain `{:?}` vs in-code storage version `{:?}`", + #pallet_name, + ::on_chain_storage_version(), + ::in_code_storage_version(), + ); + } + } else { + // default. + quote::quote! { + #frame_support::__private::log::debug!( + target: #frame_support::LOG_TARGET, + "✅ no migration for {}", + #pallet_name, + ); + } + }; - let hooks_impl = if def.hooks.is_none() { - let frame_system = &def.frame_system; - quote::quote! { - impl<#type_impl_gen> - #frame_support::traits::Hooks<#frame_system::pallet_prelude::BlockNumberFor::> - for #pallet_ident<#type_use_gen> #where_clause {} - } - } else { - proc_macro2::TokenStream::new() - }; + let hooks_impl = if def.hooks.is_none() { + let frame_system = &def.frame_system; + quote::quote! { + impl<#type_impl_gen> + #frame_support::traits::Hooks<#frame_system::pallet_prelude::BlockNumberFor::> + for #pallet_ident<#type_use_gen> #where_clause {} + } + } else { + proc_macro2::TokenStream::new() + }; - // If a storage version is set, we should ensure that the storage version on chain matches the - // in-code storage version. This assumes that `Executive` is running custom migrations before - // the pallets are called. - let post_storage_version_check = if def.pallet_struct.storage_version.is_some() { - quote::quote! { - let on_chain_version = ::on_chain_storage_version(); - let in_code_version = ::in_code_storage_version(); + // If a storage version is set, we should ensure that the storage version on chain matches the + // in-code storage version. This assumes that `Executive` is running custom migrations before + // the pallets are called. + let post_storage_version_check = if def.pallet_struct.storage_version.is_some() { + quote::quote! { + let on_chain_version = ::on_chain_storage_version(); + let in_code_version = ::in_code_storage_version(); - if on_chain_version != in_code_version { - #frame_support::__private::log::error!( - target: #frame_support::LOG_TARGET, - "{}: On chain storage version {:?} doesn't match in-code storage version {:?}.", - #pallet_name, - on_chain_version, - in_code_version, - ); + if on_chain_version != in_code_version { + #frame_support::__private::log::error!( + target: #frame_support::LOG_TARGET, + "{}: On chain storage version {:?} doesn't match in-code storage version {:?}.", + #pallet_name, + on_chain_version, + in_code_version, + ); - return Err("On chain and in-code storage version do not match. Missing runtime upgrade?".into()); - } - } - } else { - quote::quote! { - let on_chain_version = ::on_chain_storage_version(); + return Err("On chain and in-code storage version do not match. Missing runtime upgrade?".into()); + } + } + } else { + quote::quote! { + let on_chain_version = ::on_chain_storage_version(); - if on_chain_version != #frame_support::traits::StorageVersion::new(0) { - #frame_support::__private::log::error!( - target: #frame_support::LOG_TARGET, - "{}: On chain storage version {:?} is set to non zero, \ - while the pallet is missing the `#[pallet::storage_version(VERSION)]` attribute.", - #pallet_name, - on_chain_version, - ); + if on_chain_version != #frame_support::traits::StorageVersion::new(0) { + #frame_support::__private::log::error!( + target: #frame_support::LOG_TARGET, + "{}: On chain storage version {:?} is set to non zero, \ + while the pallet is missing the `#[pallet::storage_version(VERSION)]` attribute.", + #pallet_name, + on_chain_version, + ); - return Err("On chain storage version set, while the pallet doesn't \ - have the `#[pallet::storage_version(VERSION)]` attribute.".into()); - } - } - }; + return Err("On chain storage version set, while the pallet doesn't \ + have the `#[pallet::storage_version(VERSION)]` attribute.".into()); + } + } + }; - quote::quote_spanned!(span => - #hooks_impl + quote::quote_spanned!(span => + #hooks_impl - impl<#type_impl_gen> - #frame_support::traits::OnFinalize<#frame_system::pallet_prelude::BlockNumberFor::> - for #pallet_ident<#type_use_gen> #where_clause - { - fn on_finalize(n: #frame_system::pallet_prelude::BlockNumberFor::) { - #frame_support::__private::sp_tracing::enter_span!( - #frame_support::__private::sp_tracing::trace_span!("on_finalize") - ); - < - Self as #frame_support::traits::Hooks< - #frame_system::pallet_prelude::BlockNumberFor:: - > - >::on_finalize(n) - } - } + impl<#type_impl_gen> + #frame_support::traits::OnFinalize<#frame_system::pallet_prelude::BlockNumberFor::> + for #pallet_ident<#type_use_gen> #where_clause + { + fn on_finalize(n: #frame_system::pallet_prelude::BlockNumberFor::) { + #frame_support::__private::sp_tracing::enter_span!( + #frame_support::__private::sp_tracing::trace_span!("on_finalize") + ); + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::on_finalize(n) + } + } - impl<#type_impl_gen> - #frame_support::traits::OnIdle<#frame_system::pallet_prelude::BlockNumberFor::> - for #pallet_ident<#type_use_gen> #where_clause - { - fn on_idle( - n: #frame_system::pallet_prelude::BlockNumberFor::, - remaining_weight: #frame_support::weights::Weight - ) -> #frame_support::weights::Weight { - < - Self as #frame_support::traits::Hooks< - #frame_system::pallet_prelude::BlockNumberFor:: - > - >::on_idle(n, remaining_weight) - } - } + impl<#type_impl_gen> + #frame_support::traits::OnIdle<#frame_system::pallet_prelude::BlockNumberFor::> + for #pallet_ident<#type_use_gen> #where_clause + { + fn on_idle( + n: #frame_system::pallet_prelude::BlockNumberFor::, + remaining_weight: #frame_support::weights::Weight + ) -> #frame_support::weights::Weight { + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::on_idle(n, remaining_weight) + } + } - impl<#type_impl_gen> - #frame_support::traits::OnPoll<#frame_system::pallet_prelude::BlockNumberFor::> - for #pallet_ident<#type_use_gen> #where_clause - { - fn on_poll( - n: #frame_system::pallet_prelude::BlockNumberFor::, - weight: &mut #frame_support::weights::WeightMeter - ) { - < - Self as #frame_support::traits::Hooks< - #frame_system::pallet_prelude::BlockNumberFor:: - > - >::on_poll(n, weight); - } - } + impl<#type_impl_gen> + #frame_support::traits::OnPoll<#frame_system::pallet_prelude::BlockNumberFor::> + for #pallet_ident<#type_use_gen> #where_clause + { + fn on_poll( + n: #frame_system::pallet_prelude::BlockNumberFor::, + weight: &mut #frame_support::weights::WeightMeter + ) { + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::on_poll(n, weight); + } + } - impl<#type_impl_gen> - #frame_support::traits::OnInitialize<#frame_system::pallet_prelude::BlockNumberFor::> - for #pallet_ident<#type_use_gen> #where_clause - { - fn on_initialize( - n: #frame_system::pallet_prelude::BlockNumberFor:: - ) -> #frame_support::weights::Weight { - #frame_support::__private::sp_tracing::enter_span!( - #frame_support::__private::sp_tracing::trace_span!("on_initialize") - ); - < - Self as #frame_support::traits::Hooks< - #frame_system::pallet_prelude::BlockNumberFor:: - > - >::on_initialize(n) - } - } + impl<#type_impl_gen> + #frame_support::traits::OnInitialize<#frame_system::pallet_prelude::BlockNumberFor::> + for #pallet_ident<#type_use_gen> #where_clause + { + fn on_initialize( + n: #frame_system::pallet_prelude::BlockNumberFor:: + ) -> #frame_support::weights::Weight { + #frame_support::__private::sp_tracing::enter_span!( + #frame_support::__private::sp_tracing::trace_span!("on_initialize") + ); + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::on_initialize(n) + } + } - impl<#type_impl_gen> - #frame_support::traits::BeforeAllRuntimeMigrations - for #pallet_ident<#type_use_gen> #where_clause - { - fn before_all_runtime_migrations() -> #frame_support::weights::Weight { - use #frame_support::traits::{Get, PalletInfoAccess}; - use #frame_support::__private::hashing::twox_128; - use #frame_support::storage::unhashed::contains_prefixed_key; - #frame_support::__private::sp_tracing::enter_span!( - #frame_support::__private::sp_tracing::trace_span!("before_all") - ); + impl<#type_impl_gen> + #frame_support::traits::BeforeAllRuntimeMigrations + for #pallet_ident<#type_use_gen> #where_clause + { + fn before_all_runtime_migrations() -> #frame_support::weights::Weight { + use #frame_support::traits::{Get, PalletInfoAccess}; + use #frame_support::__private::hashing::twox_128; + use #frame_support::storage::unhashed::contains_prefixed_key; + #frame_support::__private::sp_tracing::enter_span!( + #frame_support::__private::sp_tracing::trace_span!("before_all") + ); - // Check if the pallet has any keys set, including the storage version. If there are - // no keys set, the pallet was just added to the runtime and needs to have its - // version initialized. - let pallet_hashed_prefix = ::name_hash(); - let exists = contains_prefixed_key(&pallet_hashed_prefix); - if !exists { - #initialize_on_chain_storage_version - ::DbWeight::get().reads_writes(1, 1) - } else { - ::DbWeight::get().reads(1) - } - } - } + // Check if the pallet has any keys set, including the storage version. If there are + // no keys set, the pallet was just added to the runtime and needs to have its + // version initialized. + let pallet_hashed_prefix = ::name_hash(); + let exists = contains_prefixed_key(&pallet_hashed_prefix); + if !exists { + #initialize_on_chain_storage_version + ::DbWeight::get().reads_writes(1, 1) + } else { + ::DbWeight::get().reads(1) + } + } + } - impl<#type_impl_gen> - #frame_support::traits::OnRuntimeUpgrade - for #pallet_ident<#type_use_gen> #where_clause - { - fn on_runtime_upgrade() -> #frame_support::weights::Weight { - #frame_support::__private::sp_tracing::enter_span!( - #frame_support::__private::sp_tracing::trace_span!("on_runtime_update") - ); + impl<#type_impl_gen> + #frame_support::traits::OnRuntimeUpgrade + for #pallet_ident<#type_use_gen> #where_clause + { + fn on_runtime_upgrade() -> #frame_support::weights::Weight { + #frame_support::__private::sp_tracing::enter_span!( + #frame_support::__private::sp_tracing::trace_span!("on_runtime_update") + ); - // log info about the upgrade. - #log_runtime_upgrade + // log info about the upgrade. + #log_runtime_upgrade - < - Self as #frame_support::traits::Hooks< - #frame_system::pallet_prelude::BlockNumberFor:: - > - >::on_runtime_upgrade() - } + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::on_runtime_upgrade() + } - #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result<#frame_support::__private::sp_std::vec::Vec, #frame_support::sp_runtime::TryRuntimeError> { - < - Self - as - #frame_support::traits::Hooks<#frame_system::pallet_prelude::BlockNumberFor::> - >::pre_upgrade() - } + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result<#frame_support::__private::sp_std::vec::Vec, #frame_support::sp_runtime::TryRuntimeError> { + < + Self + as + #frame_support::traits::Hooks<#frame_system::pallet_prelude::BlockNumberFor::> + >::pre_upgrade() + } - #[cfg(feature = "try-runtime")] - fn post_upgrade(state: #frame_support::__private::sp_std::vec::Vec) -> Result<(), #frame_support::sp_runtime::TryRuntimeError> { - #post_storage_version_check + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: #frame_support::__private::sp_std::vec::Vec) -> Result<(), #frame_support::sp_runtime::TryRuntimeError> { + #post_storage_version_check - < - Self - as - #frame_support::traits::Hooks<#frame_system::pallet_prelude::BlockNumberFor::> - >::post_upgrade(state) - } - } + < + Self + as + #frame_support::traits::Hooks<#frame_system::pallet_prelude::BlockNumberFor::> + >::post_upgrade(state) + } + } - impl<#type_impl_gen> - #frame_support::traits::OffchainWorker<#frame_system::pallet_prelude::BlockNumberFor::> - for #pallet_ident<#type_use_gen> #where_clause - { - fn offchain_worker(n: #frame_system::pallet_prelude::BlockNumberFor::) { - < - Self as #frame_support::traits::Hooks< - #frame_system::pallet_prelude::BlockNumberFor:: - > - >::offchain_worker(n) - } - } + impl<#type_impl_gen> + #frame_support::traits::OffchainWorker<#frame_system::pallet_prelude::BlockNumberFor::> + for #pallet_ident<#type_use_gen> #where_clause + { + fn offchain_worker(n: #frame_system::pallet_prelude::BlockNumberFor::) { + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::offchain_worker(n) + } + } - // Integrity tests are only required for when `std` is enabled. - #frame_support::std_enabled! { - impl<#type_impl_gen> - #frame_support::traits::IntegrityTest - for #pallet_ident<#type_use_gen> #where_clause - { - fn integrity_test() { - #frame_support::__private::sp_io::TestExternalities::default().execute_with(|| { - < - Self as #frame_support::traits::Hooks< - #frame_system::pallet_prelude::BlockNumberFor:: - > - >::integrity_test() - }); - } - } - } + // Integrity tests are only required for when `std` is enabled. + #frame_support::std_enabled! { + impl<#type_impl_gen> + #frame_support::traits::IntegrityTest + for #pallet_ident<#type_use_gen> #where_clause + { + fn integrity_test() { + #frame_support::__private::sp_io::TestExternalities::default().execute_with(|| { + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::integrity_test() + }); + } + } + } - #[cfg(feature = "try-runtime")] - impl<#type_impl_gen> - #frame_support::traits::TryState<#frame_system::pallet_prelude::BlockNumberFor::> - for #pallet_ident<#type_use_gen> #where_clause - { - fn try_state( - n: #frame_system::pallet_prelude::BlockNumberFor::, - _s: #frame_support::traits::TryStateSelect - ) -> Result<(), #frame_support::sp_runtime::TryRuntimeError> { - #frame_support::__private::log::info!( - target: #frame_support::LOG_TARGET, - "🩺 Running {:?} try-state checks", - #pallet_name, - ); - < - Self as #frame_support::traits::Hooks< - #frame_system::pallet_prelude::BlockNumberFor:: - > - >::try_state(n).map_err(|err| { - #frame_support::__private::log::error!( - target: #frame_support::LOG_TARGET, - "❌ {:?} try_state checks failed: {:?}", - #pallet_name, - err - ); + #[cfg(feature = "try-runtime")] + impl<#type_impl_gen> + #frame_support::traits::TryState<#frame_system::pallet_prelude::BlockNumberFor::> + for #pallet_ident<#type_use_gen> #where_clause + { + fn try_state( + n: #frame_system::pallet_prelude::BlockNumberFor::, + _s: #frame_support::traits::TryStateSelect + ) -> Result<(), #frame_support::sp_runtime::TryRuntimeError> { + #frame_support::__private::log::info!( + target: #frame_support::LOG_TARGET, + "🩺 Running {:?} try-state checks", + #pallet_name, + ); + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::try_state(n).map_err(|err| { + #frame_support::__private::log::error!( + target: #frame_support::LOG_TARGET, + "❌ {:?} try_state checks failed: {:?}", + #pallet_name, + err + ); - err - }) - } - } - ) + err + }) + } + } + ) } diff --git a/support/procedural-fork/src/pallet/expand/inherent.rs b/support/procedural-fork/src/pallet/expand/inherent.rs index 182d79f5b..0a80d672a 100644 --- a/support/procedural-fork/src/pallet/expand/inherent.rs +++ b/support/procedural-fork/src/pallet/expand/inherent.rs @@ -21,35 +21,38 @@ use quote::quote; use syn::{spanned::Spanned, Ident}; pub fn expand_inherents(def: &mut Def) -> TokenStream { - let count = COUNTER.with(|counter| counter.borrow_mut().inc()); - let macro_ident = Ident::new(&format!("__is_inherent_part_defined_{}", count), def.item.span()); + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + let macro_ident = Ident::new( + &format!("__is_inherent_part_defined_{}", count), + def.item.span(), + ); - let maybe_compile_error = if def.inherent.is_none() { - quote! { - compile_error!(concat!( - "`", - stringify!($pallet_name), - "` does not have #[pallet::inherent] defined, perhaps you should \ - remove `Inherent` from construct_runtime?", - )); - } - } else { - TokenStream::new() - }; + let maybe_compile_error = if def.inherent.is_none() { + quote! { + compile_error!(concat!( + "`", + stringify!($pallet_name), + "` does not have #[pallet::inherent] defined, perhaps you should \ + remove `Inherent` from construct_runtime?", + )); + } + } else { + TokenStream::new() + }; - quote! { - #[doc(hidden)] - pub mod __substrate_inherent_check { - #[macro_export] - #[doc(hidden)] - macro_rules! #macro_ident { - ($pallet_name:ident) => { - #maybe_compile_error - } - } + quote! { + #[doc(hidden)] + pub mod __substrate_inherent_check { + #[macro_export] + #[doc(hidden)] + macro_rules! #macro_ident { + ($pallet_name:ident) => { + #maybe_compile_error + } + } - #[doc(hidden)] - pub use #macro_ident as is_inherent_part_defined; - } - } + #[doc(hidden)] + pub use #macro_ident as is_inherent_part_defined; + } + } } diff --git a/support/procedural-fork/src/pallet/expand/instances.rs b/support/procedural-fork/src/pallet/expand/instances.rs index b6dfa7e6d..12423409c 100644 --- a/support/procedural-fork/src/pallet/expand/instances.rs +++ b/support/procedural-fork/src/pallet/expand/instances.rs @@ -22,22 +22,22 @@ use proc_macro2::Span; /// * Provide inherent instance to be used by construct_runtime /// * Provide Instance1 ..= Instance16 for instantiable pallet pub fn expand_instances(def: &mut Def) -> proc_macro2::TokenStream { - let frame_support = &def.frame_support; - let inherent_ident = syn::Ident::new(crate::INHERENT_INSTANCE_NAME, Span::call_site()); - let instances = if def.config.has_instance { - (1..=NUMBER_OF_INSTANCE) - .map(|i| syn::Ident::new(&format!("Instance{}", i), Span::call_site())) - .collect() - } else { - vec![] - }; + let frame_support = &def.frame_support; + let inherent_ident = syn::Ident::new(crate::INHERENT_INSTANCE_NAME, Span::call_site()); + let instances = if def.config.has_instance { + (1..=NUMBER_OF_INSTANCE) + .map(|i| syn::Ident::new(&format!("Instance{}", i), Span::call_site())) + .collect() + } else { + vec![] + }; - quote::quote!( - /// Hidden instance generated to be internally used when module is used without - /// instance. - #[doc(hidden)] - pub type #inherent_ident = (); + quote::quote!( + /// Hidden instance generated to be internally used when module is used without + /// instance. + #[doc(hidden)] + pub type #inherent_ident = (); - #( pub use #frame_support::instances::#instances; )* - ) + #( pub use #frame_support::instances::#instances; )* + ) } diff --git a/support/procedural-fork/src/pallet/expand/mod.rs b/support/procedural-fork/src/pallet/expand/mod.rs index 067839c28..ff4423f85 100644 --- a/support/procedural-fork/src/pallet/expand/mod.rs +++ b/support/procedural-fork/src/pallet/expand/mod.rs @@ -42,12 +42,12 @@ use quote::ToTokens; /// Merge where clause together, `where` token span is taken from the first not none one. pub fn merge_where_clauses(clauses: &[&Option]) -> Option { - let mut clauses = clauses.iter().filter_map(|f| f.as_ref()); - let mut res = clauses.next()?.clone(); - for other in clauses { - res.predicates.extend(other.predicates.iter().cloned()) - } - Some(res) + let mut clauses = clauses.iter().filter_map(|f| f.as_ref()); + let mut res = clauses.next()?.clone(); + for other in clauses { + res.predicates.extend(other.predicates.iter().cloned()) + } + Some(res) } /// Expand definition, in particular: @@ -55,32 +55,32 @@ pub fn merge_where_clauses(clauses: &[&Option]) -> Option proc_macro2::TokenStream { - // Remove the `pallet_doc` attribute first. - let metadata_docs = documentation::expand_documentation(&mut def); - let constants = constants::expand_constants(&mut def); - let pallet_struct = pallet_struct::expand_pallet_struct(&mut def); - let config = config::expand_config(&mut def); - let call = call::expand_call(&mut def); - let tasks = tasks::expand_tasks(&mut def); - let error = error::expand_error(&mut def); - let event = event::expand_event(&mut def); - let storages = storage::expand_storages(&mut def); - let inherents = inherent::expand_inherents(&mut def); - let instances = instances::expand_instances(&mut def); - let hooks = hooks::expand_hooks(&mut def); - let genesis_build = genesis_build::expand_genesis_build(&mut def); - let genesis_config = genesis_config::expand_genesis_config(&mut def); - let type_values = type_value::expand_type_values(&mut def); - let origins = origin::expand_origins(&mut def); - let validate_unsigned = validate_unsigned::expand_validate_unsigned(&mut def); - let tt_default_parts = tt_default_parts::expand_tt_default_parts(&mut def); - let doc_only = doc_only::expand_doc_only(&mut def); - let composites = composite::expand_composites(&mut def); + // Remove the `pallet_doc` attribute first. + let metadata_docs = documentation::expand_documentation(&mut def); + let constants = constants::expand_constants(&mut def); + let pallet_struct = pallet_struct::expand_pallet_struct(&mut def); + let config = config::expand_config(&mut def); + let call = call::expand_call(&mut def); + let tasks = tasks::expand_tasks(&mut def); + let error = error::expand_error(&mut def); + let event = event::expand_event(&mut def); + let storages = storage::expand_storages(&mut def); + let inherents = inherent::expand_inherents(&mut def); + let instances = instances::expand_instances(&mut def); + let hooks = hooks::expand_hooks(&mut def); + let genesis_build = genesis_build::expand_genesis_build(&mut def); + let genesis_config = genesis_config::expand_genesis_config(&mut def); + let type_values = type_value::expand_type_values(&mut def); + let origins = origin::expand_origins(&mut def); + let validate_unsigned = validate_unsigned::expand_validate_unsigned(&mut def); + let tt_default_parts = tt_default_parts::expand_tt_default_parts(&mut def); + let doc_only = doc_only::expand_doc_only(&mut def); + let composites = composite::expand_composites(&mut def); - def.item.attrs.insert( - 0, - syn::parse_quote!( - #[doc = r"The `pallet` module in each FRAME pallet hosts the most important items needed + def.item.attrs.insert( + 0, + syn::parse_quote!( + #[doc = r"The `pallet` module in each FRAME pallet hosts the most important items needed to construct this pallet. The main components of this pallet are: @@ -93,38 +93,38 @@ storage item. Otherwise, all storage items are listed among [*Type Definitions*] - [`Config`], which contains the configuration trait of this pallet. - [`Event`] and [`Error`], which are listed among the [*Enums*](#enums). "] - ), - ); + ), + ); - let new_items = quote::quote!( - #metadata_docs - #constants - #pallet_struct - #config - #call - #tasks - #error - #event - #storages - #inherents - #instances - #hooks - #genesis_build - #genesis_config - #type_values - #origins - #validate_unsigned - #tt_default_parts - #doc_only - #composites - ); + let new_items = quote::quote!( + #metadata_docs + #constants + #pallet_struct + #config + #call + #tasks + #error + #event + #storages + #inherents + #instances + #hooks + #genesis_build + #genesis_config + #type_values + #origins + #validate_unsigned + #tt_default_parts + #doc_only + #composites + ); - def.item - .content - .as_mut() - .expect("This is checked by parsing") - .1 - .push(syn::Item::Verbatim(new_items)); + def.item + .content + .as_mut() + .expect("This is checked by parsing") + .1 + .push(syn::Item::Verbatim(new_items)); - def.item.into_token_stream() + def.item.into_token_stream() } diff --git a/support/procedural-fork/src/pallet/expand/origin.rs b/support/procedural-fork/src/pallet/expand/origin.rs index 55865b424..167445ad6 100644 --- a/support/procedural-fork/src/pallet/expand/origin.rs +++ b/support/procedural-fork/src/pallet/expand/origin.rs @@ -21,35 +21,38 @@ use quote::quote; use syn::{spanned::Spanned, Ident}; pub fn expand_origins(def: &mut Def) -> TokenStream { - let count = COUNTER.with(|counter| counter.borrow_mut().inc()); - let macro_ident = Ident::new(&format!("__is_origin_part_defined_{}", count), def.item.span()); + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + let macro_ident = Ident::new( + &format!("__is_origin_part_defined_{}", count), + def.item.span(), + ); - let maybe_compile_error = if def.origin.is_none() { - quote! { - compile_error!(concat!( - "`", - stringify!($pallet_name), - "` does not have #[pallet::origin] defined, perhaps you should \ - remove `Origin` from construct_runtime?", - )); - } - } else { - TokenStream::new() - }; + let maybe_compile_error = if def.origin.is_none() { + quote! { + compile_error!(concat!( + "`", + stringify!($pallet_name), + "` does not have #[pallet::origin] defined, perhaps you should \ + remove `Origin` from construct_runtime?", + )); + } + } else { + TokenStream::new() + }; - quote! { - #[doc(hidden)] - pub mod __substrate_origin_check { - #[macro_export] - #[doc(hidden)] - macro_rules! #macro_ident { - ($pallet_name:ident) => { - #maybe_compile_error - } - } + quote! { + #[doc(hidden)] + pub mod __substrate_origin_check { + #[macro_export] + #[doc(hidden)] + macro_rules! #macro_ident { + ($pallet_name:ident) => { + #maybe_compile_error + } + } - #[doc(hidden)] - pub use #macro_ident as is_origin_part_defined; - } - } + #[doc(hidden)] + pub use #macro_ident as is_origin_part_defined; + } + } } diff --git a/support/procedural-fork/src/pallet/expand/pallet_struct.rs b/support/procedural-fork/src/pallet/expand/pallet_struct.rs index 7cdf6bde9..c5def65ed 100644 --- a/support/procedural-fork/src/pallet/expand/pallet_struct.rs +++ b/support/procedural-fork/src/pallet/expand/pallet_struct.rs @@ -28,263 +28,275 @@ use frame_support_procedural_tools::get_doc_literals; /// * implementation of `PalletInfoAccess` information /// * implementation of `StorageInfoTrait` on Pallet pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream { - let frame_support = &def.frame_support; - let frame_system = &def.frame_system; - let type_impl_gen = &def.type_impl_generics(def.pallet_struct.attr_span); - let type_use_gen = &def.type_use_generics(def.pallet_struct.attr_span); - let type_decl_gen = &def.type_decl_generics(def.pallet_struct.attr_span); - let pallet_ident = &def.pallet_struct.pallet; - let config_where_clause = &def.config.where_clause; - - let mut storages_where_clauses = vec![&def.config.where_clause]; - storages_where_clauses.extend(def.storages.iter().map(|storage| &storage.where_clause)); - let storages_where_clauses = merge_where_clauses(&storages_where_clauses); - - let pallet_item = { - let pallet_module_items = &mut def.item.content.as_mut().expect("Checked by def").1; - let item = &mut pallet_module_items[def.pallet_struct.index]; - if let syn::Item::Struct(item) = item { - item - } else { - unreachable!("Checked by pallet struct parser") - } - }; - - // If the first field type is `_` then we replace with `PhantomData` - if let Some(field) = pallet_item.fields.iter_mut().next() { - if field.ty == syn::parse_quote!(_) { - field.ty = syn::parse_quote!( - #frame_support::__private::sp_std::marker::PhantomData<(#type_use_gen)> - ); - } - } - - if get_doc_literals(&pallet_item.attrs).is_empty() { - pallet_item.attrs.push(syn::parse_quote!( - #[doc = r" + let frame_support = &def.frame_support; + let frame_system = &def.frame_system; + let type_impl_gen = &def.type_impl_generics(def.pallet_struct.attr_span); + let type_use_gen = &def.type_use_generics(def.pallet_struct.attr_span); + let type_decl_gen = &def.type_decl_generics(def.pallet_struct.attr_span); + let pallet_ident = &def.pallet_struct.pallet; + let config_where_clause = &def.config.where_clause; + + let mut storages_where_clauses = vec![&def.config.where_clause]; + storages_where_clauses.extend(def.storages.iter().map(|storage| &storage.where_clause)); + let storages_where_clauses = merge_where_clauses(&storages_where_clauses); + + let pallet_item = { + let pallet_module_items = &mut def.item.content.as_mut().expect("Checked by def").1; + let item = &mut pallet_module_items[def.pallet_struct.index]; + if let syn::Item::Struct(item) = item { + item + } else { + unreachable!("Checked by pallet struct parser") + } + }; + + // If the first field type is `_` then we replace with `PhantomData` + if let Some(field) = pallet_item.fields.iter_mut().next() { + if field.ty == syn::parse_quote!(_) { + field.ty = syn::parse_quote!( + #frame_support::__private::sp_std::marker::PhantomData<(#type_use_gen)> + ); + } + } + + if get_doc_literals(&pallet_item.attrs).is_empty() { + pallet_item.attrs.push(syn::parse_quote!( + #[doc = r" The `Pallet` struct, the main type that implements traits and standalone functions within the pallet. "] - )); - } - - pallet_item.attrs.push(syn::parse_quote!( - #[derive( - #frame_support::CloneNoBound, - #frame_support::EqNoBound, - #frame_support::PartialEqNoBound, - #frame_support::RuntimeDebugNoBound, - )] - )); - - let pallet_error_metadata = if let Some(error_def) = &def.error { - let error_ident = &error_def.error; - quote::quote_spanned!(def.pallet_struct.attr_span => - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #config_where_clause { - #[doc(hidden)] - pub fn error_metadata() -> Option<#frame_support::__private::metadata_ir::PalletErrorMetadataIR> { - Some(#frame_support::__private::metadata_ir::PalletErrorMetadataIR { - ty: #frame_support::__private::scale_info::meta_type::<#error_ident<#type_use_gen>>() - }) - } - } - ) - } else { - quote::quote_spanned!(def.pallet_struct.attr_span => - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #config_where_clause { - #[doc(hidden)] - pub fn error_metadata() -> Option<#frame_support::__private::metadata_ir::PalletErrorMetadataIR> { - None - } - } - ) - }; - - let storage_info_span = - def.pallet_struct.without_storage_info.unwrap_or(def.pallet_struct.attr_span); - - let storage_names = &def.storages.iter().map(|storage| &storage.ident).collect::>(); - let storage_cfg_attrs = - &def.storages.iter().map(|storage| &storage.cfg_attrs).collect::>(); - - // Depending on the flag `without_storage_info` and the storage attribute `unbounded`, we use - // partial or full storage info from storage. - let storage_info_traits = &def - .storages - .iter() - .map(|storage| { - if storage.unbounded || def.pallet_struct.without_storage_info.is_some() { - quote::quote_spanned!(storage_info_span => PartialStorageInfoTrait) - } else { - quote::quote_spanned!(storage_info_span => StorageInfoTrait) - } - }) - .collect::>(); - - let storage_info_methods = &def - .storages - .iter() - .map(|storage| { - if storage.unbounded || def.pallet_struct.without_storage_info.is_some() { - quote::quote_spanned!(storage_info_span => partial_storage_info) - } else { - quote::quote_spanned!(storage_info_span => storage_info) - } - }) - .collect::>(); - - let storage_info = quote::quote_spanned!(storage_info_span => - impl<#type_impl_gen> #frame_support::traits::StorageInfoTrait - for #pallet_ident<#type_use_gen> - #storages_where_clauses - { - fn storage_info() - -> #frame_support::__private::sp_std::vec::Vec<#frame_support::traits::StorageInfo> - { - #[allow(unused_mut)] - let mut res = #frame_support::__private::sp_std::vec![]; - - #( - #(#storage_cfg_attrs)* - { - let mut storage_info = < - #storage_names<#type_use_gen> - as #frame_support::traits::#storage_info_traits - >::#storage_info_methods(); - res.append(&mut storage_info); - } - )* - - res - } - } - ); - - let (storage_version, in_code_storage_version_ty) = - if let Some(v) = def.pallet_struct.storage_version.as_ref() { - (quote::quote! { #v }, quote::quote! { #frame_support::traits::StorageVersion }) - } else { - ( - quote::quote! { core::default::Default::default() }, - quote::quote! { #frame_support::traits::NoStorageVersionSet }, - ) - }; - - let whitelisted_storage_idents: Vec = def - .storages - .iter() - .filter_map(|s| s.whitelisted.then_some(s.ident.clone())) - .collect(); - - let whitelisted_storage_keys_impl = quote::quote![ - use #frame_support::traits::{StorageInfoTrait, TrackedStorageKey, WhitelistedStorageKeys}; - impl<#type_impl_gen> WhitelistedStorageKeys for #pallet_ident<#type_use_gen> #storages_where_clauses { - fn whitelisted_storage_keys() -> #frame_support::__private::sp_std::vec::Vec { - use #frame_support::__private::sp_std::vec; - vec![#( - TrackedStorageKey::new(#whitelisted_storage_idents::<#type_use_gen>::hashed_key().to_vec()) - ),*] - } - } - ]; - - quote::quote_spanned!(def.pallet_struct.attr_span => - #pallet_error_metadata - - /// Type alias to `Pallet`, to be used by `construct_runtime`. - /// - /// Generated by `pallet` attribute macro. - #[deprecated(note = "use `Pallet` instead")] - #[allow(dead_code)] - pub type Module<#type_decl_gen> = #pallet_ident<#type_use_gen>; - - // Implement `GetStorageVersion` for `Pallet` - impl<#type_impl_gen> #frame_support::traits::GetStorageVersion - for #pallet_ident<#type_use_gen> - #config_where_clause - { - type InCodeStorageVersion = #in_code_storage_version_ty; - - fn in_code_storage_version() -> Self::InCodeStorageVersion { - #storage_version - } - - fn on_chain_storage_version() -> #frame_support::traits::StorageVersion { - #frame_support::traits::StorageVersion::get::() - } - } - - // Implement `OnGenesis` for `Pallet` - impl<#type_impl_gen> #frame_support::traits::OnGenesis - for #pallet_ident<#type_use_gen> - #config_where_clause - { - fn on_genesis() { - let storage_version: #frame_support::traits::StorageVersion = #storage_version; - storage_version.put::(); - } - } - - // Implement `PalletInfoAccess` for `Pallet` - impl<#type_impl_gen> #frame_support::traits::PalletInfoAccess - for #pallet_ident<#type_use_gen> - #config_where_clause - { - fn index() -> usize { - < - ::PalletInfo as #frame_support::traits::PalletInfo - >::index::() - .expect("Pallet is part of the runtime because pallet `Config` trait is \ - implemented by the runtime") - } - - fn name() -> &'static str { - < - ::PalletInfo as #frame_support::traits::PalletInfo - >::name::() - .expect("Pallet is part of the runtime because pallet `Config` trait is \ - implemented by the runtime") - } - - fn name_hash() -> [u8; 16] { - < - ::PalletInfo as #frame_support::traits::PalletInfo - >::name_hash::() - .expect("Pallet is part of the runtime because pallet `Config` trait is \ - implemented by the runtime") - } - - fn module_name() -> &'static str { - < - ::PalletInfo as #frame_support::traits::PalletInfo - >::module_name::() - .expect("Pallet is part of the runtime because pallet `Config` trait is \ - implemented by the runtime") - } - - fn crate_version() -> #frame_support::traits::CrateVersion { - #frame_support::crate_to_crate_version!() - } - } - - impl<#type_impl_gen> #frame_support::traits::PalletsInfoAccess - for #pallet_ident<#type_use_gen> - #config_where_clause - { - fn count() -> usize { 1 } - fn infos() -> #frame_support::__private::sp_std::vec::Vec<#frame_support::traits::PalletInfoData> { - use #frame_support::traits::PalletInfoAccess; - let item = #frame_support::traits::PalletInfoData { - index: Self::index(), - name: Self::name(), - module_name: Self::module_name(), - crate_version: Self::crate_version(), - }; - #frame_support::__private::sp_std::vec![item] - } - } - - #storage_info - #whitelisted_storage_keys_impl - ) + )); + } + + pallet_item.attrs.push(syn::parse_quote!( + #[derive( + #frame_support::CloneNoBound, + #frame_support::EqNoBound, + #frame_support::PartialEqNoBound, + #frame_support::RuntimeDebugNoBound, + )] + )); + + let pallet_error_metadata = if let Some(error_def) = &def.error { + let error_ident = &error_def.error; + quote::quote_spanned!(def.pallet_struct.attr_span => + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #config_where_clause { + #[doc(hidden)] + pub fn error_metadata() -> Option<#frame_support::__private::metadata_ir::PalletErrorMetadataIR> { + Some(#frame_support::__private::metadata_ir::PalletErrorMetadataIR { + ty: #frame_support::__private::scale_info::meta_type::<#error_ident<#type_use_gen>>() + }) + } + } + ) + } else { + quote::quote_spanned!(def.pallet_struct.attr_span => + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #config_where_clause { + #[doc(hidden)] + pub fn error_metadata() -> Option<#frame_support::__private::metadata_ir::PalletErrorMetadataIR> { + None + } + } + ) + }; + + let storage_info_span = def + .pallet_struct + .without_storage_info + .unwrap_or(def.pallet_struct.attr_span); + + let storage_names = &def + .storages + .iter() + .map(|storage| &storage.ident) + .collect::>(); + let storage_cfg_attrs = &def + .storages + .iter() + .map(|storage| &storage.cfg_attrs) + .collect::>(); + + // Depending on the flag `without_storage_info` and the storage attribute `unbounded`, we use + // partial or full storage info from storage. + let storage_info_traits = &def + .storages + .iter() + .map(|storage| { + if storage.unbounded || def.pallet_struct.without_storage_info.is_some() { + quote::quote_spanned!(storage_info_span => PartialStorageInfoTrait) + } else { + quote::quote_spanned!(storage_info_span => StorageInfoTrait) + } + }) + .collect::>(); + + let storage_info_methods = &def + .storages + .iter() + .map(|storage| { + if storage.unbounded || def.pallet_struct.without_storage_info.is_some() { + quote::quote_spanned!(storage_info_span => partial_storage_info) + } else { + quote::quote_spanned!(storage_info_span => storage_info) + } + }) + .collect::>(); + + let storage_info = quote::quote_spanned!(storage_info_span => + impl<#type_impl_gen> #frame_support::traits::StorageInfoTrait + for #pallet_ident<#type_use_gen> + #storages_where_clauses + { + fn storage_info() + -> #frame_support::__private::sp_std::vec::Vec<#frame_support::traits::StorageInfo> + { + #[allow(unused_mut)] + let mut res = #frame_support::__private::sp_std::vec![]; + + #( + #(#storage_cfg_attrs)* + { + let mut storage_info = < + #storage_names<#type_use_gen> + as #frame_support::traits::#storage_info_traits + >::#storage_info_methods(); + res.append(&mut storage_info); + } + )* + + res + } + } + ); + + let (storage_version, in_code_storage_version_ty) = + if let Some(v) = def.pallet_struct.storage_version.as_ref() { + ( + quote::quote! { #v }, + quote::quote! { #frame_support::traits::StorageVersion }, + ) + } else { + ( + quote::quote! { core::default::Default::default() }, + quote::quote! { #frame_support::traits::NoStorageVersionSet }, + ) + }; + + let whitelisted_storage_idents: Vec = def + .storages + .iter() + .filter_map(|s| s.whitelisted.then_some(s.ident.clone())) + .collect(); + + let whitelisted_storage_keys_impl = quote::quote![ + use #frame_support::traits::{StorageInfoTrait, TrackedStorageKey, WhitelistedStorageKeys}; + impl<#type_impl_gen> WhitelistedStorageKeys for #pallet_ident<#type_use_gen> #storages_where_clauses { + fn whitelisted_storage_keys() -> #frame_support::__private::sp_std::vec::Vec { + use #frame_support::__private::sp_std::vec; + vec![#( + TrackedStorageKey::new(#whitelisted_storage_idents::<#type_use_gen>::hashed_key().to_vec()) + ),*] + } + } + ]; + + quote::quote_spanned!(def.pallet_struct.attr_span => + #pallet_error_metadata + + /// Type alias to `Pallet`, to be used by `construct_runtime`. + /// + /// Generated by `pallet` attribute macro. + #[deprecated(note = "use `Pallet` instead")] + #[allow(dead_code)] + pub type Module<#type_decl_gen> = #pallet_ident<#type_use_gen>; + + // Implement `GetStorageVersion` for `Pallet` + impl<#type_impl_gen> #frame_support::traits::GetStorageVersion + for #pallet_ident<#type_use_gen> + #config_where_clause + { + type InCodeStorageVersion = #in_code_storage_version_ty; + + fn in_code_storage_version() -> Self::InCodeStorageVersion { + #storage_version + } + + fn on_chain_storage_version() -> #frame_support::traits::StorageVersion { + #frame_support::traits::StorageVersion::get::() + } + } + + // Implement `OnGenesis` for `Pallet` + impl<#type_impl_gen> #frame_support::traits::OnGenesis + for #pallet_ident<#type_use_gen> + #config_where_clause + { + fn on_genesis() { + let storage_version: #frame_support::traits::StorageVersion = #storage_version; + storage_version.put::(); + } + } + + // Implement `PalletInfoAccess` for `Pallet` + impl<#type_impl_gen> #frame_support::traits::PalletInfoAccess + for #pallet_ident<#type_use_gen> + #config_where_clause + { + fn index() -> usize { + < + ::PalletInfo as #frame_support::traits::PalletInfo + >::index::() + .expect("Pallet is part of the runtime because pallet `Config` trait is \ + implemented by the runtime") + } + + fn name() -> &'static str { + < + ::PalletInfo as #frame_support::traits::PalletInfo + >::name::() + .expect("Pallet is part of the runtime because pallet `Config` trait is \ + implemented by the runtime") + } + + fn name_hash() -> [u8; 16] { + < + ::PalletInfo as #frame_support::traits::PalletInfo + >::name_hash::() + .expect("Pallet is part of the runtime because pallet `Config` trait is \ + implemented by the runtime") + } + + fn module_name() -> &'static str { + < + ::PalletInfo as #frame_support::traits::PalletInfo + >::module_name::() + .expect("Pallet is part of the runtime because pallet `Config` trait is \ + implemented by the runtime") + } + + fn crate_version() -> #frame_support::traits::CrateVersion { + #frame_support::crate_to_crate_version!() + } + } + + impl<#type_impl_gen> #frame_support::traits::PalletsInfoAccess + for #pallet_ident<#type_use_gen> + #config_where_clause + { + fn count() -> usize { 1 } + fn infos() -> #frame_support::__private::sp_std::vec::Vec<#frame_support::traits::PalletInfoData> { + use #frame_support::traits::PalletInfoAccess; + let item = #frame_support::traits::PalletInfoData { + index: Self::index(), + name: Self::name(), + module_name: Self::module_name(), + crate_version: Self::crate_version(), + }; + #frame_support::__private::sp_std::vec![item] + } + } + + #storage_info + #whitelisted_storage_keys_impl + ) } diff --git a/support/procedural-fork/src/pallet/expand/storage.rs b/support/procedural-fork/src/pallet/expand/storage.rs index 937b068cf..b77e9846b 100644 --- a/support/procedural-fork/src/pallet/expand/storage.rs +++ b/support/procedural-fork/src/pallet/expand/storage.rs @@ -16,14 +16,14 @@ // limitations under the License. use crate::{ - counter_prefix, - pallet::{ - parse::{ - helper::two128_str, - storage::{Metadata, QueryKind, StorageDef, StorageGenerics}, - }, - Def, - }, + counter_prefix, + pallet::{ + parse::{ + helper::two128_str, + storage::{Metadata, QueryKind, StorageDef, StorageGenerics}, + }, + Def, + }, }; use quote::ToTokens; use std::{collections::HashMap, ops::IndexMut}; @@ -32,73 +32,76 @@ use syn::spanned::Spanned; /// Generate the prefix_ident related to the storage. /// prefix_ident is used for the prefix struct to be given to storage as first generic param. fn prefix_ident(storage: &StorageDef) -> syn::Ident { - let storage_ident = &storage.ident; - syn::Ident::new(&format!("_GeneratedPrefixForStorage{}", storage_ident), storage_ident.span()) + let storage_ident = &storage.ident; + syn::Ident::new( + &format!("_GeneratedPrefixForStorage{}", storage_ident), + storage_ident.span(), + ) } /// Generate the counter_prefix_ident related to the storage. /// counter_prefix_ident is used for the prefix struct to be given to counted storage map. fn counter_prefix_ident(storage_ident: &syn::Ident) -> syn::Ident { - syn::Ident::new( - &format!("_GeneratedCounterPrefixForStorage{}", storage_ident), - storage_ident.span(), - ) + syn::Ident::new( + &format!("_GeneratedCounterPrefixForStorage{}", storage_ident), + storage_ident.span(), + ) } /// Check for duplicated storage prefixes. This step is necessary since users can specify an /// alternative storage prefix using the #[pallet::storage_prefix] syntax, and we need to ensure /// that the prefix specified by the user is not a duplicate of an existing one. fn check_prefix_duplicates( - storage_def: &StorageDef, - // A hashmap of all already used prefix and their associated error if duplication - used_prefixes: &mut HashMap, + storage_def: &StorageDef, + // A hashmap of all already used prefix and their associated error if duplication + used_prefixes: &mut HashMap, ) -> syn::Result<()> { - let prefix = storage_def.prefix(); - let dup_err = syn::Error::new( - storage_def.prefix_span(), - format!("Duplicate storage prefixes found for `{}`", prefix), - ); - - if let Some(other_dup_err) = used_prefixes.insert(prefix.clone(), dup_err.clone()) { - let mut err = dup_err; - err.combine(other_dup_err); - return Err(err) - } - - if let Metadata::CountedMap { .. } = storage_def.metadata { - let counter_prefix = counter_prefix(&prefix); - let counter_dup_err = syn::Error::new( - storage_def.prefix_span(), - format!( - "Duplicate storage prefixes found for `{}`, used for counter associated to \ + let prefix = storage_def.prefix(); + let dup_err = syn::Error::new( + storage_def.prefix_span(), + format!("Duplicate storage prefixes found for `{}`", prefix), + ); + + if let Some(other_dup_err) = used_prefixes.insert(prefix.clone(), dup_err.clone()) { + let mut err = dup_err; + err.combine(other_dup_err); + return Err(err); + } + + if let Metadata::CountedMap { .. } = storage_def.metadata { + let counter_prefix = counter_prefix(&prefix); + let counter_dup_err = syn::Error::new( + storage_def.prefix_span(), + format!( + "Duplicate storage prefixes found for `{}`, used for counter associated to \ counted storage map", - counter_prefix, - ), - ); - - if let Some(other_dup_err) = used_prefixes.insert(counter_prefix, counter_dup_err.clone()) { - let mut err = counter_dup_err; - err.combine(other_dup_err); - return Err(err) - } - } - - Ok(()) + counter_prefix, + ), + ); + + if let Some(other_dup_err) = used_prefixes.insert(counter_prefix, counter_dup_err.clone()) { + let mut err = counter_dup_err; + err.combine(other_dup_err); + return Err(err); + } + } + + Ok(()) } pub struct ResultOnEmptyStructMetadata { - /// The Rust ident that is going to be used as the name of the OnEmpty struct. - pub name: syn::Ident, - /// The path to the error type being returned by the ResultQuery. - pub error_path: syn::Path, - /// The visibility of the OnEmpty struct. - pub visibility: syn::Visibility, - /// The type of the storage item. - pub value_ty: syn::Type, - /// The name of the pallet error enum variant that is going to be returned. - pub variant_name: syn::Ident, - /// The span used to report compilation errors about the OnEmpty struct. - pub span: proc_macro2::Span, + /// The Rust ident that is going to be used as the name of the OnEmpty struct. + pub name: syn::Ident, + /// The path to the error type being returned by the ResultQuery. + pub error_path: syn::Path, + /// The visibility of the OnEmpty struct. + pub visibility: syn::Visibility, + /// The type of the storage item. + pub value_ty: syn::Type, + /// The name of the pallet error enum variant that is going to be returned. + pub variant_name: syn::Ident, + /// The span used to report compilation errors about the OnEmpty struct. + pub span: proc_macro2::Span, } /// @@ -106,277 +109,305 @@ pub struct ResultOnEmptyStructMetadata { /// * if generics are named: reorder the generic, remove their name, and add the missing ones. /// * Add `#[allow(type_alias_bounds)]` pub fn process_generics(def: &mut Def) -> syn::Result> { - let frame_support = &def.frame_support; - let mut on_empty_struct_metadata = Vec::new(); - - for storage_def in def.storages.iter_mut() { - let item = &mut def.item.content.as_mut().expect("Checked by def").1[storage_def.index]; - - let typ_item = match item { - syn::Item::Type(t) => t, - _ => unreachable!("Checked by def"), - }; - - typ_item.attrs.push(syn::parse_quote!(#[allow(type_alias_bounds)])); - - let typ_path = match &mut *typ_item.ty { - syn::Type::Path(p) => p, - _ => unreachable!("Checked by def"), - }; - - let args = match &mut typ_path.path.segments[0].arguments { - syn::PathArguments::AngleBracketed(args) => args, - _ => unreachable!("Checked by def"), - }; - - let prefix_ident = prefix_ident(storage_def); - let type_use_gen = if def.config.has_instance { - quote::quote_spanned!(storage_def.attr_span => T, I) - } else { - quote::quote_spanned!(storage_def.attr_span => T) - }; - - let default_query_kind: syn::Type = - syn::parse_quote!(#frame_support::storage::types::OptionQuery); - let mut default_on_empty = |value_ty: syn::Type| -> syn::Type { - if let Some(QueryKind::ResultQuery(error_path, variant_name)) = - storage_def.query_kind.as_ref() - { - let on_empty_ident = - quote::format_ident!("__Frame_Internal_Get{}Result", storage_def.ident); - on_empty_struct_metadata.push(ResultOnEmptyStructMetadata { - name: on_empty_ident.clone(), - visibility: storage_def.vis.clone(), - value_ty, - error_path: error_path.clone(), - variant_name: variant_name.clone(), - span: storage_def.attr_span, - }); - return syn::parse_quote!(#on_empty_ident) - } - syn::parse_quote!(#frame_support::traits::GetDefault) - }; - let default_max_values: syn::Type = syn::parse_quote!(#frame_support::traits::GetDefault); - - let set_result_query_type_parameter = |query_type: &mut syn::Type| -> syn::Result<()> { - if let Some(QueryKind::ResultQuery(error_path, _)) = storage_def.query_kind.as_ref() { - if let syn::Type::Path(syn::TypePath { path: syn::Path { segments, .. }, .. }) = - query_type - { - if let Some(seg) = segments.last_mut() { - if let syn::PathArguments::AngleBracketed( - syn::AngleBracketedGenericArguments { args, .. }, - ) = &mut seg.arguments - { - args.clear(); - args.push(syn::GenericArgument::Type(syn::parse_quote!(#error_path))); - } - } - } else { - let msg = format!( - "Invalid pallet::storage, unexpected type for query, expected ResultQuery \ + let frame_support = &def.frame_support; + let mut on_empty_struct_metadata = Vec::new(); + + for storage_def in def.storages.iter_mut() { + let item = &mut def.item.content.as_mut().expect("Checked by def").1[storage_def.index]; + + let typ_item = match item { + syn::Item::Type(t) => t, + _ => unreachable!("Checked by def"), + }; + + typ_item + .attrs + .push(syn::parse_quote!(#[allow(type_alias_bounds)])); + + let typ_path = match &mut *typ_item.ty { + syn::Type::Path(p) => p, + _ => unreachable!("Checked by def"), + }; + + let args = match &mut typ_path.path.segments[0].arguments { + syn::PathArguments::AngleBracketed(args) => args, + _ => unreachable!("Checked by def"), + }; + + let prefix_ident = prefix_ident(storage_def); + let type_use_gen = if def.config.has_instance { + quote::quote_spanned!(storage_def.attr_span => T, I) + } else { + quote::quote_spanned!(storage_def.attr_span => T) + }; + + let default_query_kind: syn::Type = + syn::parse_quote!(#frame_support::storage::types::OptionQuery); + let mut default_on_empty = |value_ty: syn::Type| -> syn::Type { + if let Some(QueryKind::ResultQuery(error_path, variant_name)) = + storage_def.query_kind.as_ref() + { + let on_empty_ident = + quote::format_ident!("__Frame_Internal_Get{}Result", storage_def.ident); + on_empty_struct_metadata.push(ResultOnEmptyStructMetadata { + name: on_empty_ident.clone(), + visibility: storage_def.vis.clone(), + value_ty, + error_path: error_path.clone(), + variant_name: variant_name.clone(), + span: storage_def.attr_span, + }); + return syn::parse_quote!(#on_empty_ident); + } + syn::parse_quote!(#frame_support::traits::GetDefault) + }; + let default_max_values: syn::Type = syn::parse_quote!(#frame_support::traits::GetDefault); + + let set_result_query_type_parameter = |query_type: &mut syn::Type| -> syn::Result<()> { + if let Some(QueryKind::ResultQuery(error_path, _)) = storage_def.query_kind.as_ref() { + if let syn::Type::Path(syn::TypePath { + path: syn::Path { segments, .. }, + .. + }) = query_type + { + if let Some(seg) = segments.last_mut() { + if let syn::PathArguments::AngleBracketed( + syn::AngleBracketedGenericArguments { args, .. }, + ) = &mut seg.arguments + { + args.clear(); + args.push(syn::GenericArgument::Type(syn::parse_quote!(#error_path))); + } + } + } else { + let msg = format!( + "Invalid pallet::storage, unexpected type for query, expected ResultQuery \ with 1 type parameter, found `{}`", - query_type.to_token_stream().to_string() - ); - return Err(syn::Error::new(query_type.span(), msg)) - } - } - Ok(()) - }; - - if let Some(named_generics) = storage_def.named_generics.clone() { - args.args.clear(); - args.args.push(syn::parse_quote!( #prefix_ident<#type_use_gen> )); - match named_generics { - StorageGenerics::Value { value, query_kind, on_empty } => { - args.args.push(syn::GenericArgument::Type(value.clone())); - let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone()); - set_result_query_type_parameter(&mut query_kind)?; - args.args.push(syn::GenericArgument::Type(query_kind)); - let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value)); - args.args.push(syn::GenericArgument::Type(on_empty)); - }, - StorageGenerics::Map { hasher, key, value, query_kind, on_empty, max_values } | - StorageGenerics::CountedMap { - hasher, - key, - value, - query_kind, - on_empty, - max_values, - } => { - args.args.push(syn::GenericArgument::Type(hasher)); - args.args.push(syn::GenericArgument::Type(key)); - args.args.push(syn::GenericArgument::Type(value.clone())); - let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone()); - set_result_query_type_parameter(&mut query_kind)?; - args.args.push(syn::GenericArgument::Type(query_kind)); - let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value)); - args.args.push(syn::GenericArgument::Type(on_empty)); - let max_values = max_values.unwrap_or_else(|| default_max_values.clone()); - args.args.push(syn::GenericArgument::Type(max_values)); - }, - StorageGenerics::DoubleMap { - hasher1, - key1, - hasher2, - key2, - value, - query_kind, - on_empty, - max_values, - } => { - args.args.push(syn::GenericArgument::Type(hasher1)); - args.args.push(syn::GenericArgument::Type(key1)); - args.args.push(syn::GenericArgument::Type(hasher2)); - args.args.push(syn::GenericArgument::Type(key2)); - args.args.push(syn::GenericArgument::Type(value.clone())); - let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone()); - set_result_query_type_parameter(&mut query_kind)?; - args.args.push(syn::GenericArgument::Type(query_kind)); - let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value)); - args.args.push(syn::GenericArgument::Type(on_empty)); - let max_values = max_values.unwrap_or_else(|| default_max_values.clone()); - args.args.push(syn::GenericArgument::Type(max_values)); - }, - StorageGenerics::NMap { keygen, value, query_kind, on_empty, max_values } | - StorageGenerics::CountedNMap { - keygen, - value, - query_kind, - on_empty, - max_values, - } => { - args.args.push(syn::GenericArgument::Type(keygen)); - args.args.push(syn::GenericArgument::Type(value.clone())); - let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone()); - set_result_query_type_parameter(&mut query_kind)?; - args.args.push(syn::GenericArgument::Type(query_kind)); - let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value)); - args.args.push(syn::GenericArgument::Type(on_empty)); - let max_values = max_values.unwrap_or_else(|| default_max_values.clone()); - args.args.push(syn::GenericArgument::Type(max_values)); - }, - } - } else { - args.args[0] = syn::parse_quote!( #prefix_ident<#type_use_gen> ); - - let (value_idx, query_idx, on_empty_idx) = match storage_def.metadata { - Metadata::Value { .. } => (1, 2, 3), - Metadata::NMap { .. } | Metadata::CountedNMap { .. } => (2, 3, 4), - Metadata::Map { .. } | Metadata::CountedMap { .. } => (3, 4, 5), - Metadata::DoubleMap { .. } => (5, 6, 7), - }; - - if storage_def.use_default_hasher { - let hasher_indices: Vec = match storage_def.metadata { - Metadata::Map { .. } | Metadata::CountedMap { .. } => vec![1], - Metadata::DoubleMap { .. } => vec![1, 3], - _ => vec![], - }; - for hasher_idx in hasher_indices { - args.args[hasher_idx] = syn::GenericArgument::Type( - syn::parse_quote!(#frame_support::Blake2_128Concat), - ); - } - } - - if query_idx < args.args.len() { - if let syn::GenericArgument::Type(query_kind) = args.args.index_mut(query_idx) { - set_result_query_type_parameter(query_kind)?; - } - } else if let Some(QueryKind::ResultQuery(error_path, _)) = - storage_def.query_kind.as_ref() - { - args.args.push(syn::GenericArgument::Type(syn::parse_quote!(#error_path))) - } - - // Here, we only need to check if OnEmpty is *not* specified, and if so, then we have to - // generate a default OnEmpty struct for it. - if on_empty_idx >= args.args.len() && - matches!(storage_def.query_kind.as_ref(), Some(QueryKind::ResultQuery(_, _))) - { - let value_ty = match args.args[value_idx].clone() { - syn::GenericArgument::Type(ty) => ty, - _ => unreachable!(), - }; - let on_empty = default_on_empty(value_ty); - args.args.push(syn::GenericArgument::Type(on_empty)); - } - } - } - - Ok(on_empty_struct_metadata) + query_type.to_token_stream().to_string() + ); + return Err(syn::Error::new(query_type.span(), msg)); + } + } + Ok(()) + }; + + if let Some(named_generics) = storage_def.named_generics.clone() { + args.args.clear(); + args.args + .push(syn::parse_quote!( #prefix_ident<#type_use_gen> )); + match named_generics { + StorageGenerics::Value { + value, + query_kind, + on_empty, + } => { + args.args.push(syn::GenericArgument::Type(value.clone())); + let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone()); + set_result_query_type_parameter(&mut query_kind)?; + args.args.push(syn::GenericArgument::Type(query_kind)); + let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value)); + args.args.push(syn::GenericArgument::Type(on_empty)); + } + StorageGenerics::Map { + hasher, + key, + value, + query_kind, + on_empty, + max_values, + } + | StorageGenerics::CountedMap { + hasher, + key, + value, + query_kind, + on_empty, + max_values, + } => { + args.args.push(syn::GenericArgument::Type(hasher)); + args.args.push(syn::GenericArgument::Type(key)); + args.args.push(syn::GenericArgument::Type(value.clone())); + let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone()); + set_result_query_type_parameter(&mut query_kind)?; + args.args.push(syn::GenericArgument::Type(query_kind)); + let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value)); + args.args.push(syn::GenericArgument::Type(on_empty)); + let max_values = max_values.unwrap_or_else(|| default_max_values.clone()); + args.args.push(syn::GenericArgument::Type(max_values)); + } + StorageGenerics::DoubleMap { + hasher1, + key1, + hasher2, + key2, + value, + query_kind, + on_empty, + max_values, + } => { + args.args.push(syn::GenericArgument::Type(hasher1)); + args.args.push(syn::GenericArgument::Type(key1)); + args.args.push(syn::GenericArgument::Type(hasher2)); + args.args.push(syn::GenericArgument::Type(key2)); + args.args.push(syn::GenericArgument::Type(value.clone())); + let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone()); + set_result_query_type_parameter(&mut query_kind)?; + args.args.push(syn::GenericArgument::Type(query_kind)); + let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value)); + args.args.push(syn::GenericArgument::Type(on_empty)); + let max_values = max_values.unwrap_or_else(|| default_max_values.clone()); + args.args.push(syn::GenericArgument::Type(max_values)); + } + StorageGenerics::NMap { + keygen, + value, + query_kind, + on_empty, + max_values, + } + | StorageGenerics::CountedNMap { + keygen, + value, + query_kind, + on_empty, + max_values, + } => { + args.args.push(syn::GenericArgument::Type(keygen)); + args.args.push(syn::GenericArgument::Type(value.clone())); + let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone()); + set_result_query_type_parameter(&mut query_kind)?; + args.args.push(syn::GenericArgument::Type(query_kind)); + let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value)); + args.args.push(syn::GenericArgument::Type(on_empty)); + let max_values = max_values.unwrap_or_else(|| default_max_values.clone()); + args.args.push(syn::GenericArgument::Type(max_values)); + } + } + } else { + args.args[0] = syn::parse_quote!( #prefix_ident<#type_use_gen> ); + + let (value_idx, query_idx, on_empty_idx) = match storage_def.metadata { + Metadata::Value { .. } => (1, 2, 3), + Metadata::NMap { .. } | Metadata::CountedNMap { .. } => (2, 3, 4), + Metadata::Map { .. } | Metadata::CountedMap { .. } => (3, 4, 5), + Metadata::DoubleMap { .. } => (5, 6, 7), + }; + + if storage_def.use_default_hasher { + let hasher_indices: Vec = match storage_def.metadata { + Metadata::Map { .. } | Metadata::CountedMap { .. } => vec![1], + Metadata::DoubleMap { .. } => vec![1, 3], + _ => vec![], + }; + for hasher_idx in hasher_indices { + args.args[hasher_idx] = syn::GenericArgument::Type( + syn::parse_quote!(#frame_support::Blake2_128Concat), + ); + } + } + + if query_idx < args.args.len() { + if let syn::GenericArgument::Type(query_kind) = args.args.index_mut(query_idx) { + set_result_query_type_parameter(query_kind)?; + } + } else if let Some(QueryKind::ResultQuery(error_path, _)) = + storage_def.query_kind.as_ref() + { + args.args + .push(syn::GenericArgument::Type(syn::parse_quote!(#error_path))) + } + + // Here, we only need to check if OnEmpty is *not* specified, and if so, then we have to + // generate a default OnEmpty struct for it. + if on_empty_idx >= args.args.len() + && matches!( + storage_def.query_kind.as_ref(), + Some(QueryKind::ResultQuery(_, _)) + ) + { + let value_ty = match args.args[value_idx].clone() { + syn::GenericArgument::Type(ty) => ty, + _ => unreachable!(), + }; + let on_empty = default_on_empty(value_ty); + args.args.push(syn::GenericArgument::Type(on_empty)); + } + } + } + + Ok(on_empty_struct_metadata) } fn augment_final_docs(def: &mut Def) { - // expand the docs with a new line showing the storage type (value, map, double map, etc), and - // the key/value type(s). - let mut push_string_literal = |doc_line: &str, storage: &mut StorageDef| { - let item = &mut def.item.content.as_mut().expect("Checked by def").1[storage.index]; - let typ_item = match item { - syn::Item::Type(t) => t, - _ => unreachable!("Checked by def"), - }; - typ_item.attrs.push(syn::parse_quote!(#[doc = ""])); - typ_item.attrs.push(syn::parse_quote!(#[doc = #doc_line])); - }; - def.storages.iter_mut().for_each(|storage| match &storage.metadata { - Metadata::Value { value } => { - let doc_line = format!( - "Storage type is [`StorageValue`] with value type `{}`.", - value.to_token_stream() - ); - push_string_literal(&doc_line, storage); - }, - Metadata::Map { key, value } => { - let doc_line = format!( - "Storage type is [`StorageMap`] with key type `{}` and value type `{}`.", - key.to_token_stream(), - value.to_token_stream() - ); - push_string_literal(&doc_line, storage); - }, - Metadata::DoubleMap { key1, key2, value } => { - let doc_line = format!( + // expand the docs with a new line showing the storage type (value, map, double map, etc), and + // the key/value type(s). + let mut push_string_literal = |doc_line: &str, storage: &mut StorageDef| { + let item = &mut def.item.content.as_mut().expect("Checked by def").1[storage.index]; + let typ_item = match item { + syn::Item::Type(t) => t, + _ => unreachable!("Checked by def"), + }; + typ_item.attrs.push(syn::parse_quote!(#[doc = ""])); + typ_item.attrs.push(syn::parse_quote!(#[doc = #doc_line])); + }; + def.storages + .iter_mut() + .for_each(|storage| match &storage.metadata { + Metadata::Value { value } => { + let doc_line = format!( + "Storage type is [`StorageValue`] with value type `{}`.", + value.to_token_stream() + ); + push_string_literal(&doc_line, storage); + } + Metadata::Map { key, value } => { + let doc_line = format!( + "Storage type is [`StorageMap`] with key type `{}` and value type `{}`.", + key.to_token_stream(), + value.to_token_stream() + ); + push_string_literal(&doc_line, storage); + } + Metadata::DoubleMap { key1, key2, value } => { + let doc_line = format!( "Storage type is [`StorageDoubleMap`] with key1 type {}, key2 type {} and value type {}.", key1.to_token_stream(), key2.to_token_stream(), value.to_token_stream() ); - push_string_literal(&doc_line, storage); - }, - Metadata::NMap { keys, value, .. } => { - let doc_line = format!( - "Storage type is [`StorageNMap`] with keys type ({}) and value type {}.", - keys.iter() - .map(|k| k.to_token_stream().to_string()) - .collect::>() - .join(", "), - value.to_token_stream() - ); - push_string_literal(&doc_line, storage); - }, - Metadata::CountedNMap { keys, value, .. } => { - let doc_line = format!( - "Storage type is [`CountedStorageNMap`] with keys type ({}) and value type {}.", - keys.iter() - .map(|k| k.to_token_stream().to_string()) - .collect::>() - .join(", "), - value.to_token_stream() - ); - push_string_literal(&doc_line, storage); - }, - Metadata::CountedMap { key, value } => { - let doc_line = format!( - "Storage type is [`CountedStorageMap`] with key type {} and value type {}.", - key.to_token_stream(), - value.to_token_stream() - ); - push_string_literal(&doc_line, storage); - }, - }); + push_string_literal(&doc_line, storage); + } + Metadata::NMap { keys, value, .. } => { + let doc_line = format!( + "Storage type is [`StorageNMap`] with keys type ({}) and value type {}.", + keys.iter() + .map(|k| k.to_token_stream().to_string()) + .collect::>() + .join(", "), + value.to_token_stream() + ); + push_string_literal(&doc_line, storage); + } + Metadata::CountedNMap { keys, value, .. } => { + let doc_line = format!( + "Storage type is [`CountedStorageNMap`] with keys type ({}) and value type {}.", + keys.iter() + .map(|k| k.to_token_stream().to_string()) + .collect::>() + .join(", "), + value.to_token_stream() + ); + push_string_literal(&doc_line, storage); + } + Metadata::CountedMap { key, value } => { + let doc_line = format!( + "Storage type is [`CountedStorageMap`] with key type {} and value type {}.", + key.to_token_stream(), + value.to_token_stream() + ); + push_string_literal(&doc_line, storage); + } + }); } /// @@ -387,29 +418,29 @@ fn augment_final_docs(def: &mut Def) { /// * Add `#[allow(type_alias_bounds)]` on storages type alias /// * generate metadatas pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { - let on_empty_struct_metadata = match process_generics(def) { - Ok(idents) => idents, - Err(e) => return e.into_compile_error(), - }; - - augment_final_docs(def); - - // Check for duplicate prefixes - let mut prefix_set = HashMap::new(); - let mut errors = def - .storages - .iter() - .filter_map(|storage_def| check_prefix_duplicates(storage_def, &mut prefix_set).err()); - if let Some(mut final_error) = errors.next() { - errors.for_each(|error| final_error.combine(error)); - return final_error.into_compile_error() - } - - let frame_support = &def.frame_support; - let frame_system = &def.frame_system; - let pallet_ident = &def.pallet_struct.pallet; - - let entries_builder = def.storages.iter().map(|storage| { + let on_empty_struct_metadata = match process_generics(def) { + Ok(idents) => idents, + Err(e) => return e.into_compile_error(), + }; + + augment_final_docs(def); + + // Check for duplicate prefixes + let mut prefix_set = HashMap::new(); + let mut errors = def + .storages + .iter() + .filter_map(|storage_def| check_prefix_duplicates(storage_def, &mut prefix_set).err()); + if let Some(mut final_error) = errors.next() { + errors.for_each(|error| final_error.combine(error)); + return final_error.into_compile_error(); + } + + let frame_support = &def.frame_support; + let frame_system = &def.frame_system; + let pallet_ident = &def.pallet_struct.pallet; + + let entries_builder = def.storages.iter().map(|storage| { let no_docs = vec![]; let docs = if cfg!(feature = "no-metadata-docs") { &no_docs } else { &storage.docs }; @@ -432,202 +463,202 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { ) }); - let getters = def.storages.iter().map(|storage| { - if let Some(getter) = &storage.getter { - let completed_where_clause = - super::merge_where_clauses(&[&storage.where_clause, &def.config.where_clause]); - - let ident = &storage.ident; - let gen = &def.type_use_generics(storage.attr_span); - let type_impl_gen = &def.type_impl_generics(storage.attr_span); - let type_use_gen = &def.type_use_generics(storage.attr_span); - let full_ident = quote::quote_spanned!(storage.attr_span => #ident<#gen> ); - - let cfg_attrs = &storage.cfg_attrs; - - // If the storage item is public, link it and otherwise just mention it. - // - // We can not just copy the docs from a non-public type as it may links to internal - // types which makes the compiler very unhappy :( - let getter_doc_line = if matches!(storage.vis, syn::Visibility::Public(_)) { - format!("An auto-generated getter for [`{}`].", storage.ident) - } else { - format!("An auto-generated getter for `{}`.", storage.ident) - }; - - match &storage.metadata { - Metadata::Value { value } => { - let query = match storage.query_kind.as_ref().expect("Checked by def") { - QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => - Option<#value> - ), - QueryKind::ResultQuery(error_path, _) => { - quote::quote_spanned!(storage.attr_span => - Result<#value, #error_path> - ) - }, - QueryKind::ValueQuery => quote::quote!(#value), - }; - quote::quote_spanned!(storage.attr_span => - #(#cfg_attrs)* - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { - #[doc = #getter_doc_line] - pub fn #getter() -> #query { - < - #full_ident as #frame_support::storage::StorageValue<#value> - >::get() - } - } - ) - }, - Metadata::Map { key, value } => { - let query = match storage.query_kind.as_ref().expect("Checked by def") { - QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => - Option<#value> - ), - QueryKind::ResultQuery(error_path, _) => { - quote::quote_spanned!(storage.attr_span => - Result<#value, #error_path> - ) - }, - QueryKind::ValueQuery => quote::quote!(#value), - }; - quote::quote_spanned!(storage.attr_span => - #(#cfg_attrs)* - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { - #[doc = #getter_doc_line] - pub fn #getter(k: KArg) -> #query where - KArg: #frame_support::__private::codec::EncodeLike<#key>, - { - < - #full_ident as #frame_support::storage::StorageMap<#key, #value> - >::get(k) - } - } - ) - }, - Metadata::CountedMap { key, value } => { - let query = match storage.query_kind.as_ref().expect("Checked by def") { - QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => - Option<#value> - ), - QueryKind::ResultQuery(error_path, _) => { - quote::quote_spanned!(storage.attr_span => - Result<#value, #error_path> - ) - }, - QueryKind::ValueQuery => quote::quote!(#value), - }; - quote::quote_spanned!(storage.attr_span => - #(#cfg_attrs)* - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { - #[doc = #getter_doc_line] - pub fn #getter(k: KArg) -> #query where - KArg: #frame_support::__private::codec::EncodeLike<#key>, - { - // NOTE: we can't use any trait here because CountedStorageMap - // doesn't implement any. - <#full_ident>::get(k) - } - } - ) - }, - Metadata::DoubleMap { key1, key2, value } => { - let query = match storage.query_kind.as_ref().expect("Checked by def") { - QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => - Option<#value> - ), - QueryKind::ResultQuery(error_path, _) => { - quote::quote_spanned!(storage.attr_span => - Result<#value, #error_path> - ) - }, - QueryKind::ValueQuery => quote::quote!(#value), - }; - quote::quote_spanned!(storage.attr_span => - #(#cfg_attrs)* - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { - #[doc = #getter_doc_line] - pub fn #getter(k1: KArg1, k2: KArg2) -> #query where - KArg1: #frame_support::__private::codec::EncodeLike<#key1>, - KArg2: #frame_support::__private::codec::EncodeLike<#key2>, - { - < - #full_ident as - #frame_support::storage::StorageDoubleMap<#key1, #key2, #value> - >::get(k1, k2) - } - } - ) - }, - Metadata::NMap { keygen, value, .. } => { - let query = match storage.query_kind.as_ref().expect("Checked by def") { - QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => - Option<#value> - ), - QueryKind::ResultQuery(error_path, _) => { - quote::quote_spanned!(storage.attr_span => - Result<#value, #error_path> - ) - }, - QueryKind::ValueQuery => quote::quote!(#value), - }; - quote::quote_spanned!(storage.attr_span => - #(#cfg_attrs)* - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { - #[doc = #getter_doc_line] - pub fn #getter(key: KArg) -> #query - where - KArg: #frame_support::storage::types::EncodeLikeTuple< - <#keygen as #frame_support::storage::types::KeyGenerator>::KArg - > - + #frame_support::storage::types::TupleToEncodedIter, - { - < - #full_ident as - #frame_support::storage::StorageNMap<#keygen, #value> - >::get(key) - } - } - ) - }, - Metadata::CountedNMap { keygen, value, .. } => { - let query = match storage.query_kind.as_ref().expect("Checked by def") { - QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => - Option<#value> - ), - QueryKind::ResultQuery(error_path, _) => { - quote::quote_spanned!(storage.attr_span => - Result<#value, #error_path> - ) - }, - QueryKind::ValueQuery => quote::quote!(#value), - }; - quote::quote_spanned!(storage.attr_span => - #(#cfg_attrs)* - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { - #[doc = #getter_doc_line] - pub fn #getter(key: KArg) -> #query - where - KArg: #frame_support::storage::types::EncodeLikeTuple< - <#keygen as #frame_support::storage::types::KeyGenerator>::KArg - > - + #frame_support::storage::types::TupleToEncodedIter, - { - // NOTE: we can't use any trait here because CountedStorageNMap - // doesn't implement any. - <#full_ident>::get(key) - } - } - ) - }, - } - } else { - Default::default() - } - }); - - let prefix_structs = def.storages.iter().map(|storage_def| { + let getters = def.storages.iter().map(|storage| { + if let Some(getter) = &storage.getter { + let completed_where_clause = + super::merge_where_clauses(&[&storage.where_clause, &def.config.where_clause]); + + let ident = &storage.ident; + let gen = &def.type_use_generics(storage.attr_span); + let type_impl_gen = &def.type_impl_generics(storage.attr_span); + let type_use_gen = &def.type_use_generics(storage.attr_span); + let full_ident = quote::quote_spanned!(storage.attr_span => #ident<#gen> ); + + let cfg_attrs = &storage.cfg_attrs; + + // If the storage item is public, link it and otherwise just mention it. + // + // We can not just copy the docs from a non-public type as it may links to internal + // types which makes the compiler very unhappy :( + let getter_doc_line = if matches!(storage.vis, syn::Visibility::Public(_)) { + format!("An auto-generated getter for [`{}`].", storage.ident) + } else { + format!("An auto-generated getter for `{}`.", storage.ident) + }; + + match &storage.metadata { + Metadata::Value { value } => { + let query = match storage.query_kind.as_ref().expect("Checked by def") { + QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => + Option<#value> + ), + QueryKind::ResultQuery(error_path, _) => { + quote::quote_spanned!(storage.attr_span => + Result<#value, #error_path> + ) + } + QueryKind::ValueQuery => quote::quote!(#value), + }; + quote::quote_spanned!(storage.attr_span => + #(#cfg_attrs)* + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { + #[doc = #getter_doc_line] + pub fn #getter() -> #query { + < + #full_ident as #frame_support::storage::StorageValue<#value> + >::get() + } + } + ) + } + Metadata::Map { key, value } => { + let query = match storage.query_kind.as_ref().expect("Checked by def") { + QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => + Option<#value> + ), + QueryKind::ResultQuery(error_path, _) => { + quote::quote_spanned!(storage.attr_span => + Result<#value, #error_path> + ) + } + QueryKind::ValueQuery => quote::quote!(#value), + }; + quote::quote_spanned!(storage.attr_span => + #(#cfg_attrs)* + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { + #[doc = #getter_doc_line] + pub fn #getter(k: KArg) -> #query where + KArg: #frame_support::__private::codec::EncodeLike<#key>, + { + < + #full_ident as #frame_support::storage::StorageMap<#key, #value> + >::get(k) + } + } + ) + } + Metadata::CountedMap { key, value } => { + let query = match storage.query_kind.as_ref().expect("Checked by def") { + QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => + Option<#value> + ), + QueryKind::ResultQuery(error_path, _) => { + quote::quote_spanned!(storage.attr_span => + Result<#value, #error_path> + ) + } + QueryKind::ValueQuery => quote::quote!(#value), + }; + quote::quote_spanned!(storage.attr_span => + #(#cfg_attrs)* + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { + #[doc = #getter_doc_line] + pub fn #getter(k: KArg) -> #query where + KArg: #frame_support::__private::codec::EncodeLike<#key>, + { + // NOTE: we can't use any trait here because CountedStorageMap + // doesn't implement any. + <#full_ident>::get(k) + } + } + ) + } + Metadata::DoubleMap { key1, key2, value } => { + let query = match storage.query_kind.as_ref().expect("Checked by def") { + QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => + Option<#value> + ), + QueryKind::ResultQuery(error_path, _) => { + quote::quote_spanned!(storage.attr_span => + Result<#value, #error_path> + ) + } + QueryKind::ValueQuery => quote::quote!(#value), + }; + quote::quote_spanned!(storage.attr_span => + #(#cfg_attrs)* + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { + #[doc = #getter_doc_line] + pub fn #getter(k1: KArg1, k2: KArg2) -> #query where + KArg1: #frame_support::__private::codec::EncodeLike<#key1>, + KArg2: #frame_support::__private::codec::EncodeLike<#key2>, + { + < + #full_ident as + #frame_support::storage::StorageDoubleMap<#key1, #key2, #value> + >::get(k1, k2) + } + } + ) + } + Metadata::NMap { keygen, value, .. } => { + let query = match storage.query_kind.as_ref().expect("Checked by def") { + QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => + Option<#value> + ), + QueryKind::ResultQuery(error_path, _) => { + quote::quote_spanned!(storage.attr_span => + Result<#value, #error_path> + ) + } + QueryKind::ValueQuery => quote::quote!(#value), + }; + quote::quote_spanned!(storage.attr_span => + #(#cfg_attrs)* + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { + #[doc = #getter_doc_line] + pub fn #getter(key: KArg) -> #query + where + KArg: #frame_support::storage::types::EncodeLikeTuple< + <#keygen as #frame_support::storage::types::KeyGenerator>::KArg + > + + #frame_support::storage::types::TupleToEncodedIter, + { + < + #full_ident as + #frame_support::storage::StorageNMap<#keygen, #value> + >::get(key) + } + } + ) + } + Metadata::CountedNMap { keygen, value, .. } => { + let query = match storage.query_kind.as_ref().expect("Checked by def") { + QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => + Option<#value> + ), + QueryKind::ResultQuery(error_path, _) => { + quote::quote_spanned!(storage.attr_span => + Result<#value, #error_path> + ) + } + QueryKind::ValueQuery => quote::quote!(#value), + }; + quote::quote_spanned!(storage.attr_span => + #(#cfg_attrs)* + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { + #[doc = #getter_doc_line] + pub fn #getter(key: KArg) -> #query + where + KArg: #frame_support::storage::types::EncodeLikeTuple< + <#keygen as #frame_support::storage::types::KeyGenerator>::KArg + > + + #frame_support::storage::types::TupleToEncodedIter, + { + // NOTE: we can't use any trait here because CountedStorageNMap + // doesn't implement any. + <#full_ident>::get(key) + } + } + ) + } + } + } else { + Default::default() + } + }); + + let prefix_structs = def.storages.iter().map(|storage_def| { let type_impl_gen = &def.type_impl_generics(storage_def.attr_span); let type_use_gen = &def.type_use_generics(storage_def.attr_span); let prefix_struct_ident = prefix_ident(storage_def); @@ -767,153 +798,159 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { ) }); - let on_empty_structs = on_empty_struct_metadata.into_iter().map(|metadata| { - use crate::pallet::parse::GenericKind; - use syn::{GenericArgument, Path, PathArguments, PathSegment, Type, TypePath}; - - let ResultOnEmptyStructMetadata { - name, - visibility, - value_ty, - error_path, - variant_name, - span, - } = metadata; - - let generic_kind = match error_path.segments.last() { - Some(PathSegment { arguments: PathArguments::AngleBracketed(args), .. }) => { - let (has_config, has_instance) = - args.args.iter().fold((false, false), |(has_config, has_instance), arg| { - match arg { - GenericArgument::Type(Type::Path(TypePath { - path: Path { segments, .. }, - .. - })) => { - let maybe_config = - segments.first().map_or(false, |seg| seg.ident == "T"); - let maybe_instance = - segments.first().map_or(false, |seg| seg.ident == "I"); - - (has_config || maybe_config, has_instance || maybe_instance) - }, - _ => (has_config, has_instance), - } - }); - GenericKind::from_gens(has_config, has_instance).unwrap_or(GenericKind::None) - }, - _ => GenericKind::None, - }; - let type_impl_gen = generic_kind.type_impl_gen(proc_macro2::Span::call_site()); - let config_where_clause = &def.config.where_clause; - - quote::quote_spanned!(span => - #[doc(hidden)] - #[allow(non_camel_case_types)] - #visibility struct #name; - - impl<#type_impl_gen> #frame_support::traits::Get> - for #name - #config_where_clause - { - fn get() -> Result<#value_ty, #error_path> { - Err(<#error_path>::#variant_name) - } - } - ) - }); - - // aggregated where clause of all storage types and the whole pallet. - let mut where_clauses = vec![&def.config.where_clause]; - where_clauses.extend(def.storages.iter().map(|storage| &storage.where_clause)); - let completed_where_clause = super::merge_where_clauses(&where_clauses); - let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site()); - let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site()); - - let try_decode_entire_state = { - let mut storage_names = def - .storages - .iter() - .filter_map(|storage| { - // A little hacky; don't generate for cfg gated storages to not get compile errors - // when building "frame-feature-testing" gated storages in the "frame-support-test" - // crate. - if storage.try_decode && storage.cfg_attrs.is_empty() { - let ident = &storage.ident; - let gen = &def.type_use_generics(storage.attr_span); - Some(quote::quote_spanned!(storage.attr_span => #ident<#gen> )) - } else { - None - } - }) - .collect::>(); - storage_names.sort_by_cached_key(|ident| ident.to_string()); - - quote::quote!( - #[cfg(feature = "try-runtime")] - impl<#type_impl_gen> #frame_support::traits::TryDecodeEntireStorage - for #pallet_ident<#type_use_gen> #completed_where_clause - { - fn try_decode_entire_state() -> Result> { - let pallet_name = <::PalletInfo as frame_support::traits::PalletInfo> - ::name::<#pallet_ident<#type_use_gen>>() - .expect("Every active pallet has a name in the runtime; qed"); - - #frame_support::__private::log::debug!(target: "runtime::try-decode-state", "trying to decode pallet: {pallet_name}"); - - // NOTE: for now, we have to exclude storage items that are feature gated. - let mut errors = #frame_support::__private::sp_std::vec::Vec::new(); - let mut decoded = 0usize; - - #( - #frame_support::__private::log::debug!(target: "runtime::try-decode-state", "trying to decode storage: \ - {pallet_name}::{}", stringify!(#storage_names)); - - match <#storage_names as #frame_support::traits::TryDecodeEntireStorage>::try_decode_entire_state() { - Ok(count) => { - decoded += count; - }, - Err(err) => { - errors.extend(err); - }, - } - )* - - if errors.is_empty() { - Ok(decoded) - } else { - Err(errors) - } - } - } - ) - }; - - quote::quote!( - impl<#type_impl_gen> #pallet_ident<#type_use_gen> - #completed_where_clause - { - #[doc(hidden)] - pub fn storage_metadata() -> #frame_support::__private::metadata_ir::PalletStorageMetadataIR { - #frame_support::__private::metadata_ir::PalletStorageMetadataIR { - prefix: < - ::PalletInfo as - #frame_support::traits::PalletInfo - >::name::<#pallet_ident<#type_use_gen>>() - .expect("No name found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`."), - entries: { - #[allow(unused_mut)] - let mut entries = #frame_support::__private::sp_std::vec![]; - #( #entries_builder )* - entries - }, - } - } - } - - #( #getters )* - #( #prefix_structs )* - #( #on_empty_structs )* - - #try_decode_entire_state - ) + let on_empty_structs = on_empty_struct_metadata.into_iter().map(|metadata| { + use crate::pallet::parse::GenericKind; + use syn::{GenericArgument, Path, PathArguments, PathSegment, Type, TypePath}; + + let ResultOnEmptyStructMetadata { + name, + visibility, + value_ty, + error_path, + variant_name, + span, + } = metadata; + + let generic_kind = match error_path.segments.last() { + Some(PathSegment { + arguments: PathArguments::AngleBracketed(args), + .. + }) => { + let (has_config, has_instance) = + args.args + .iter() + .fold( + (false, false), + |(has_config, has_instance), arg| match arg { + GenericArgument::Type(Type::Path(TypePath { + path: Path { segments, .. }, + .. + })) => { + let maybe_config = + segments.first().map_or(false, |seg| seg.ident == "T"); + let maybe_instance = + segments.first().map_or(false, |seg| seg.ident == "I"); + + (has_config || maybe_config, has_instance || maybe_instance) + } + _ => (has_config, has_instance), + }, + ); + GenericKind::from_gens(has_config, has_instance).unwrap_or(GenericKind::None) + } + _ => GenericKind::None, + }; + let type_impl_gen = generic_kind.type_impl_gen(proc_macro2::Span::call_site()); + let config_where_clause = &def.config.where_clause; + + quote::quote_spanned!(span => + #[doc(hidden)] + #[allow(non_camel_case_types)] + #visibility struct #name; + + impl<#type_impl_gen> #frame_support::traits::Get> + for #name + #config_where_clause + { + fn get() -> Result<#value_ty, #error_path> { + Err(<#error_path>::#variant_name) + } + } + ) + }); + + // aggregated where clause of all storage types and the whole pallet. + let mut where_clauses = vec![&def.config.where_clause]; + where_clauses.extend(def.storages.iter().map(|storage| &storage.where_clause)); + let completed_where_clause = super::merge_where_clauses(&where_clauses); + let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site()); + let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site()); + + let try_decode_entire_state = { + let mut storage_names = def + .storages + .iter() + .filter_map(|storage| { + // A little hacky; don't generate for cfg gated storages to not get compile errors + // when building "frame-feature-testing" gated storages in the "frame-support-test" + // crate. + if storage.try_decode && storage.cfg_attrs.is_empty() { + let ident = &storage.ident; + let gen = &def.type_use_generics(storage.attr_span); + Some(quote::quote_spanned!(storage.attr_span => #ident<#gen> )) + } else { + None + } + }) + .collect::>(); + storage_names.sort_by_cached_key(|ident| ident.to_string()); + + quote::quote!( + #[cfg(feature = "try-runtime")] + impl<#type_impl_gen> #frame_support::traits::TryDecodeEntireStorage + for #pallet_ident<#type_use_gen> #completed_where_clause + { + fn try_decode_entire_state() -> Result> { + let pallet_name = <::PalletInfo as frame_support::traits::PalletInfo> + ::name::<#pallet_ident<#type_use_gen>>() + .expect("Every active pallet has a name in the runtime; qed"); + + #frame_support::__private::log::debug!(target: "runtime::try-decode-state", "trying to decode pallet: {pallet_name}"); + + // NOTE: for now, we have to exclude storage items that are feature gated. + let mut errors = #frame_support::__private::sp_std::vec::Vec::new(); + let mut decoded = 0usize; + + #( + #frame_support::__private::log::debug!(target: "runtime::try-decode-state", "trying to decode storage: \ + {pallet_name}::{}", stringify!(#storage_names)); + + match <#storage_names as #frame_support::traits::TryDecodeEntireStorage>::try_decode_entire_state() { + Ok(count) => { + decoded += count; + }, + Err(err) => { + errors.extend(err); + }, + } + )* + + if errors.is_empty() { + Ok(decoded) + } else { + Err(errors) + } + } + } + ) + }; + + quote::quote!( + impl<#type_impl_gen> #pallet_ident<#type_use_gen> + #completed_where_clause + { + #[doc(hidden)] + pub fn storage_metadata() -> #frame_support::__private::metadata_ir::PalletStorageMetadataIR { + #frame_support::__private::metadata_ir::PalletStorageMetadataIR { + prefix: < + ::PalletInfo as + #frame_support::traits::PalletInfo + >::name::<#pallet_ident<#type_use_gen>>() + .expect("No name found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`."), + entries: { + #[allow(unused_mut)] + let mut entries = #frame_support::__private::sp_std::vec![]; + #( #entries_builder )* + entries + }, + } + } + } + + #( #getters )* + #( #prefix_structs )* + #( #on_empty_structs )* + + #try_decode_entire_state + ) } diff --git a/support/procedural-fork/src/pallet/expand/tasks.rs b/support/procedural-fork/src/pallet/expand/tasks.rs index 6697e5c82..8c4dfb54f 100644 --- a/support/procedural-fork/src/pallet/expand/tasks.rs +++ b/support/procedural-fork/src/pallet/expand/tasks.rs @@ -27,141 +27,145 @@ use quote::{format_ident, quote, ToTokens}; use syn::{parse_quote, spanned::Spanned, ItemEnum, ItemImpl}; impl TaskEnumDef { - /// Since we optionally allow users to manually specify a `#[pallet::task_enum]`, in the - /// event they _don't_ specify one (which is actually the most common behavior) we have to - /// generate one based on the existing [`TasksDef`]. This method performs that generation. - pub fn generate( - tasks: &TasksDef, - type_decl_bounded_generics: TokenStream2, - type_use_generics: TokenStream2, - ) -> Self { - let variants = if tasks.tasks_attr.is_some() { - tasks - .tasks - .iter() - .map(|task| { - let ident = &task.item.sig.ident; - let ident = - format_ident!("{}", ident.to_string().to_class_case(), span = ident.span()); + /// Since we optionally allow users to manually specify a `#[pallet::task_enum]`, in the + /// event they _don't_ specify one (which is actually the most common behavior) we have to + /// generate one based on the existing [`TasksDef`]. This method performs that generation. + pub fn generate( + tasks: &TasksDef, + type_decl_bounded_generics: TokenStream2, + type_use_generics: TokenStream2, + ) -> Self { + let variants = if tasks.tasks_attr.is_some() { + tasks + .tasks + .iter() + .map(|task| { + let ident = &task.item.sig.ident; + let ident = + format_ident!("{}", ident.to_string().to_class_case(), span = ident.span()); - let args = task.item.sig.inputs.iter().collect::>(); + let args = task.item.sig.inputs.iter().collect::>(); - if args.is_empty() { - quote!(#ident) - } else { - quote!(#ident { - #(#args),* - }) - } - }) - .collect::>() - } else { - Vec::new() - }; - let mut task_enum_def: TaskEnumDef = parse_quote! { - /// Auto-generated enum that encapsulates all tasks defined by this pallet. - /// - /// Conceptually similar to the [`Call`] enum, but for tasks. This is only - /// generated if there are tasks present in this pallet. - #[pallet::task_enum] - pub enum Task<#type_decl_bounded_generics> { - #( - #variants, - )* - } - }; - task_enum_def.type_use_generics = type_use_generics; - task_enum_def - } + if args.is_empty() { + quote!(#ident) + } else { + quote!(#ident { + #(#args),* + }) + } + }) + .collect::>() + } else { + Vec::new() + }; + let mut task_enum_def: TaskEnumDef = parse_quote! { + /// Auto-generated enum that encapsulates all tasks defined by this pallet. + /// + /// Conceptually similar to the [`Call`] enum, but for tasks. This is only + /// generated if there are tasks present in this pallet. + #[pallet::task_enum] + pub enum Task<#type_decl_bounded_generics> { + #( + #variants, + )* + } + }; + task_enum_def.type_use_generics = type_use_generics; + task_enum_def + } } impl ToTokens for TaskEnumDef { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let item_enum = &self.item_enum; - let ident = &item_enum.ident; - let vis = &item_enum.vis; - let attrs = &item_enum.attrs; - let generics = &item_enum.generics; - let variants = &item_enum.variants; - let scrate = &self.scrate; - let type_use_generics = &self.type_use_generics; - if self.attr.is_some() { - // `item_enum` is short-hand / generated enum - tokens.extend(quote! { - #(#attrs)* - #[derive( - #scrate::CloneNoBound, - #scrate::EqNoBound, - #scrate::PartialEqNoBound, - #scrate::pallet_prelude::Encode, - #scrate::pallet_prelude::Decode, - #scrate::pallet_prelude::TypeInfo, - )] - #[codec(encode_bound())] - #[codec(decode_bound())] - #[scale_info(skip_type_params(#type_use_generics))] - #vis enum #ident #generics { - #variants - #[doc(hidden)] - #[codec(skip)] - __Ignore(core::marker::PhantomData, #scrate::Never), - } + fn to_tokens(&self, tokens: &mut TokenStream2) { + let item_enum = &self.item_enum; + let ident = &item_enum.ident; + let vis = &item_enum.vis; + let attrs = &item_enum.attrs; + let generics = &item_enum.generics; + let variants = &item_enum.variants; + let scrate = &self.scrate; + let type_use_generics = &self.type_use_generics; + if self.attr.is_some() { + // `item_enum` is short-hand / generated enum + tokens.extend(quote! { + #(#attrs)* + #[derive( + #scrate::CloneNoBound, + #scrate::EqNoBound, + #scrate::PartialEqNoBound, + #scrate::pallet_prelude::Encode, + #scrate::pallet_prelude::Decode, + #scrate::pallet_prelude::TypeInfo, + )] + #[codec(encode_bound())] + #[codec(decode_bound())] + #[scale_info(skip_type_params(#type_use_generics))] + #vis enum #ident #generics { + #variants + #[doc(hidden)] + #[codec(skip)] + __Ignore(core::marker::PhantomData, #scrate::Never), + } - impl core::fmt::Debug for #ident<#type_use_generics> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct(stringify!(#ident)).field("value", self).finish() - } - } - }); - } else { - // `item_enum` is a manually specified enum (no attribute) - tokens.extend(item_enum.to_token_stream()); - } - } + impl core::fmt::Debug for #ident<#type_use_generics> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct(stringify!(#ident)).field("value", self).finish() + } + } + }); + } else { + // `item_enum` is a manually specified enum (no attribute) + tokens.extend(item_enum.to_token_stream()); + } + } } /// Represents an already-expanded [`TasksDef`]. #[derive(Parse)] pub struct ExpandedTasksDef { - pub task_item_impl: ItemImpl, - pub task_trait_impl: ItemImpl, + pub task_item_impl: ItemImpl, + pub task_trait_impl: ItemImpl, } impl ToTokens for TasksDef { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let scrate = &self.scrate; - let enum_ident = syn::Ident::new("Task", self.enum_ident.span()); - let enum_arguments = &self.enum_arguments; - let enum_use = quote!(#enum_ident #enum_arguments); + fn to_tokens(&self, tokens: &mut TokenStream2) { + let scrate = &self.scrate; + let enum_ident = syn::Ident::new("Task", self.enum_ident.span()); + let enum_arguments = &self.enum_arguments; + let enum_use = quote!(#enum_ident #enum_arguments); - let task_fn_idents = self - .tasks - .iter() - .map(|task| { - format_ident!( - "{}", - &task.item.sig.ident.to_string().to_class_case(), - span = task.item.sig.ident.span() - ) - }) - .collect::>(); - let task_indices = self.tasks.iter().map(|task| &task.index_attr.meta.index); - let task_conditions = self.tasks.iter().map(|task| &task.condition_attr.meta.expr); - let task_weights = self.tasks.iter().map(|task| &task.weight_attr.meta.expr); - let task_iters = self.tasks.iter().map(|task| &task.list_attr.meta.expr); + let task_fn_idents = self + .tasks + .iter() + .map(|task| { + format_ident!( + "{}", + &task.item.sig.ident.to_string().to_class_case(), + span = task.item.sig.ident.span() + ) + }) + .collect::>(); + let task_indices = self.tasks.iter().map(|task| &task.index_attr.meta.index); + let task_conditions = self.tasks.iter().map(|task| &task.condition_attr.meta.expr); + let task_weights = self.tasks.iter().map(|task| &task.weight_attr.meta.expr); + let task_iters = self.tasks.iter().map(|task| &task.list_attr.meta.expr); - let task_fn_impls = self.tasks.iter().map(|task| { - let mut task_fn_impl = task.item.clone(); - task_fn_impl.attrs = vec![]; - task_fn_impl - }); + let task_fn_impls = self.tasks.iter().map(|task| { + let mut task_fn_impl = task.item.clone(); + task_fn_impl.attrs = vec![]; + task_fn_impl + }); - let task_fn_names = self.tasks.iter().map(|task| &task.item.sig.ident); - let task_arg_names = self.tasks.iter().map(|task| &task.arg_names).collect::>(); + let task_fn_names = self.tasks.iter().map(|task| &task.item.sig.ident); + let task_arg_names = self + .tasks + .iter() + .map(|task| &task.arg_names) + .collect::>(); - let sp_std = quote!(#scrate::__private::sp_std); - let impl_generics = &self.item_impl.generics; - tokens.extend(quote! { + let sp_std = quote!(#scrate::__private::sp_std); + let impl_generics = &self.item_impl.generics; + tokens.extend(quote! { impl #impl_generics #enum_use { #(#task_fn_impls)* @@ -212,56 +216,66 @@ impl ToTokens for TasksDef { } } }); - } + } } /// Expands the [`TasksDef`] in the enclosing [`Def`], if present, and returns its tokens. /// /// This modifies the underlying [`Def`] in addition to returning any tokens that were added. pub fn expand_tasks_impl(def: &mut Def) -> TokenStream2 { - let Some(tasks) = &mut def.tasks else { return quote!() }; - let ExpandedTasksDef { task_item_impl, task_trait_impl } = parse_quote!(#tasks); - quote! { - #task_item_impl - #task_trait_impl - } + let Some(tasks) = &mut def.tasks else { + return quote!(); + }; + let ExpandedTasksDef { + task_item_impl, + task_trait_impl, + } = parse_quote!(#tasks); + quote! { + #task_item_impl + #task_trait_impl + } } /// Represents a fully-expanded [`TaskEnumDef`]. #[derive(Parse)] pub struct ExpandedTaskEnum { - pub item_enum: ItemEnum, - pub debug_impl: ItemImpl, + pub item_enum: ItemEnum, + pub debug_impl: ItemImpl, } /// Modifies a [`Def`] to expand the underlying [`TaskEnumDef`] if present, and also returns /// its tokens. A blank [`TokenStream2`] is returned if no [`TaskEnumDef`] has been generated /// or defined. pub fn expand_task_enum(def: &mut Def) -> TokenStream2 { - let Some(task_enum) = &mut def.task_enum else { return quote!() }; - let ExpandedTaskEnum { item_enum, debug_impl } = parse_quote!(#task_enum); - quote! { - #item_enum - #debug_impl - } + let Some(task_enum) = &mut def.task_enum else { + return quote!(); + }; + let ExpandedTaskEnum { + item_enum, + debug_impl, + } = parse_quote!(#task_enum); + quote! { + #item_enum + #debug_impl + } } /// Modifies a [`Def`] to expand the underlying [`TasksDef`] and also generate a /// [`TaskEnumDef`] if applicable. The tokens for these items are returned if they are created. pub fn expand_tasks(def: &mut Def) -> TokenStream2 { - if let Some(tasks_def) = &def.tasks { - if def.task_enum.is_none() { - def.task_enum = Some(TaskEnumDef::generate( - &tasks_def, - def.type_decl_bounded_generics(tasks_def.item_impl.span()), - def.type_use_generics(tasks_def.item_impl.span()), - )); - } - } - let tasks_extra_output = expand_tasks_impl(def); - let task_enum_extra_output = expand_task_enum(def); - quote! { - #tasks_extra_output - #task_enum_extra_output - } + if let Some(tasks_def) = &def.tasks { + if def.task_enum.is_none() { + def.task_enum = Some(TaskEnumDef::generate( + &tasks_def, + def.type_decl_bounded_generics(tasks_def.item_impl.span()), + def.type_use_generics(tasks_def.item_impl.span()), + )); + } + } + let tasks_extra_output = expand_tasks_impl(def); + let task_enum_extra_output = expand_task_enum(def); + quote! { + #tasks_extra_output + #task_enum_extra_output + } } diff --git a/support/procedural-fork/src/pallet/expand/tt_default_parts.rs b/support/procedural-fork/src/pallet/expand/tt_default_parts.rs index 99364aaa9..57b78339a 100644 --- a/support/procedural-fork/src/pallet/expand/tt_default_parts.rs +++ b/support/procedural-fork/src/pallet/expand/tt_default_parts.rs @@ -16,201 +16,211 @@ // limitations under the License. use crate::{ - pallet::{CompositeKeyword, Def}, - COUNTER, + pallet::{CompositeKeyword, Def}, + COUNTER, }; use syn::spanned::Spanned; /// Generate the `tt_default_parts` macro. pub fn expand_tt_default_parts(def: &mut Def) -> proc_macro2::TokenStream { - let count = COUNTER.with(|counter| counter.borrow_mut().inc()); - let default_parts_unique_id = - syn::Ident::new(&format!("__tt_default_parts_{}", count), def.item.span()); - let extra_parts_unique_id = - syn::Ident::new(&format!("__tt_extra_parts_{}", count), def.item.span()); - let default_parts_unique_id_v2 = - syn::Ident::new(&format!("__tt_default_parts_v2_{}", count), def.item.span()); - - let call_part = def.call.as_ref().map(|_| quote::quote!(Call,)); - - let task_part = def.task_enum.as_ref().map(|_| quote::quote!(Task,)); - - let storage_part = (!def.storages.is_empty()).then(|| quote::quote!(Storage,)); - - let event_part = def.event.as_ref().map(|event| { - let gen = event.gen_kind.is_generic().then(|| quote::quote!( )); - quote::quote!( Event #gen , ) - }); - - let error_part = def.error.as_ref().map(|_| quote::quote!(Error,)); - - let origin_part = def.origin.as_ref().map(|origin| { - let gen = origin.is_generic.then(|| quote::quote!( )); - quote::quote!( Origin #gen , ) - }); - - let config_part = def.genesis_config.as_ref().map(|genesis_config| { - let gen = genesis_config.gen_kind.is_generic().then(|| quote::quote!( )); - quote::quote!( Config #gen , ) - }); - - let inherent_part = def.inherent.as_ref().map(|_| quote::quote!(Inherent,)); - - let validate_unsigned_part = - def.validate_unsigned.as_ref().map(|_| quote::quote!(ValidateUnsigned,)); - - let freeze_reason_part = def - .composites - .iter() - .any(|c| matches!(c.composite_keyword, CompositeKeyword::FreezeReason(_))) - .then_some(quote::quote!(FreezeReason,)); - - let hold_reason_part = def - .composites - .iter() - .any(|c| matches!(c.composite_keyword, CompositeKeyword::HoldReason(_))) - .then_some(quote::quote!(HoldReason,)); - - let lock_id_part = def - .composites - .iter() - .any(|c| matches!(c.composite_keyword, CompositeKeyword::LockId(_))) - .then_some(quote::quote!(LockId,)); - - let slash_reason_part = def - .composites - .iter() - .any(|c| matches!(c.composite_keyword, CompositeKeyword::SlashReason(_))) - .then_some(quote::quote!(SlashReason,)); - - let call_part_v2 = def.call.as_ref().map(|_| quote::quote!(+ Call)); - - let task_part_v2 = def.task_enum.as_ref().map(|_| quote::quote!(+ Task)); - - let storage_part_v2 = (!def.storages.is_empty()).then(|| quote::quote!(+ Storage)); - - let event_part_v2 = def.event.as_ref().map(|event| { - let gen = event.gen_kind.is_generic().then(|| quote::quote!()); - quote::quote!(+ Event #gen) - }); - - let error_part_v2 = def.error.as_ref().map(|_| quote::quote!(+ Error)); - - let origin_part_v2 = def.origin.as_ref().map(|origin| { - let gen = origin.is_generic.then(|| quote::quote!()); - quote::quote!(+ Origin #gen) - }); - - let config_part_v2 = def.genesis_config.as_ref().map(|genesis_config| { - let gen = genesis_config.gen_kind.is_generic().then(|| quote::quote!()); - quote::quote!(+ Config #gen) - }); - - let inherent_part_v2 = def.inherent.as_ref().map(|_| quote::quote!(+ Inherent)); - - let validate_unsigned_part_v2 = - def.validate_unsigned.as_ref().map(|_| quote::quote!(+ ValidateUnsigned)); - - let freeze_reason_part_v2 = def - .composites - .iter() - .any(|c| matches!(c.composite_keyword, CompositeKeyword::FreezeReason(_))) - .then_some(quote::quote!(+ FreezeReason)); - - let hold_reason_part_v2 = def - .composites - .iter() - .any(|c| matches!(c.composite_keyword, CompositeKeyword::HoldReason(_))) - .then_some(quote::quote!(+ HoldReason)); - - let lock_id_part_v2 = def - .composites - .iter() - .any(|c| matches!(c.composite_keyword, CompositeKeyword::LockId(_))) - .then_some(quote::quote!(+ LockId)); - - let slash_reason_part_v2 = def - .composites - .iter() - .any(|c| matches!(c.composite_keyword, CompositeKeyword::SlashReason(_))) - .then_some(quote::quote!(+ SlashReason)); - - quote::quote!( - // This macro follows the conventions as laid out by the `tt-call` crate. It does not - // accept any arguments and simply returns the pallet parts, separated by commas, then - // wrapped inside of braces and finally prepended with double colons, to the caller inside - // of a key named `tokens`. - // - // We need to accept a path argument here, because this macro gets expanded on the - // crate that called the `construct_runtime!` macro, and the actual path is unknown. - #[macro_export] - #[doc(hidden)] - macro_rules! #default_parts_unique_id { - { - $caller:tt - your_tt_return = [{ $my_tt_return:path }] - } => { - $my_tt_return! { - $caller - tokens = [{ - expanded::{ - Pallet, #call_part #storage_part #event_part #error_part #origin_part #config_part - #inherent_part #validate_unsigned_part #freeze_reason_part #task_part - #hold_reason_part #lock_id_part #slash_reason_part - } - }] - } - }; - } - - pub use #default_parts_unique_id as tt_default_parts; - - - // This macro is similar to the `tt_default_parts!`. It expands the pallets that are declared - // explicitly (`System: frame_system::{Pallet, Call}`) with extra parts. - // - // For example, after expansion an explicit pallet would look like: - // `System: expanded::{Error} ::{Pallet, Call}`. - // - // The `expanded` keyword is a marker of the final state of the `construct_runtime!`. - #[macro_export] - #[doc(hidden)] - macro_rules! #extra_parts_unique_id { - { - $caller:tt - your_tt_return = [{ $my_tt_return:path }] - } => { - $my_tt_return! { - $caller - tokens = [{ - expanded::{ - #error_part - } - }] - } - }; - } - - pub use #extra_parts_unique_id as tt_extra_parts; - - #[macro_export] - #[doc(hidden)] - macro_rules! #default_parts_unique_id_v2 { - { - $caller:tt - frame_support = [{ $($frame_support:ident)::* }] - } => { - $($frame_support)*::__private::tt_return! { - $caller - tokens = [{ - + Pallet #call_part_v2 #storage_part_v2 #event_part_v2 #error_part_v2 #origin_part_v2 #config_part_v2 - #inherent_part_v2 #validate_unsigned_part_v2 #freeze_reason_part_v2 #task_part_v2 - #hold_reason_part_v2 #lock_id_part_v2 #slash_reason_part_v2 - }] - } - }; - } - - pub use #default_parts_unique_id_v2 as tt_default_parts_v2; - ) + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + let default_parts_unique_id = + syn::Ident::new(&format!("__tt_default_parts_{}", count), def.item.span()); + let extra_parts_unique_id = + syn::Ident::new(&format!("__tt_extra_parts_{}", count), def.item.span()); + let default_parts_unique_id_v2 = + syn::Ident::new(&format!("__tt_default_parts_v2_{}", count), def.item.span()); + + let call_part = def.call.as_ref().map(|_| quote::quote!(Call,)); + + let task_part = def.task_enum.as_ref().map(|_| quote::quote!(Task,)); + + let storage_part = (!def.storages.is_empty()).then(|| quote::quote!(Storage,)); + + let event_part = def.event.as_ref().map(|event| { + let gen = event.gen_kind.is_generic().then(|| quote::quote!( )); + quote::quote!( Event #gen , ) + }); + + let error_part = def.error.as_ref().map(|_| quote::quote!(Error,)); + + let origin_part = def.origin.as_ref().map(|origin| { + let gen = origin.is_generic.then(|| quote::quote!( )); + quote::quote!( Origin #gen , ) + }); + + let config_part = def.genesis_config.as_ref().map(|genesis_config| { + let gen = genesis_config + .gen_kind + .is_generic() + .then(|| quote::quote!( )); + quote::quote!( Config #gen , ) + }); + + let inherent_part = def.inherent.as_ref().map(|_| quote::quote!(Inherent,)); + + let validate_unsigned_part = def + .validate_unsigned + .as_ref() + .map(|_| quote::quote!(ValidateUnsigned,)); + + let freeze_reason_part = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::FreezeReason(_))) + .then_some(quote::quote!(FreezeReason,)); + + let hold_reason_part = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::HoldReason(_))) + .then_some(quote::quote!(HoldReason,)); + + let lock_id_part = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::LockId(_))) + .then_some(quote::quote!(LockId,)); + + let slash_reason_part = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::SlashReason(_))) + .then_some(quote::quote!(SlashReason,)); + + let call_part_v2 = def.call.as_ref().map(|_| quote::quote!(+ Call)); + + let task_part_v2 = def.task_enum.as_ref().map(|_| quote::quote!(+ Task)); + + let storage_part_v2 = (!def.storages.is_empty()).then(|| quote::quote!(+ Storage)); + + let event_part_v2 = def.event.as_ref().map(|event| { + let gen = event.gen_kind.is_generic().then(|| quote::quote!()); + quote::quote!(+ Event #gen) + }); + + let error_part_v2 = def.error.as_ref().map(|_| quote::quote!(+ Error)); + + let origin_part_v2 = def.origin.as_ref().map(|origin| { + let gen = origin.is_generic.then(|| quote::quote!()); + quote::quote!(+ Origin #gen) + }); + + let config_part_v2 = def.genesis_config.as_ref().map(|genesis_config| { + let gen = genesis_config + .gen_kind + .is_generic() + .then(|| quote::quote!()); + quote::quote!(+ Config #gen) + }); + + let inherent_part_v2 = def.inherent.as_ref().map(|_| quote::quote!(+ Inherent)); + + let validate_unsigned_part_v2 = def + .validate_unsigned + .as_ref() + .map(|_| quote::quote!(+ ValidateUnsigned)); + + let freeze_reason_part_v2 = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::FreezeReason(_))) + .then_some(quote::quote!(+ FreezeReason)); + + let hold_reason_part_v2 = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::HoldReason(_))) + .then_some(quote::quote!(+ HoldReason)); + + let lock_id_part_v2 = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::LockId(_))) + .then_some(quote::quote!(+ LockId)); + + let slash_reason_part_v2 = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::SlashReason(_))) + .then_some(quote::quote!(+ SlashReason)); + + quote::quote!( + // This macro follows the conventions as laid out by the `tt-call` crate. It does not + // accept any arguments and simply returns the pallet parts, separated by commas, then + // wrapped inside of braces and finally prepended with double colons, to the caller inside + // of a key named `tokens`. + // + // We need to accept a path argument here, because this macro gets expanded on the + // crate that called the `construct_runtime!` macro, and the actual path is unknown. + #[macro_export] + #[doc(hidden)] + macro_rules! #default_parts_unique_id { + { + $caller:tt + your_tt_return = [{ $my_tt_return:path }] + } => { + $my_tt_return! { + $caller + tokens = [{ + expanded::{ + Pallet, #call_part #storage_part #event_part #error_part #origin_part #config_part + #inherent_part #validate_unsigned_part #freeze_reason_part #task_part + #hold_reason_part #lock_id_part #slash_reason_part + } + }] + } + }; + } + + pub use #default_parts_unique_id as tt_default_parts; + + + // This macro is similar to the `tt_default_parts!`. It expands the pallets that are declared + // explicitly (`System: frame_system::{Pallet, Call}`) with extra parts. + // + // For example, after expansion an explicit pallet would look like: + // `System: expanded::{Error} ::{Pallet, Call}`. + // + // The `expanded` keyword is a marker of the final state of the `construct_runtime!`. + #[macro_export] + #[doc(hidden)] + macro_rules! #extra_parts_unique_id { + { + $caller:tt + your_tt_return = [{ $my_tt_return:path }] + } => { + $my_tt_return! { + $caller + tokens = [{ + expanded::{ + #error_part + } + }] + } + }; + } + + pub use #extra_parts_unique_id as tt_extra_parts; + + #[macro_export] + #[doc(hidden)] + macro_rules! #default_parts_unique_id_v2 { + { + $caller:tt + frame_support = [{ $($frame_support:ident)::* }] + } => { + $($frame_support)*::__private::tt_return! { + $caller + tokens = [{ + + Pallet #call_part_v2 #storage_part_v2 #event_part_v2 #error_part_v2 #origin_part_v2 #config_part_v2 + #inherent_part_v2 #validate_unsigned_part_v2 #freeze_reason_part_v2 #task_part_v2 + #hold_reason_part_v2 #lock_id_part_v2 #slash_reason_part_v2 + }] + } + }; + } + + pub use #default_parts_unique_id_v2 as tt_default_parts_v2; + ) } diff --git a/support/procedural-fork/src/pallet/expand/type_value.rs b/support/procedural-fork/src/pallet/expand/type_value.rs index 5dc6309c0..84db3e431 100644 --- a/support/procedural-fork/src/pallet/expand/type_value.rs +++ b/support/procedural-fork/src/pallet/expand/type_value.rs @@ -22,56 +22,56 @@ use crate::pallet::Def; /// * implement the `Get<..>` on it /// * Rename the name of the function to internal name pub fn expand_type_values(def: &mut Def) -> proc_macro2::TokenStream { - let mut expand = quote::quote!(); - let frame_support = &def.frame_support; + let mut expand = quote::quote!(); + let frame_support = &def.frame_support; - for type_value in &def.type_values { - let fn_name_str = &type_value.ident.to_string(); - let fn_name_snakecase = inflector::cases::snakecase::to_snake_case(fn_name_str); - let fn_ident_renamed = syn::Ident::new( - &format!("__type_value_for_{}", fn_name_snakecase), - type_value.ident.span(), - ); + for type_value in &def.type_values { + let fn_name_str = &type_value.ident.to_string(); + let fn_name_snakecase = inflector::cases::snakecase::to_snake_case(fn_name_str); + let fn_ident_renamed = syn::Ident::new( + &format!("__type_value_for_{}", fn_name_snakecase), + type_value.ident.span(), + ); - let type_value_item = { - let item = &mut def.item.content.as_mut().expect("Checked by def").1[type_value.index]; - if let syn::Item::Fn(item) = item { - item - } else { - unreachable!("Checked by error parser") - } - }; + let type_value_item = { + let item = &mut def.item.content.as_mut().expect("Checked by def").1[type_value.index]; + if let syn::Item::Fn(item) = item { + item + } else { + unreachable!("Checked by error parser") + } + }; - // Rename the type_value function name - type_value_item.sig.ident = fn_ident_renamed.clone(); + // Rename the type_value function name + type_value_item.sig.ident = fn_ident_renamed.clone(); - let vis = &type_value.vis; - let ident = &type_value.ident; - let type_ = &type_value.type_; - let where_clause = &type_value.where_clause; + let vis = &type_value.vis; + let ident = &type_value.ident; + let type_ = &type_value.type_; + let where_clause = &type_value.where_clause; - let (struct_impl_gen, struct_use_gen) = if type_value.is_generic { - ( - def.type_impl_generics(type_value.attr_span), - def.type_use_generics(type_value.attr_span), - ) - } else { - (Default::default(), Default::default()) - }; + let (struct_impl_gen, struct_use_gen) = if type_value.is_generic { + ( + def.type_impl_generics(type_value.attr_span), + def.type_use_generics(type_value.attr_span), + ) + } else { + (Default::default(), Default::default()) + }; - let docs = &type_value.docs; + let docs = &type_value.docs; - expand.extend(quote::quote_spanned!(type_value.attr_span => - #( #[doc = #docs] )* - #vis struct #ident<#struct_use_gen>(core::marker::PhantomData<((), #struct_use_gen)>); - impl<#struct_impl_gen> #frame_support::traits::Get<#type_> for #ident<#struct_use_gen> - #where_clause - { - fn get() -> #type_ { - #fn_ident_renamed::<#struct_use_gen>() - } - } - )); - } - expand + expand.extend(quote::quote_spanned!(type_value.attr_span => + #( #[doc = #docs] )* + #vis struct #ident<#struct_use_gen>(core::marker::PhantomData<((), #struct_use_gen)>); + impl<#struct_impl_gen> #frame_support::traits::Get<#type_> for #ident<#struct_use_gen> + #where_clause + { + fn get() -> #type_ { + #fn_ident_renamed::<#struct_use_gen>() + } + } + )); + } + expand } diff --git a/support/procedural-fork/src/pallet/expand/validate_unsigned.rs b/support/procedural-fork/src/pallet/expand/validate_unsigned.rs index 876995585..28c78a1c6 100644 --- a/support/procedural-fork/src/pallet/expand/validate_unsigned.rs +++ b/support/procedural-fork/src/pallet/expand/validate_unsigned.rs @@ -21,36 +21,38 @@ use quote::quote; use syn::{spanned::Spanned, Ident}; pub fn expand_validate_unsigned(def: &mut Def) -> TokenStream { - let count = COUNTER.with(|counter| counter.borrow_mut().inc()); - let macro_ident = - Ident::new(&format!("__is_validate_unsigned_part_defined_{}", count), def.item.span()); + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + let macro_ident = Ident::new( + &format!("__is_validate_unsigned_part_defined_{}", count), + def.item.span(), + ); - let maybe_compile_error = if def.validate_unsigned.is_none() { - quote! { - compile_error!(concat!( - "`", - stringify!($pallet_name), - "` does not have #[pallet::validate_unsigned] defined, perhaps you should \ - remove `ValidateUnsigned` from construct_runtime?", - )); - } - } else { - TokenStream::new() - }; + let maybe_compile_error = if def.validate_unsigned.is_none() { + quote! { + compile_error!(concat!( + "`", + stringify!($pallet_name), + "` does not have #[pallet::validate_unsigned] defined, perhaps you should \ + remove `ValidateUnsigned` from construct_runtime?", + )); + } + } else { + TokenStream::new() + }; - quote! { - #[doc(hidden)] - pub mod __substrate_validate_unsigned_check { - #[macro_export] - #[doc(hidden)] - macro_rules! #macro_ident { - ($pallet_name:ident) => { - #maybe_compile_error - } - } + quote! { + #[doc(hidden)] + pub mod __substrate_validate_unsigned_check { + #[macro_export] + #[doc(hidden)] + macro_rules! #macro_ident { + ($pallet_name:ident) => { + #maybe_compile_error + } + } - #[doc(hidden)] - pub use #macro_ident as is_validate_unsigned_part_defined; - } - } + #[doc(hidden)] + pub use #macro_ident as is_validate_unsigned_part_defined; + } + } } diff --git a/support/procedural-fork/src/pallet/expand/warnings.rs b/support/procedural-fork/src/pallet/expand/warnings.rs index 030e3ddaf..3d71b83af 100644 --- a/support/procedural-fork/src/pallet/expand/warnings.rs +++ b/support/procedural-fork/src/pallet/expand/warnings.rs @@ -20,79 +20,84 @@ use crate::pallet::parse::call::{CallVariantDef, CallWeightDef}; use proc_macro_warning::Warning; use syn::{ - spanned::Spanned, - visit::{self, Visit}, + spanned::Spanned, + visit::{self, Visit}, }; /// Warn if any of the call arguments starts with a underscore and is used in a weight formula. pub(crate) fn weight_witness_warning( - method: &CallVariantDef, - dev_mode: bool, - warnings: &mut Vec, + method: &CallVariantDef, + dev_mode: bool, + warnings: &mut Vec, ) { - if dev_mode { - return - } - let CallWeightDef::Immediate(w) = &method.weight else { return }; + if dev_mode { + return; + } + let CallWeightDef::Immediate(w) = &method.weight else { + return; + }; - let partial_warning = Warning::new_deprecated("UncheckedWeightWitness") - .old("not check weight witness data") - .new("ensure that all witness data for weight calculation is checked before usage") - .help_link("https://github.com/paritytech/polkadot-sdk/pull/1818"); + let partial_warning = Warning::new_deprecated("UncheckedWeightWitness") + .old("not check weight witness data") + .new("ensure that all witness data for weight calculation is checked before usage") + .help_link("https://github.com/paritytech/polkadot-sdk/pull/1818"); - for (_, arg_ident, _) in method.args.iter() { - if !arg_ident.to_string().starts_with('_') || !contains_ident(w.clone(), &arg_ident) { - continue - } + for (_, arg_ident, _) in method.args.iter() { + if !arg_ident.to_string().starts_with('_') || !contains_ident(w.clone(), &arg_ident) { + continue; + } - let warning = partial_warning - .clone() - .index(warnings.len()) - .span(arg_ident.span()) - .build_or_panic(); + let warning = partial_warning + .clone() + .index(warnings.len()) + .span(arg_ident.span()) + .build_or_panic(); - warnings.push(warning); - } + warnings.push(warning); + } } /// Warn if the weight is a constant and the pallet not in `dev_mode`. pub(crate) fn weight_constant_warning( - weight: &syn::Expr, - dev_mode: bool, - warnings: &mut Vec, + weight: &syn::Expr, + dev_mode: bool, + warnings: &mut Vec, ) { - if dev_mode { - return - } - let syn::Expr::Lit(lit) = weight else { return }; + if dev_mode { + return; + } + let syn::Expr::Lit(lit) = weight else { return }; - let warning = Warning::new_deprecated("ConstantWeight") - .index(warnings.len()) - .old("use hard-coded constant as call weight") - .new("benchmark all calls or put the pallet into `dev` mode") - .help_link("https://github.com/paritytech/substrate/pull/13798") - .span(lit.span()) - .build_or_panic(); + let warning = Warning::new_deprecated("ConstantWeight") + .index(warnings.len()) + .old("use hard-coded constant as call weight") + .new("benchmark all calls or put the pallet into `dev` mode") + .help_link("https://github.com/paritytech/substrate/pull/13798") + .span(lit.span()) + .build_or_panic(); - warnings.push(warning); + warnings.push(warning); } /// Returns whether `expr` contains `ident`. fn contains_ident(mut expr: syn::Expr, ident: &syn::Ident) -> bool { - struct ContainsIdent { - ident: syn::Ident, - found: bool, - } + struct ContainsIdent { + ident: syn::Ident, + found: bool, + } - impl<'a> Visit<'a> for ContainsIdent { - fn visit_ident(&mut self, i: &syn::Ident) { - if *i == self.ident { - self.found = true; - } - } - } + impl<'a> Visit<'a> for ContainsIdent { + fn visit_ident(&mut self, i: &syn::Ident) { + if *i == self.ident { + self.found = true; + } + } + } - let mut visitor = ContainsIdent { ident: ident.clone(), found: false }; - visit::visit_expr(&mut visitor, &mut expr); - visitor.found + let mut visitor = ContainsIdent { + ident: ident.clone(), + found: false, + }; + visit::visit_expr(&mut visitor, &mut expr); + visitor.found } diff --git a/support/procedural-fork/src/pallet/mod.rs b/support/procedural-fork/src/pallet/mod.rs index 42d8272fb..d3796662f 100644 --- a/support/procedural-fork/src/pallet/mod.rs +++ b/support/procedural-fork/src/pallet/mod.rs @@ -32,30 +32,30 @@ pub use parse::{composite::keyword::CompositeKeyword, Def}; use syn::spanned::Spanned; mod keyword { - syn::custom_keyword!(dev_mode); + syn::custom_keyword!(dev_mode); } pub fn pallet( - attr: proc_macro::TokenStream, - item: proc_macro::TokenStream, + attr: proc_macro::TokenStream, + item: proc_macro::TokenStream, ) -> proc_macro::TokenStream { - let mut dev_mode = false; - if !attr.is_empty() { - if let Ok(_) = syn::parse::(attr.clone()) { - dev_mode = true; - } else { - let msg = "Invalid pallet macro call: unexpected attribute. Macro call must be \ + let mut dev_mode = false; + if !attr.is_empty() { + if let Ok(_) = syn::parse::(attr.clone()) { + dev_mode = true; + } else { + let msg = "Invalid pallet macro call: unexpected attribute. Macro call must be \ bare, such as `#[frame_support::pallet]` or `#[pallet]`, or must specify the \ `dev_mode` attribute, such as `#[frame_support::pallet(dev_mode)]` or \ #[pallet(dev_mode)]."; - let span = proc_macro2::TokenStream::from(attr).span(); - return syn::Error::new(span, msg).to_compile_error().into() - } - } - - let item = syn::parse_macro_input!(item as syn::ItemMod); - match parse::Def::try_from(item, dev_mode) { - Ok(def) => expand::expand(def).into(), - Err(e) => e.to_compile_error().into(), - } + let span = proc_macro2::TokenStream::from(attr).span(); + return syn::Error::new(span, msg).to_compile_error().into(); + } + } + + let item = syn::parse_macro_input!(item as syn::ItemMod); + match parse::Def::try_from(item, dev_mode) { + Ok(def) => expand::expand(def).into(), + Err(e) => e.to_compile_error().into(), + } } diff --git a/support/procedural-fork/src/pallet/parse/call.rs b/support/procedural-fork/src/pallet/parse/call.rs index 4e09b86fd..865c63473 100644 --- a/support/procedural-fork/src/pallet/parse/call.rs +++ b/support/procedural-fork/src/pallet/parse/call.rs @@ -24,124 +24,124 @@ use syn::{spanned::Spanned, ExprClosure}; /// List of additional token to be used for parsing. mod keyword { - syn::custom_keyword!(Call); - syn::custom_keyword!(OriginFor); - syn::custom_keyword!(RuntimeOrigin); - syn::custom_keyword!(weight); - syn::custom_keyword!(call_index); - syn::custom_keyword!(compact); - syn::custom_keyword!(T); - syn::custom_keyword!(pallet); - syn::custom_keyword!(feeless_if); + syn::custom_keyword!(Call); + syn::custom_keyword!(OriginFor); + syn::custom_keyword!(RuntimeOrigin); + syn::custom_keyword!(weight); + syn::custom_keyword!(call_index); + syn::custom_keyword!(compact); + syn::custom_keyword!(T); + syn::custom_keyword!(pallet); + syn::custom_keyword!(feeless_if); } /// Definition of dispatchables typically `impl Pallet { ... }` pub struct CallDef { - /// The where_clause used. - pub where_clause: Option, - /// A set of usage of instance, must be check for consistency with trait. - pub instances: Vec, - /// The index of call item in pallet module. - pub index: usize, - /// Information on methods (used for expansion). - pub methods: Vec, - /// The span of the pallet::call attribute. - pub attr_span: proc_macro2::Span, - /// Docs, specified on the impl Block. - pub docs: Vec, - /// The optional `weight` attribute on the `pallet::call`. - pub inherited_call_weight: Option, + /// The where_clause used. + pub where_clause: Option, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Vec, + /// The index of call item in pallet module. + pub index: usize, + /// Information on methods (used for expansion). + pub methods: Vec, + /// The span of the pallet::call attribute. + pub attr_span: proc_macro2::Span, + /// Docs, specified on the impl Block. + pub docs: Vec, + /// The optional `weight` attribute on the `pallet::call`. + pub inherited_call_weight: Option, } /// The weight of a call. #[derive(Clone)] pub enum CallWeightDef { - /// Explicitly set on the call itself with `#[pallet::weight(…)]`. This value is used. - Immediate(syn::Expr), + /// Explicitly set on the call itself with `#[pallet::weight(…)]`. This value is used. + Immediate(syn::Expr), - /// The default value that should be set for dev-mode pallets. Usually zero. - DevModeDefault, + /// The default value that should be set for dev-mode pallets. Usually zero. + DevModeDefault, - /// Inherits whatever value is configured on the pallet level. - /// - /// The concrete value is not known at this point. - Inherited, + /// Inherits whatever value is configured on the pallet level. + /// + /// The concrete value is not known at this point. + Inherited, } /// Definition of dispatchable typically: `#[weight...] fn foo(origin .., param1: ...) -> ..` #[derive(Clone)] pub struct CallVariantDef { - /// Function name. - pub name: syn::Ident, - /// Information on args: `(is_compact, name, type)` - pub args: Vec<(bool, syn::Ident, Box)>, - /// Weight for the call. - pub weight: CallWeightDef, - /// Call index of the dispatchable. - pub call_index: u8, - /// Whether an explicit call index was specified. - pub explicit_call_index: bool, - /// Docs, used for metadata. - pub docs: Vec, - /// Attributes annotated at the top of the dispatchable function. - pub attrs: Vec, - /// The `cfg` attributes. - pub cfg_attrs: Vec, - /// The optional `feeless_if` attribute on the `pallet::call`. - pub feeless_check: Option, + /// Function name. + pub name: syn::Ident, + /// Information on args: `(is_compact, name, type)` + pub args: Vec<(bool, syn::Ident, Box)>, + /// Weight for the call. + pub weight: CallWeightDef, + /// Call index of the dispatchable. + pub call_index: u8, + /// Whether an explicit call index was specified. + pub explicit_call_index: bool, + /// Docs, used for metadata. + pub docs: Vec, + /// Attributes annotated at the top of the dispatchable function. + pub attrs: Vec, + /// The `cfg` attributes. + pub cfg_attrs: Vec, + /// The optional `feeless_if` attribute on the `pallet::call`. + pub feeless_check: Option, } /// Attributes for functions in call impl block. pub enum FunctionAttr { - /// Parse for `#[pallet::call_index(expr)]` - CallIndex(u8), - /// Parse for `#[pallet::weight(expr)]` - Weight(syn::Expr), - /// Parse for `#[pallet::feeless_if(expr)]` - FeelessIf(Span, syn::ExprClosure), + /// Parse for `#[pallet::call_index(expr)]` + CallIndex(u8), + /// Parse for `#[pallet::weight(expr)]` + Weight(syn::Expr), + /// Parse for `#[pallet::feeless_if(expr)]` + FeelessIf(Span, syn::ExprClosure), } impl syn::parse::Parse for FunctionAttr { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - let content; - syn::bracketed!(content in input); - content.parse::()?; - content.parse::()?; - - let lookahead = content.lookahead1(); - if lookahead.peek(keyword::weight) { - content.parse::()?; - let weight_content; - syn::parenthesized!(weight_content in content); - Ok(FunctionAttr::Weight(weight_content.parse::()?)) - } else if lookahead.peek(keyword::call_index) { - content.parse::()?; - let call_index_content; - syn::parenthesized!(call_index_content in content); - let index = call_index_content.parse::()?; - if !index.suffix().is_empty() { - let msg = "Number literal must not have a suffix"; - return Err(syn::Error::new(index.span(), msg)) - } - Ok(FunctionAttr::CallIndex(index.base10_parse()?)) - } else if lookahead.peek(keyword::feeless_if) { - content.parse::()?; - let closure_content; - syn::parenthesized!(closure_content in content); - Ok(FunctionAttr::FeelessIf( - closure_content.span(), - closure_content.parse::().map_err(|e| { - let msg = "Invalid feeless_if attribute: expected a closure"; - let mut err = syn::Error::new(closure_content.span(), msg); - err.combine(e); - err - })?, - )) - } else { - Err(lookahead.error()) - } - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + let lookahead = content.lookahead1(); + if lookahead.peek(keyword::weight) { + content.parse::()?; + let weight_content; + syn::parenthesized!(weight_content in content); + Ok(FunctionAttr::Weight(weight_content.parse::()?)) + } else if lookahead.peek(keyword::call_index) { + content.parse::()?; + let call_index_content; + syn::parenthesized!(call_index_content in content); + let index = call_index_content.parse::()?; + if !index.suffix().is_empty() { + let msg = "Number literal must not have a suffix"; + return Err(syn::Error::new(index.span(), msg)); + } + Ok(FunctionAttr::CallIndex(index.base10_parse()?)) + } else if lookahead.peek(keyword::feeless_if) { + content.parse::()?; + let closure_content; + syn::parenthesized!(closure_content in content); + Ok(FunctionAttr::FeelessIf( + closure_content.span(), + closure_content.parse::().map_err(|e| { + let msg = "Invalid feeless_if attribute: expected a closure"; + let mut err = syn::Error::new(closure_content.span(), msg); + err.combine(e); + err + })?, + )) + } else { + Err(lookahead.error()) + } + } } /// Attribute for arguments in function in call impl block. @@ -149,319 +149,324 @@ impl syn::parse::Parse for FunctionAttr { pub struct ArgAttrIsCompact; impl syn::parse::Parse for ArgAttrIsCompact { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - let content; - syn::bracketed!(content in input); - content.parse::()?; - content.parse::()?; - - content.parse::()?; - Ok(ArgAttrIsCompact) - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + content.parse::()?; + Ok(ArgAttrIsCompact) + } } /// Check the syntax is `OriginFor`, `&OriginFor` or `T::RuntimeOrigin`. pub fn check_dispatchable_first_arg_type(ty: &syn::Type, is_ref: bool) -> syn::Result<()> { - pub struct CheckOriginFor(bool); - impl syn::parse::Parse for CheckOriginFor { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let is_ref = input.parse::().is_ok(); - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::]>()?; - - Ok(Self(is_ref)) - } - } - - pub struct CheckRuntimeOrigin; - impl syn::parse::Parse for CheckRuntimeOrigin { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - input.parse::()?; - input.parse::()?; - - Ok(Self) - } - } - - let result_origin_for = syn::parse2::(ty.to_token_stream()); - let result_runtime_origin = syn::parse2::(ty.to_token_stream()); - return match (result_origin_for, result_runtime_origin) { - (Ok(CheckOriginFor(has_ref)), _) if is_ref == has_ref => Ok(()), - (_, Ok(_)) => Ok(()), - (_, _) => { - let msg = if is_ref { - "Invalid type: expected `&OriginFor`" - } else { - "Invalid type: expected `OriginFor` or `T::RuntimeOrigin`" - }; - return Err(syn::Error::new(ty.span(), msg)) - }, - } + pub struct CheckOriginFor(bool); + impl syn::parse::Parse for CheckOriginFor { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let is_ref = input.parse::().is_ok(); + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::]>()?; + + Ok(Self(is_ref)) + } + } + + pub struct CheckRuntimeOrigin; + impl syn::parse::Parse for CheckRuntimeOrigin { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + input.parse::()?; + input.parse::()?; + + Ok(Self) + } + } + + let result_origin_for = syn::parse2::(ty.to_token_stream()); + let result_runtime_origin = syn::parse2::(ty.to_token_stream()); + return match (result_origin_for, result_runtime_origin) { + (Ok(CheckOriginFor(has_ref)), _) if is_ref == has_ref => Ok(()), + (_, Ok(_)) => Ok(()), + (_, _) => { + let msg = if is_ref { + "Invalid type: expected `&OriginFor`" + } else { + "Invalid type: expected `OriginFor` or `T::RuntimeOrigin`" + }; + return Err(syn::Error::new(ty.span(), msg)); + } + }; } impl CallDef { - pub fn try_from( - attr_span: proc_macro2::Span, - index: usize, - item: &mut syn::Item, - dev_mode: bool, - inherited_call_weight: Option, - ) -> syn::Result { - let item_impl = if let syn::Item::Impl(item) = item { - item - } else { - return Err(syn::Error::new(item.span(), "Invalid pallet::call, expected item impl")) - }; - - let instances = vec![ - helper::check_impl_gen(&item_impl.generics, item_impl.impl_token.span())?, - helper::check_pallet_struct_usage(&item_impl.self_ty)?, - ]; - - if let Some((_, _, for_)) = item_impl.trait_ { - let msg = "Invalid pallet::call, expected no trait ident as in \ + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + dev_mode: bool, + inherited_call_weight: Option, + ) -> syn::Result { + let item_impl = if let syn::Item::Impl(item) = item { + item + } else { + return Err(syn::Error::new( + item.span(), + "Invalid pallet::call, expected item impl", + )); + }; + + let instances = vec![ + helper::check_impl_gen(&item_impl.generics, item_impl.impl_token.span())?, + helper::check_pallet_struct_usage(&item_impl.self_ty)?, + ]; + + if let Some((_, _, for_)) = item_impl.trait_ { + let msg = "Invalid pallet::call, expected no trait ident as in \ `impl<..> Pallet<..> { .. }`"; - return Err(syn::Error::new(for_.span(), msg)) - } - - let mut methods = vec![]; - let mut indices = HashMap::new(); - let mut last_index: Option = None; - for item in &mut item_impl.items { - if let syn::ImplItem::Fn(method) = item { - if !matches!(method.vis, syn::Visibility::Public(_)) { - let msg = "Invalid pallet::call, dispatchable function must be public: \ + return Err(syn::Error::new(for_.span(), msg)); + } + + let mut methods = vec![]; + let mut indices = HashMap::new(); + let mut last_index: Option = None; + for item in &mut item_impl.items { + if let syn::ImplItem::Fn(method) = item { + if !matches!(method.vis, syn::Visibility::Public(_)) { + let msg = "Invalid pallet::call, dispatchable function must be public: \ `pub fn`"; - let span = match method.vis { - syn::Visibility::Inherited => method.sig.span(), - _ => method.vis.span(), - }; - - return Err(syn::Error::new(span, msg)) - } - - match method.sig.inputs.first() { - None => { - let msg = "Invalid pallet::call, must have at least origin arg"; - return Err(syn::Error::new(method.sig.span(), msg)) - }, - Some(syn::FnArg::Receiver(_)) => { - let msg = "Invalid pallet::call, first argument must be a typed argument, \ + let span = match method.vis { + syn::Visibility::Inherited => method.sig.span(), + _ => method.vis.span(), + }; + + return Err(syn::Error::new(span, msg)); + } + + match method.sig.inputs.first() { + None => { + let msg = "Invalid pallet::call, must have at least origin arg"; + return Err(syn::Error::new(method.sig.span(), msg)); + } + Some(syn::FnArg::Receiver(_)) => { + let msg = "Invalid pallet::call, first argument must be a typed argument, \ e.g. `origin: OriginFor`"; - return Err(syn::Error::new(method.sig.span(), msg)) - }, - Some(syn::FnArg::Typed(arg)) => { - check_dispatchable_first_arg_type(&arg.ty, false)?; - }, - } - - if let syn::ReturnType::Type(_, type_) = &method.sig.output { - helper::check_pallet_call_return_type(type_)?; - } else { - let msg = "Invalid pallet::call, require return type \ + return Err(syn::Error::new(method.sig.span(), msg)); + } + Some(syn::FnArg::Typed(arg)) => { + check_dispatchable_first_arg_type(&arg.ty, false)?; + } + } + + if let syn::ReturnType::Type(_, type_) = &method.sig.output { + helper::check_pallet_call_return_type(type_)?; + } else { + let msg = "Invalid pallet::call, require return type \ DispatchResultWithPostInfo"; - return Err(syn::Error::new(method.sig.span(), msg)) - } - - let cfg_attrs: Vec = helper::get_item_cfg_attrs(&method.attrs); - let mut call_idx_attrs = vec![]; - let mut weight_attrs = vec![]; - let mut feeless_attrs = vec![]; - for attr in helper::take_item_pallet_attrs(&mut method.attrs)?.into_iter() { - match attr { - FunctionAttr::CallIndex(_) => { - call_idx_attrs.push(attr); - }, - FunctionAttr::Weight(_) => { - weight_attrs.push(attr); - }, - FunctionAttr::FeelessIf(span, _) => { - feeless_attrs.push((span, attr)); - }, - } - } - - if weight_attrs.is_empty() && dev_mode { - // inject a default O(1) weight when dev mode is enabled and no weight has - // been specified on the call - let empty_weight: syn::Expr = syn::parse_quote!(0); - weight_attrs.push(FunctionAttr::Weight(empty_weight)); - } - - let weight = match weight_attrs.len() { - 0 if inherited_call_weight.is_some() => CallWeightDef::Inherited, - 0 if dev_mode => CallWeightDef::DevModeDefault, - 0 => return Err(syn::Error::new( - method.sig.span(), - "A pallet::call requires either a concrete `#[pallet::weight($expr)]` or an + return Err(syn::Error::new(method.sig.span(), msg)); + } + + let cfg_attrs: Vec = helper::get_item_cfg_attrs(&method.attrs); + let mut call_idx_attrs = vec![]; + let mut weight_attrs = vec![]; + let mut feeless_attrs = vec![]; + for attr in helper::take_item_pallet_attrs(&mut method.attrs)?.into_iter() { + match attr { + FunctionAttr::CallIndex(_) => { + call_idx_attrs.push(attr); + } + FunctionAttr::Weight(_) => { + weight_attrs.push(attr); + } + FunctionAttr::FeelessIf(span, _) => { + feeless_attrs.push((span, attr)); + } + } + } + + if weight_attrs.is_empty() && dev_mode { + // inject a default O(1) weight when dev mode is enabled and no weight has + // been specified on the call + let empty_weight: syn::Expr = syn::parse_quote!(0); + weight_attrs.push(FunctionAttr::Weight(empty_weight)); + } + + let weight = match weight_attrs.len() { + 0 if inherited_call_weight.is_some() => CallWeightDef::Inherited, + 0 if dev_mode => CallWeightDef::DevModeDefault, + 0 => return Err(syn::Error::new( + method.sig.span(), + "A pallet::call requires either a concrete `#[pallet::weight($expr)]` or an inherited weight from the `#[pallet:call(weight($type))]` attribute, but none were given.", - )), - 1 => match weight_attrs.pop().unwrap() { - FunctionAttr::Weight(w) => CallWeightDef::Immediate(w), - _ => unreachable!("checked during creation of the let binding"), - }, - _ => { - let msg = "Invalid pallet::call, too many weight attributes given"; - return Err(syn::Error::new(method.sig.span(), msg)) - }, - }; - - if call_idx_attrs.len() > 1 { - let msg = "Invalid pallet::call, too many call_index attributes given"; - return Err(syn::Error::new(method.sig.span(), msg)) - } - let call_index = call_idx_attrs.pop().map(|attr| match attr { - FunctionAttr::CallIndex(idx) => idx, - _ => unreachable!("checked during creation of the let binding"), - }); - let explicit_call_index = call_index.is_some(); - - let final_index = match call_index { - Some(i) => i, - None => - last_index.map_or(Some(0), |idx| idx.checked_add(1)).ok_or_else(|| { - let msg = "Call index doesn't fit into u8, index is 256"; - syn::Error::new(method.sig.span(), msg) - })?, - }; - last_index = Some(final_index); - - if let Some(used_fn) = indices.insert(final_index, method.sig.ident.clone()) { - let msg = format!( - "Call indices are conflicting: Both functions {} and {} are at index {}", - used_fn, method.sig.ident, final_index, - ); - let mut err = syn::Error::new(used_fn.span(), &msg); - err.combine(syn::Error::new(method.sig.ident.span(), msg)); - return Err(err) - } - - let mut args = vec![]; - for arg in method.sig.inputs.iter_mut().skip(1) { - let arg = if let syn::FnArg::Typed(arg) = arg { - arg - } else { - unreachable!("Only first argument can be receiver"); - }; - - let arg_attrs: Vec = - helper::take_item_pallet_attrs(&mut arg.attrs)?; - - if arg_attrs.len() > 1 { - let msg = "Invalid pallet::call, argument has too many attributes"; - return Err(syn::Error::new(arg.span(), msg)) - } - - let arg_ident = if let syn::Pat::Ident(pat) = &*arg.pat { - pat.ident.clone() - } else { - let msg = "Invalid pallet::call, argument must be ident"; - return Err(syn::Error::new(arg.pat.span(), msg)) - }; - - args.push((!arg_attrs.is_empty(), arg_ident, arg.ty.clone())); - } - - let docs = get_doc_literals(&method.attrs); - - if feeless_attrs.len() > 1 { - let msg = "Invalid pallet::call, there can only be one feeless_if attribute"; - return Err(syn::Error::new(feeless_attrs[1].0, msg)) - } - let feeless_check: Option = - feeless_attrs.pop().map(|(_, attr)| match attr { - FunctionAttr::FeelessIf(_, closure) => closure, - _ => unreachable!("checked during creation of the let binding"), - }); - - if let Some(ref feeless_check) = feeless_check { - if feeless_check.inputs.len() != args.len() + 1 { - let msg = "Invalid pallet::call, feeless_if closure must have same \ + )), + 1 => match weight_attrs.pop().unwrap() { + FunctionAttr::Weight(w) => CallWeightDef::Immediate(w), + _ => unreachable!("checked during creation of the let binding"), + }, + _ => { + let msg = "Invalid pallet::call, too many weight attributes given"; + return Err(syn::Error::new(method.sig.span(), msg)); + } + }; + + if call_idx_attrs.len() > 1 { + let msg = "Invalid pallet::call, too many call_index attributes given"; + return Err(syn::Error::new(method.sig.span(), msg)); + } + let call_index = call_idx_attrs.pop().map(|attr| match attr { + FunctionAttr::CallIndex(idx) => idx, + _ => unreachable!("checked during creation of the let binding"), + }); + let explicit_call_index = call_index.is_some(); + + let final_index = match call_index { + Some(i) => i, + None => last_index + .map_or(Some(0), |idx| idx.checked_add(1)) + .ok_or_else(|| { + let msg = "Call index doesn't fit into u8, index is 256"; + syn::Error::new(method.sig.span(), msg) + })?, + }; + last_index = Some(final_index); + + if let Some(used_fn) = indices.insert(final_index, method.sig.ident.clone()) { + let msg = format!( + "Call indices are conflicting: Both functions {} and {} are at index {}", + used_fn, method.sig.ident, final_index, + ); + let mut err = syn::Error::new(used_fn.span(), &msg); + err.combine(syn::Error::new(method.sig.ident.span(), msg)); + return Err(err); + } + + let mut args = vec![]; + for arg in method.sig.inputs.iter_mut().skip(1) { + let arg = if let syn::FnArg::Typed(arg) = arg { + arg + } else { + unreachable!("Only first argument can be receiver"); + }; + + let arg_attrs: Vec = + helper::take_item_pallet_attrs(&mut arg.attrs)?; + + if arg_attrs.len() > 1 { + let msg = "Invalid pallet::call, argument has too many attributes"; + return Err(syn::Error::new(arg.span(), msg)); + } + + let arg_ident = if let syn::Pat::Ident(pat) = &*arg.pat { + pat.ident.clone() + } else { + let msg = "Invalid pallet::call, argument must be ident"; + return Err(syn::Error::new(arg.pat.span(), msg)); + }; + + args.push((!arg_attrs.is_empty(), arg_ident, arg.ty.clone())); + } + + let docs = get_doc_literals(&method.attrs); + + if feeless_attrs.len() > 1 { + let msg = "Invalid pallet::call, there can only be one feeless_if attribute"; + return Err(syn::Error::new(feeless_attrs[1].0, msg)); + } + let feeless_check: Option = + feeless_attrs.pop().map(|(_, attr)| match attr { + FunctionAttr::FeelessIf(_, closure) => closure, + _ => unreachable!("checked during creation of the let binding"), + }); + + if let Some(ref feeless_check) = feeless_check { + if feeless_check.inputs.len() != args.len() + 1 { + let msg = "Invalid pallet::call, feeless_if closure must have same \ number of arguments as the dispatchable function"; - return Err(syn::Error::new(feeless_check.span(), msg)) - } - - match feeless_check.inputs.first() { - None => { - let msg = "Invalid pallet::call, feeless_if closure must have at least origin arg"; - return Err(syn::Error::new(feeless_check.span(), msg)) - }, - Some(syn::Pat::Type(arg)) => { - check_dispatchable_first_arg_type(&arg.ty, true)?; - }, - _ => { - let msg = "Invalid pallet::call, feeless_if closure first argument must be a typed argument, \ + return Err(syn::Error::new(feeless_check.span(), msg)); + } + + match feeless_check.inputs.first() { + None => { + let msg = "Invalid pallet::call, feeless_if closure must have at least origin arg"; + return Err(syn::Error::new(feeless_check.span(), msg)); + } + Some(syn::Pat::Type(arg)) => { + check_dispatchable_first_arg_type(&arg.ty, true)?; + } + _ => { + let msg = "Invalid pallet::call, feeless_if closure first argument must be a typed argument, \ e.g. `origin: OriginFor`"; - return Err(syn::Error::new(feeless_check.span(), msg)) - }, - } - - for (feeless_arg, arg) in feeless_check.inputs.iter().skip(1).zip(args.iter()) { - let feeless_arg_type = - if let syn::Pat::Type(syn::PatType { ty, .. }) = feeless_arg.clone() { - if let syn::Type::Reference(pat) = *ty { - pat.elem.clone() - } else { - let msg = "Invalid pallet::call, feeless_if closure argument must be a reference"; - return Err(syn::Error::new(ty.span(), msg)) - } - } else { - let msg = "Invalid pallet::call, feeless_if closure argument must be a type ascription pattern"; - return Err(syn::Error::new(feeless_arg.span(), msg)) - }; - - if feeless_arg_type != arg.2 { - let msg = - "Invalid pallet::call, feeless_if closure argument must have \ + return Err(syn::Error::new(feeless_check.span(), msg)); + } + } + + for (feeless_arg, arg) in feeless_check.inputs.iter().skip(1).zip(args.iter()) { + let feeless_arg_type = if let syn::Pat::Type(syn::PatType { ty, .. }) = + feeless_arg.clone() + { + if let syn::Type::Reference(pat) = *ty { + pat.elem.clone() + } else { + let msg = "Invalid pallet::call, feeless_if closure argument must be a reference"; + return Err(syn::Error::new(ty.span(), msg)); + } + } else { + let msg = "Invalid pallet::call, feeless_if closure argument must be a type ascription pattern"; + return Err(syn::Error::new(feeless_arg.span(), msg)); + }; + + if feeless_arg_type != arg.2 { + let msg = + "Invalid pallet::call, feeless_if closure argument must have \ a reference to the same type as the dispatchable function argument"; - return Err(syn::Error::new(feeless_arg.span(), msg)) - } - } - - let valid_return = match &feeless_check.output { - syn::ReturnType::Type(_, type_) => match *(type_.clone()) { - syn::Type::Path(syn::TypePath { path, .. }) => path.is_ident("bool"), - _ => false, - }, - _ => false, - }; - if !valid_return { - let msg = "Invalid pallet::call, feeless_if closure must return `bool`"; - return Err(syn::Error::new(feeless_check.output.span(), msg)) - } - } - - methods.push(CallVariantDef { - name: method.sig.ident.clone(), - weight, - call_index: final_index, - explicit_call_index, - args, - docs, - attrs: method.attrs.clone(), - cfg_attrs, - feeless_check, - }); - } else { - let msg = "Invalid pallet::call, only method accepted"; - return Err(syn::Error::new(item.span(), msg)) - } - } - - Ok(Self { - index, - attr_span, - instances, - methods, - where_clause: item_impl.generics.where_clause.clone(), - docs: get_doc_literals(&item_impl.attrs), - inherited_call_weight, - }) - } + return Err(syn::Error::new(feeless_arg.span(), msg)); + } + } + + let valid_return = match &feeless_check.output { + syn::ReturnType::Type(_, type_) => match *(type_.clone()) { + syn::Type::Path(syn::TypePath { path, .. }) => path.is_ident("bool"), + _ => false, + }, + _ => false, + }; + if !valid_return { + let msg = "Invalid pallet::call, feeless_if closure must return `bool`"; + return Err(syn::Error::new(feeless_check.output.span(), msg)); + } + } + + methods.push(CallVariantDef { + name: method.sig.ident.clone(), + weight, + call_index: final_index, + explicit_call_index, + args, + docs, + attrs: method.attrs.clone(), + cfg_attrs, + feeless_check, + }); + } else { + let msg = "Invalid pallet::call, only method accepted"; + return Err(syn::Error::new(item.span(), msg)); + } + } + + Ok(Self { + index, + attr_span, + instances, + methods, + where_clause: item_impl.generics.where_clause.clone(), + docs: get_doc_literals(&item_impl.attrs), + inherited_call_weight, + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/composite.rs b/support/procedural-fork/src/pallet/parse/composite.rs index c3ac74846..38da1f205 100644 --- a/support/procedural-fork/src/pallet/parse/composite.rs +++ b/support/procedural-fork/src/pallet/parse/composite.rs @@ -20,172 +20,178 @@ use quote::ToTokens; use syn::spanned::Spanned; pub mod keyword { - use super::*; - - syn::custom_keyword!(FreezeReason); - syn::custom_keyword!(HoldReason); - syn::custom_keyword!(LockId); - syn::custom_keyword!(SlashReason); - syn::custom_keyword!(Task); - - pub enum CompositeKeyword { - FreezeReason(FreezeReason), - HoldReason(HoldReason), - LockId(LockId), - SlashReason(SlashReason), - Task(Task), - } - - impl ToTokens for CompositeKeyword { - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - use CompositeKeyword::*; - match self { - FreezeReason(inner) => inner.to_tokens(tokens), - HoldReason(inner) => inner.to_tokens(tokens), - LockId(inner) => inner.to_tokens(tokens), - SlashReason(inner) => inner.to_tokens(tokens), - Task(inner) => inner.to_tokens(tokens), - } - } - } - - impl syn::parse::Parse for CompositeKeyword { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let lookahead = input.lookahead1(); - if lookahead.peek(FreezeReason) { - Ok(Self::FreezeReason(input.parse()?)) - } else if lookahead.peek(HoldReason) { - Ok(Self::HoldReason(input.parse()?)) - } else if lookahead.peek(LockId) { - Ok(Self::LockId(input.parse()?)) - } else if lookahead.peek(SlashReason) { - Ok(Self::SlashReason(input.parse()?)) - } else if lookahead.peek(Task) { - Ok(Self::Task(input.parse()?)) - } else { - Err(lookahead.error()) - } - } - } - - impl std::fmt::Display for CompositeKeyword { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use CompositeKeyword::*; - write!( - f, - "{}", - match self { - FreezeReason(_) => "FreezeReason", - HoldReason(_) => "HoldReason", - Task(_) => "Task", - LockId(_) => "LockId", - SlashReason(_) => "SlashReason", - } - ) - } - } + use super::*; + + syn::custom_keyword!(FreezeReason); + syn::custom_keyword!(HoldReason); + syn::custom_keyword!(LockId); + syn::custom_keyword!(SlashReason); + syn::custom_keyword!(Task); + + pub enum CompositeKeyword { + FreezeReason(FreezeReason), + HoldReason(HoldReason), + LockId(LockId), + SlashReason(SlashReason), + Task(Task), + } + + impl ToTokens for CompositeKeyword { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + use CompositeKeyword::*; + match self { + FreezeReason(inner) => inner.to_tokens(tokens), + HoldReason(inner) => inner.to_tokens(tokens), + LockId(inner) => inner.to_tokens(tokens), + SlashReason(inner) => inner.to_tokens(tokens), + Task(inner) => inner.to_tokens(tokens), + } + } + } + + impl syn::parse::Parse for CompositeKeyword { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let lookahead = input.lookahead1(); + if lookahead.peek(FreezeReason) { + Ok(Self::FreezeReason(input.parse()?)) + } else if lookahead.peek(HoldReason) { + Ok(Self::HoldReason(input.parse()?)) + } else if lookahead.peek(LockId) { + Ok(Self::LockId(input.parse()?)) + } else if lookahead.peek(SlashReason) { + Ok(Self::SlashReason(input.parse()?)) + } else if lookahead.peek(Task) { + Ok(Self::Task(input.parse()?)) + } else { + Err(lookahead.error()) + } + } + } + + impl std::fmt::Display for CompositeKeyword { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use CompositeKeyword::*; + write!( + f, + "{}", + match self { + FreezeReason(_) => "FreezeReason", + HoldReason(_) => "HoldReason", + Task(_) => "Task", + LockId(_) => "LockId", + SlashReason(_) => "SlashReason", + } + ) + } + } } pub struct CompositeDef { - /// The index of the CompositeDef item in the pallet module. - pub index: usize, - /// The composite keyword used (contains span). - pub composite_keyword: keyword::CompositeKeyword, - /// Name of the associated type. - pub ident: syn::Ident, - /// Type parameters and where clause attached to a declaration of the pallet::composite_enum. - pub generics: syn::Generics, - /// The span of the pallet::composite_enum attribute. - pub attr_span: proc_macro2::Span, - /// Variant count of the pallet::composite_enum. - pub variant_count: u32, + /// The index of the CompositeDef item in the pallet module. + pub index: usize, + /// The composite keyword used (contains span). + pub composite_keyword: keyword::CompositeKeyword, + /// Name of the associated type. + pub ident: syn::Ident, + /// Type parameters and where clause attached to a declaration of the pallet::composite_enum. + pub generics: syn::Generics, + /// The span of the pallet::composite_enum attribute. + pub attr_span: proc_macro2::Span, + /// Variant count of the pallet::composite_enum. + pub variant_count: u32, } impl CompositeDef { - pub fn try_from( - attr_span: proc_macro2::Span, - index: usize, - scrate: &syn::Path, - item: &mut syn::Item, - ) -> syn::Result { - let item = if let syn::Item::Enum(item) = item { - // check variants: composite enums support only field-less enum variants. This is - // because fields can introduce too many possibilities, making it challenging to compute - // a fixed variant count. - for variant in &item.variants { - match variant.fields { - syn::Fields::Named(_) | syn::Fields::Unnamed(_) => - return Err(syn::Error::new( - variant.ident.span(), - "The composite enum does not support variants with fields!", - )), - syn::Fields::Unit => (), - } - } - item - } else { - return Err(syn::Error::new( - item.span(), - "Invalid pallet::composite_enum, expected enum item", - )) - }; - - if !matches!(item.vis, syn::Visibility::Public(_)) { - let msg = format!("Invalid pallet::composite_enum, `{}` must be public", item.ident); - return Err(syn::Error::new(item.span(), msg)) - } - - let has_instance = if item.generics.params.first().is_some() { - helper::check_config_def_gen(&item.generics, item.ident.span())?; - true - } else { - false - }; - - let has_derive_attr = item.attrs.iter().any(|attr| { - if let syn::Meta::List(syn::MetaList { path, .. }) = &attr.meta { - path.get_ident().map(|ident| ident == "derive").unwrap_or(false) - } else { - false - } - }); - - if !has_derive_attr { - let derive_attr: syn::Attribute = syn::parse_quote! { - #[derive( - Copy, Clone, Eq, PartialEq, - #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, - #scrate::__private::scale_info::TypeInfo, - #scrate::__private::RuntimeDebug, - )] - }; - item.attrs.push(derive_attr); - } - - if has_instance { - item.attrs.push(syn::parse_quote! { - #[scale_info(skip_type_params(I))] - }); - - item.variants.push(syn::parse_quote! { - #[doc(hidden)] - #[codec(skip)] - __Ignore( - ::core::marker::PhantomData, - ) - }); - } - - let composite_keyword = - syn::parse2::(item.ident.to_token_stream())?; - - Ok(CompositeDef { - index, - composite_keyword, - attr_span, - generics: item.generics.clone(), - variant_count: item.variants.len() as u32, - ident: item.ident.clone(), - }) - } + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + scrate: &syn::Path, + item: &mut syn::Item, + ) -> syn::Result { + let item = if let syn::Item::Enum(item) = item { + // check variants: composite enums support only field-less enum variants. This is + // because fields can introduce too many possibilities, making it challenging to compute + // a fixed variant count. + for variant in &item.variants { + match variant.fields { + syn::Fields::Named(_) | syn::Fields::Unnamed(_) => { + return Err(syn::Error::new( + variant.ident.span(), + "The composite enum does not support variants with fields!", + )) + } + syn::Fields::Unit => (), + } + } + item + } else { + return Err(syn::Error::new( + item.span(), + "Invalid pallet::composite_enum, expected enum item", + )); + }; + + if !matches!(item.vis, syn::Visibility::Public(_)) { + let msg = format!( + "Invalid pallet::composite_enum, `{}` must be public", + item.ident + ); + return Err(syn::Error::new(item.span(), msg)); + } + + let has_instance = if item.generics.params.first().is_some() { + helper::check_config_def_gen(&item.generics, item.ident.span())?; + true + } else { + false + }; + + let has_derive_attr = item.attrs.iter().any(|attr| { + if let syn::Meta::List(syn::MetaList { path, .. }) = &attr.meta { + path.get_ident() + .map(|ident| ident == "derive") + .unwrap_or(false) + } else { + false + } + }); + + if !has_derive_attr { + let derive_attr: syn::Attribute = syn::parse_quote! { + #[derive( + Copy, Clone, Eq, PartialEq, + #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, + #scrate::__private::scale_info::TypeInfo, + #scrate::__private::RuntimeDebug, + )] + }; + item.attrs.push(derive_attr); + } + + if has_instance { + item.attrs.push(syn::parse_quote! { + #[scale_info(skip_type_params(I))] + }); + + item.variants.push(syn::parse_quote! { + #[doc(hidden)] + #[codec(skip)] + __Ignore( + ::core::marker::PhantomData, + ) + }); + } + + let composite_keyword = + syn::parse2::(item.ident.to_token_stream())?; + + Ok(CompositeDef { + index, + composite_keyword, + attr_span, + generics: item.generics.clone(), + variant_count: item.variants.len() as u32, + ident: item.ident.clone(), + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/config.rs b/support/procedural-fork/src/pallet/parse/config.rs index fbab92db1..cde565245 100644 --- a/support/procedural-fork/src/pallet/parse/config.rs +++ b/support/procedural-fork/src/pallet/parse/config.rs @@ -22,569 +22,592 @@ use syn::{spanned::Spanned, token, Token}; /// List of additional token to be used for parsing. mod keyword { - syn::custom_keyword!(Config); - syn::custom_keyword!(From); - syn::custom_keyword!(T); - syn::custom_keyword!(I); - syn::custom_keyword!(config); - syn::custom_keyword!(pallet); - syn::custom_keyword!(IsType); - syn::custom_keyword!(RuntimeEvent); - syn::custom_keyword!(Event); - syn::custom_keyword!(frame_system); - syn::custom_keyword!(disable_frame_system_supertrait_check); - syn::custom_keyword!(no_default); - syn::custom_keyword!(no_default_bounds); - syn::custom_keyword!(constant); + syn::custom_keyword!(Config); + syn::custom_keyword!(From); + syn::custom_keyword!(T); + syn::custom_keyword!(I); + syn::custom_keyword!(config); + syn::custom_keyword!(pallet); + syn::custom_keyword!(IsType); + syn::custom_keyword!(RuntimeEvent); + syn::custom_keyword!(Event); + syn::custom_keyword!(frame_system); + syn::custom_keyword!(disable_frame_system_supertrait_check); + syn::custom_keyword!(no_default); + syn::custom_keyword!(no_default_bounds); + syn::custom_keyword!(constant); } #[derive(Default)] pub struct DefaultTrait { - /// A bool for each sub-trait item indicates whether the item has - /// `#[pallet::no_default_bounds]` attached to it. If true, the item will not have any bounds - /// in the generated default sub-trait. - pub items: Vec<(syn::TraitItem, bool)>, - pub has_system: bool, + /// A bool for each sub-trait item indicates whether the item has + /// `#[pallet::no_default_bounds]` attached to it. If true, the item will not have any bounds + /// in the generated default sub-trait. + pub items: Vec<(syn::TraitItem, bool)>, + pub has_system: bool, } /// Input definition for the pallet config. pub struct ConfigDef { - /// The index of item in pallet module. - pub index: usize, - /// Whether the trait has instance (i.e. define with `Config`) - pub has_instance: bool, - /// Const associated type. - pub consts_metadata: Vec, - /// Whether the trait has the associated type `Event`, note that those bounds are - /// checked: - /// * `IsType::RuntimeEvent` - /// * `From` or `From>` or `From>` - pub has_event_type: bool, - /// The where clause on trait definition but modified so `Self` is `T`. - pub where_clause: Option, - /// The span of the pallet::config attribute. - pub attr_span: proc_macro2::Span, - /// Whether a default sub-trait should be generated. - /// - /// Contains default sub-trait items (instantiated by `#[pallet::config(with_default)]`). - /// Vec will be empty if `#[pallet::config(with_default)]` is not specified or if there are - /// no trait items. - pub default_sub_trait: Option, + /// The index of item in pallet module. + pub index: usize, + /// Whether the trait has instance (i.e. define with `Config`) + pub has_instance: bool, + /// Const associated type. + pub consts_metadata: Vec, + /// Whether the trait has the associated type `Event`, note that those bounds are + /// checked: + /// * `IsType::RuntimeEvent` + /// * `From` or `From>` or `From>` + pub has_event_type: bool, + /// The where clause on trait definition but modified so `Self` is `T`. + pub where_clause: Option, + /// The span of the pallet::config attribute. + pub attr_span: proc_macro2::Span, + /// Whether a default sub-trait should be generated. + /// + /// Contains default sub-trait items (instantiated by `#[pallet::config(with_default)]`). + /// Vec will be empty if `#[pallet::config(with_default)]` is not specified or if there are + /// no trait items. + pub default_sub_trait: Option, } /// Input definition for a constant in pallet config. pub struct ConstMetadataDef { - /// Name of the associated type. - pub ident: syn::Ident, - /// The type in Get, e.g. `u32` in `type Foo: Get;`, but `Self` is replaced by `T` - pub type_: syn::Type, - /// The doc associated - pub doc: Vec, + /// Name of the associated type. + pub ident: syn::Ident, + /// The type in Get, e.g. `u32` in `type Foo: Get;`, but `Self` is replaced by `T` + pub type_: syn::Type, + /// The doc associated + pub doc: Vec, } impl TryFrom<&syn::TraitItemType> for ConstMetadataDef { - type Error = syn::Error; - - fn try_from(trait_ty: &syn::TraitItemType) -> Result { - let err = |span, msg| { - syn::Error::new(span, format!("Invalid usage of `#[pallet::constant]`: {}", msg)) - }; - let doc = get_doc_literals(&trait_ty.attrs); - let ident = trait_ty.ident.clone(); - let bound = trait_ty - .bounds - .iter() - .find_map(|b| { - if let syn::TypeParamBound::Trait(tb) = b { - tb.path - .segments - .last() - .and_then(|s| if s.ident == "Get" { Some(s) } else { None }) - } else { - None - } - }) - .ok_or_else(|| err(trait_ty.span(), "`Get` trait bound not found"))?; - let type_arg = if let syn::PathArguments::AngleBracketed(ref ab) = bound.arguments { - if ab.args.len() == 1 { - if let syn::GenericArgument::Type(ref ty) = ab.args[0] { - Ok(ty) - } else { - Err(err(ab.args[0].span(), "Expected a type argument")) - } - } else { - Err(err(bound.span(), "Expected a single type argument")) - } - } else { - Err(err(bound.span(), "Expected trait generic args")) - }?; - let type_ = syn::parse2::(replace_self_by_t(type_arg.to_token_stream())) - .expect("Internal error: replacing `Self` by `T` should result in valid type"); - - Ok(Self { ident, type_, doc }) - } + type Error = syn::Error; + + fn try_from(trait_ty: &syn::TraitItemType) -> Result { + let err = |span, msg| { + syn::Error::new( + span, + format!("Invalid usage of `#[pallet::constant]`: {}", msg), + ) + }; + let doc = get_doc_literals(&trait_ty.attrs); + let ident = trait_ty.ident.clone(); + let bound = trait_ty + .bounds + .iter() + .find_map(|b| { + if let syn::TypeParamBound::Trait(tb) = b { + tb.path + .segments + .last() + .and_then(|s| if s.ident == "Get" { Some(s) } else { None }) + } else { + None + } + }) + .ok_or_else(|| err(trait_ty.span(), "`Get` trait bound not found"))?; + let type_arg = if let syn::PathArguments::AngleBracketed(ref ab) = bound.arguments { + if ab.args.len() == 1 { + if let syn::GenericArgument::Type(ref ty) = ab.args[0] { + Ok(ty) + } else { + Err(err(ab.args[0].span(), "Expected a type argument")) + } + } else { + Err(err(bound.span(), "Expected a single type argument")) + } + } else { + Err(err(bound.span(), "Expected trait generic args")) + }?; + let type_ = syn::parse2::(replace_self_by_t(type_arg.to_token_stream())) + .expect("Internal error: replacing `Self` by `T` should result in valid type"); + + Ok(Self { ident, type_, doc }) + } } /// Parse for `#[pallet::disable_frame_system_supertrait_check]` pub struct DisableFrameSystemSupertraitCheck; impl syn::parse::Parse for DisableFrameSystemSupertraitCheck { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - let content; - syn::bracketed!(content in input); - content.parse::()?; - content.parse::()?; - - content.parse::()?; - Ok(Self) - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + content.parse::()?; + Ok(Self) + } } /// Parsing for the `typ` portion of `PalletAttr` #[derive(derive_syn_parse::Parse, PartialEq, Eq)] pub enum PalletAttrType { - #[peek(keyword::no_default, name = "no_default")] - NoDefault(keyword::no_default), - #[peek(keyword::no_default_bounds, name = "no_default_bounds")] - NoBounds(keyword::no_default_bounds), - #[peek(keyword::constant, name = "constant")] - Constant(keyword::constant), + #[peek(keyword::no_default, name = "no_default")] + NoDefault(keyword::no_default), + #[peek(keyword::no_default_bounds, name = "no_default_bounds")] + NoBounds(keyword::no_default_bounds), + #[peek(keyword::constant, name = "constant")] + Constant(keyword::constant), } /// Parsing for `#[pallet::X]` #[derive(derive_syn_parse::Parse)] pub struct PalletAttr { - _pound: Token![#], - #[bracket] - _bracket: token::Bracket, - #[inside(_bracket)] - _pallet: keyword::pallet, - #[prefix(Token![::] in _bracket)] - #[inside(_bracket)] - typ: PalletAttrType, + _pound: Token![#], + #[bracket] + _bracket: token::Bracket, + #[inside(_bracket)] + _pallet: keyword::pallet, + #[prefix(Token![::] in _bracket)] + #[inside(_bracket)] + typ: PalletAttrType, } /// Parse for `IsType<::RuntimeEvent>` and retrieve `$path` pub struct IsTypeBoundEventParse(syn::Path); impl syn::parse::Parse for IsTypeBoundEventParse { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - let config_path = input.parse::()?; - input.parse::]>()?; - input.parse::()?; - input.parse::()?; - input.parse::]>()?; - - Ok(Self(config_path)) - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + let config_path = input.parse::()?; + input.parse::]>()?; + input.parse::()?; + input.parse::()?; + input.parse::]>()?; + + Ok(Self(config_path)) + } } /// Parse for `From` or `From>` or `From>` pub struct FromEventParse { - is_generic: bool, - has_instance: bool, + is_generic: bool, + has_instance: bool, } impl syn::parse::Parse for FromEventParse { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let mut is_generic = false; - let mut has_instance = false; - - input.parse::()?; - input.parse::()?; - input.parse::()?; - if input.peek(syn::Token![<]) { - is_generic = true; - input.parse::()?; - input.parse::()?; - if input.peek(syn::Token![,]) { - input.parse::()?; - input.parse::()?; - has_instance = true; - } - input.parse::]>()?; - } - input.parse::]>()?; - - Ok(Self { is_generic, has_instance }) - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut is_generic = false; + let mut has_instance = false; + + input.parse::()?; + input.parse::()?; + input.parse::()?; + if input.peek(syn::Token![<]) { + is_generic = true; + input.parse::()?; + input.parse::()?; + if input.peek(syn::Token![,]) { + input.parse::()?; + input.parse::()?; + has_instance = true; + } + input.parse::]>()?; + } + input.parse::]>()?; + + Ok(Self { + is_generic, + has_instance, + }) + } } /// Check if trait_item is `type RuntimeEvent`, if so checks its bounds are those expected. /// (Event type is reserved type) fn check_event_type( - frame_system: &syn::Path, - trait_item: &syn::TraitItem, - trait_has_instance: bool, + frame_system: &syn::Path, + trait_item: &syn::TraitItem, + trait_has_instance: bool, ) -> syn::Result { - if let syn::TraitItem::Type(type_) = trait_item { - if type_.ident == "RuntimeEvent" { - // Check event has no generics - if !type_.generics.params.is_empty() || type_.generics.where_clause.is_some() { - let msg = "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must have\ + if let syn::TraitItem::Type(type_) = trait_item { + if type_.ident == "RuntimeEvent" { + // Check event has no generics + if !type_.generics.params.is_empty() || type_.generics.where_clause.is_some() { + let msg = "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must have\ no generics nor where_clause"; - return Err(syn::Error::new(trait_item.span(), msg)) - } + return Err(syn::Error::new(trait_item.span(), msg)); + } - // Check bound contains IsType and From - let has_is_type_bound = type_.bounds.iter().any(|s| { - syn::parse2::(s.to_token_stream()) - .map_or(false, |b| has_expected_system_config(b.0, frame_system)) - }); + // Check bound contains IsType and From + let has_is_type_bound = type_.bounds.iter().any(|s| { + syn::parse2::(s.to_token_stream()) + .map_or(false, |b| has_expected_system_config(b.0, frame_system)) + }); - if !has_is_type_bound { - let msg = "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must \ + if !has_is_type_bound { + let msg = "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must \ bound: `IsType<::RuntimeEvent>`".to_string(); - return Err(syn::Error::new(type_.span(), msg)) - } - - let from_event_bound = type_ - .bounds - .iter() - .find_map(|s| syn::parse2::(s.to_token_stream()).ok()); - - let from_event_bound = if let Some(b) = from_event_bound { - b - } else { - let msg = "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must \ + return Err(syn::Error::new(type_.span(), msg)); + } + + let from_event_bound = type_ + .bounds + .iter() + .find_map(|s| syn::parse2::(s.to_token_stream()).ok()); + + let from_event_bound = if let Some(b) = from_event_bound { + b + } else { + let msg = "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must \ bound: `From` or `From>` or `From>`"; - return Err(syn::Error::new(type_.span(), msg)) - }; + return Err(syn::Error::new(type_.span(), msg)); + }; - if from_event_bound.is_generic && (from_event_bound.has_instance != trait_has_instance) - { - let msg = "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` bounds inconsistent \ + if from_event_bound.is_generic && (from_event_bound.has_instance != trait_has_instance) + { + let msg = "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` bounds inconsistent \ `From`. Config and generic Event must be both with instance or \ without instance"; - return Err(syn::Error::new(type_.span(), msg)) - } - - Ok(true) - } else { - Ok(false) - } - } else { - Ok(false) - } + return Err(syn::Error::new(type_.span(), msg)); + } + + Ok(true) + } else { + Ok(false) + } + } else { + Ok(false) + } } /// Check that the path to `frame_system::Config` is valid, this is that the path is just /// `frame_system::Config` or when using the `frame` crate it is `frame::xyz::frame_system::Config`. fn has_expected_system_config(path: syn::Path, frame_system: &syn::Path) -> bool { - // Check if `frame_system` is actually 'frame_system'. - if path.segments.iter().all(|s| s.ident != "frame_system") { - return false - } - - let mut expected_system_config = - match (is_using_frame_crate(&path), is_using_frame_crate(&frame_system)) { - (true, false) => - // We can't use the path to `frame_system` from `frame` if `frame_system` is not being - // in scope through `frame`. - return false, - (false, true) => - // We know that the only valid frame_system path is one that is `frame_system`, as - // `frame` re-exports it as such. - syn::parse2::(quote::quote!(frame_system)).expect("is a valid path; qed"), - (_, _) => - // They are either both `frame_system` or both `frame::xyz::frame_system`. - frame_system.clone(), - }; - - expected_system_config - .segments - .push(syn::PathSegment::from(syn::Ident::new("Config", path.span()))); - - // the parse path might be something like `frame_system::Config<...>`, so we - // only compare the idents along the path. - expected_system_config - .segments - .into_iter() - .map(|ps| ps.ident) - .collect::>() == - path.segments.into_iter().map(|ps| ps.ident).collect::>() + // Check if `frame_system` is actually 'frame_system'. + if path.segments.iter().all(|s| s.ident != "frame_system") { + return false; + } + + let mut expected_system_config = match ( + is_using_frame_crate(&path), + is_using_frame_crate(&frame_system), + ) { + (true, false) => + // We can't use the path to `frame_system` from `frame` if `frame_system` is not being + // in scope through `frame`. + { + return false + } + (false, true) => + // We know that the only valid frame_system path is one that is `frame_system`, as + // `frame` re-exports it as such. + { + syn::parse2::(quote::quote!(frame_system)).expect("is a valid path; qed") + } + (_, _) => + // They are either both `frame_system` or both `frame::xyz::frame_system`. + { + frame_system.clone() + } + }; + + expected_system_config + .segments + .push(syn::PathSegment::from(syn::Ident::new( + "Config", + path.span(), + ))); + + // the parse path might be something like `frame_system::Config<...>`, so we + // only compare the idents along the path. + expected_system_config + .segments + .into_iter() + .map(|ps| ps.ident) + .collect::>() + == path + .segments + .into_iter() + .map(|ps| ps.ident) + .collect::>() } /// Replace ident `Self` by `T` pub fn replace_self_by_t(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream { - input - .into_iter() - .map(|token_tree| match token_tree { - proc_macro2::TokenTree::Group(group) => - proc_macro2::Group::new(group.delimiter(), replace_self_by_t(group.stream())).into(), - proc_macro2::TokenTree::Ident(ident) if ident == "Self" => - proc_macro2::Ident::new("T", ident.span()).into(), - other => other, - }) - .collect() + input + .into_iter() + .map(|token_tree| match token_tree { + proc_macro2::TokenTree::Group(group) => { + proc_macro2::Group::new(group.delimiter(), replace_self_by_t(group.stream())).into() + } + proc_macro2::TokenTree::Ident(ident) if ident == "Self" => { + proc_macro2::Ident::new("T", ident.span()).into() + } + other => other, + }) + .collect() } impl ConfigDef { - pub fn try_from( - frame_system: &syn::Path, - attr_span: proc_macro2::Span, - index: usize, - item: &mut syn::Item, - enable_default: bool, - ) -> syn::Result { - let item = if let syn::Item::Trait(item) = item { - item - } else { - let msg = "Invalid pallet::config, expected trait definition"; - return Err(syn::Error::new(item.span(), msg)) - }; - - if !matches!(item.vis, syn::Visibility::Public(_)) { - let msg = "Invalid pallet::config, trait must be public"; - return Err(syn::Error::new(item.span(), msg)) - } - - syn::parse2::(item.ident.to_token_stream())?; - - let where_clause = { - let stream = replace_self_by_t(item.generics.where_clause.to_token_stream()); - syn::parse2::>(stream).expect( - "Internal error: replacing `Self` by `T` should result in valid where + pub fn try_from( + frame_system: &syn::Path, + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + enable_default: bool, + ) -> syn::Result { + let item = if let syn::Item::Trait(item) = item { + item + } else { + let msg = "Invalid pallet::config, expected trait definition"; + return Err(syn::Error::new(item.span(), msg)); + }; + + if !matches!(item.vis, syn::Visibility::Public(_)) { + let msg = "Invalid pallet::config, trait must be public"; + return Err(syn::Error::new(item.span(), msg)); + } + + syn::parse2::(item.ident.to_token_stream())?; + + let where_clause = { + let stream = replace_self_by_t(item.generics.where_clause.to_token_stream()); + syn::parse2::>(stream).expect( + "Internal error: replacing `Self` by `T` should result in valid where clause", - ) - }; - - if item.generics.params.len() > 1 { - let msg = "Invalid pallet::config, expected no more than one generic"; - return Err(syn::Error::new(item.generics.params[2].span(), msg)) - } - - let has_instance = if item.generics.params.first().is_some() { - helper::check_config_def_gen(&item.generics, item.ident.span())?; - true - } else { - false - }; - - let has_frame_system_supertrait = item.supertraits.iter().any(|s| { - syn::parse2::(s.to_token_stream()) - .map_or(false, |b| has_expected_system_config(b, frame_system)) - }); - - let mut has_event_type = false; - let mut consts_metadata = vec![]; - let mut default_sub_trait = if enable_default { - Some(DefaultTrait { - items: Default::default(), - has_system: has_frame_system_supertrait, - }) - } else { - None - }; - for trait_item in &mut item.items { - let is_event = check_event_type(frame_system, trait_item, has_instance)?; - has_event_type = has_event_type || is_event; - - let mut already_no_default = false; - let mut already_constant = false; - let mut already_no_default_bounds = false; - - while let Ok(Some(pallet_attr)) = - helper::take_first_item_pallet_attr::(trait_item) - { - match (pallet_attr.typ, &trait_item) { - (PalletAttrType::Constant(_), syn::TraitItem::Type(ref typ)) => { - if already_constant { - return Err(syn::Error::new( - pallet_attr._bracket.span.join(), - "Duplicate #[pallet::constant] attribute not allowed.", - )) - } - already_constant = true; - consts_metadata.push(ConstMetadataDef::try_from(typ)?); - }, - (PalletAttrType::Constant(_), _) => - return Err(syn::Error::new( - trait_item.span(), - "Invalid #[pallet::constant] in #[pallet::config], expected type item", - )), - (PalletAttrType::NoDefault(_), _) => { - if !enable_default { - return Err(syn::Error::new( + ) + }; + + if item.generics.params.len() > 1 { + let msg = "Invalid pallet::config, expected no more than one generic"; + return Err(syn::Error::new(item.generics.params[2].span(), msg)); + } + + let has_instance = if item.generics.params.first().is_some() { + helper::check_config_def_gen(&item.generics, item.ident.span())?; + true + } else { + false + }; + + let has_frame_system_supertrait = item.supertraits.iter().any(|s| { + syn::parse2::(s.to_token_stream()) + .map_or(false, |b| has_expected_system_config(b, frame_system)) + }); + + let mut has_event_type = false; + let mut consts_metadata = vec![]; + let mut default_sub_trait = if enable_default { + Some(DefaultTrait { + items: Default::default(), + has_system: has_frame_system_supertrait, + }) + } else { + None + }; + for trait_item in &mut item.items { + let is_event = check_event_type(frame_system, trait_item, has_instance)?; + has_event_type = has_event_type || is_event; + + let mut already_no_default = false; + let mut already_constant = false; + let mut already_no_default_bounds = false; + + while let Ok(Some(pallet_attr)) = + helper::take_first_item_pallet_attr::(trait_item) + { + match (pallet_attr.typ, &trait_item) { + (PalletAttrType::Constant(_), syn::TraitItem::Type(ref typ)) => { + if already_constant { + return Err(syn::Error::new( + pallet_attr._bracket.span.join(), + "Duplicate #[pallet::constant] attribute not allowed.", + )); + } + already_constant = true; + consts_metadata.push(ConstMetadataDef::try_from(typ)?); + } + (PalletAttrType::Constant(_), _) => { + return Err(syn::Error::new( + trait_item.span(), + "Invalid #[pallet::constant] in #[pallet::config], expected type item", + )) + } + (PalletAttrType::NoDefault(_), _) => { + if !enable_default { + return Err(syn::Error::new( pallet_attr._bracket.span.join(), "`#[pallet:no_default]` can only be used if `#[pallet::config(with_default)]` \ has been specified" - )) - } - if already_no_default { - return Err(syn::Error::new( - pallet_attr._bracket.span.join(), - "Duplicate #[pallet::no_default] attribute not allowed.", - )) - } - - already_no_default = true; - }, - (PalletAttrType::NoBounds(_), _) => { - if !enable_default { - return Err(syn::Error::new( + )); + } + if already_no_default { + return Err(syn::Error::new( + pallet_attr._bracket.span.join(), + "Duplicate #[pallet::no_default] attribute not allowed.", + )); + } + + already_no_default = true; + } + (PalletAttrType::NoBounds(_), _) => { + if !enable_default { + return Err(syn::Error::new( pallet_attr._bracket.span.join(), "`#[pallet:no_default_bounds]` can only be used if `#[pallet::config(with_default)]` \ has been specified" - )) - } - if already_no_default_bounds { - return Err(syn::Error::new( - pallet_attr._bracket.span.join(), - "Duplicate #[pallet::no_default_bounds] attribute not allowed.", - )) - } - already_no_default_bounds = true; - }, - } - } - - if !already_no_default && enable_default { - default_sub_trait - .as_mut() - .expect("is 'Some(_)' if 'enable_default'; qed") - .items - .push((trait_item.clone(), already_no_default_bounds)); - } - } - - let attr: Option = - helper::take_first_item_pallet_attr(&mut item.attrs)?; - let disable_system_supertrait_check = attr.is_some(); - - if !has_frame_system_supertrait && !disable_system_supertrait_check { - let found = if item.supertraits.is_empty() { - "none".to_string() - } else { - let mut found = item - .supertraits - .iter() - .fold(String::new(), |acc, s| format!("{}`{}`, ", acc, quote::quote!(#s))); - found.pop(); - found.pop(); - found - }; - - let msg = format!( - "Invalid pallet::trait, expected explicit `{}::Config` as supertrait, \ + )); + } + if already_no_default_bounds { + return Err(syn::Error::new( + pallet_attr._bracket.span.join(), + "Duplicate #[pallet::no_default_bounds] attribute not allowed.", + )); + } + already_no_default_bounds = true; + } + } + } + + if !already_no_default && enable_default { + default_sub_trait + .as_mut() + .expect("is 'Some(_)' if 'enable_default'; qed") + .items + .push((trait_item.clone(), already_no_default_bounds)); + } + } + + let attr: Option = + helper::take_first_item_pallet_attr(&mut item.attrs)?; + let disable_system_supertrait_check = attr.is_some(); + + if !has_frame_system_supertrait && !disable_system_supertrait_check { + let found = if item.supertraits.is_empty() { + "none".to_string() + } else { + let mut found = item.supertraits.iter().fold(String::new(), |acc, s| { + format!("{}`{}`, ", acc, quote::quote!(#s)) + }); + found.pop(); + found.pop(); + found + }; + + let msg = format!( + "Invalid pallet::trait, expected explicit `{}::Config` as supertrait, \ found {}. \ (try `pub trait Config: frame_system::Config {{ ...` or \ `pub trait Config: frame_system::Config {{ ...`). \ To disable this check, use `#[pallet::disable_frame_system_supertrait_check]`", - frame_system.to_token_stream(), - found, - ); - return Err(syn::Error::new(item.span(), msg)) - } - - Ok(Self { - index, - has_instance, - consts_metadata, - has_event_type, - where_clause, - attr_span, - default_sub_trait, - }) - } + frame_system.to_token_stream(), + found, + ); + return Err(syn::Error::new(item.span(), msg)); + } + + Ok(Self { + index, + has_instance, + consts_metadata, + has_event_type, + where_clause, + attr_span, + default_sub_trait, + }) + } } #[cfg(test)] mod tests { - use super::*; - #[test] - fn has_expected_system_config_works() { - let frame_system = syn::parse2::(quote::quote!(frame_system)).unwrap(); - let path = syn::parse2::(quote::quote!(frame_system::Config)).unwrap(); - assert!(has_expected_system_config(path, &frame_system)); - } - - #[test] - fn has_expected_system_config_works_with_assoc_type() { - let frame_system = syn::parse2::(quote::quote!(frame_system)).unwrap(); - let path = - syn::parse2::(quote::quote!(frame_system::Config)) - .unwrap(); - assert!(has_expected_system_config(path, &frame_system)); - } - - #[test] - fn has_expected_system_config_works_with_frame() { - let frame_system = - syn::parse2::(quote::quote!(frame::deps::frame_system)).unwrap(); - let path = syn::parse2::(quote::quote!(frame_system::Config)).unwrap(); - assert!(has_expected_system_config(path, &frame_system)); - } - - #[test] - fn has_expected_system_config_works_with_frame_full_path() { - let frame_system = - syn::parse2::(quote::quote!(frame::deps::frame_system)).unwrap(); - let path = - syn::parse2::(quote::quote!(frame::deps::frame_system::Config)).unwrap(); - assert!(has_expected_system_config(path, &frame_system)); - } - - #[test] - fn has_expected_system_config_works_with_other_frame_full_path() { - let frame_system = - syn::parse2::(quote::quote!(frame::xyz::frame_system)).unwrap(); - let path = - syn::parse2::(quote::quote!(frame::xyz::frame_system::Config)).unwrap(); - assert!(has_expected_system_config(path, &frame_system)); - } - - #[test] - fn has_expected_system_config_does_not_works_with_mixed_frame_full_path() { - let frame_system = - syn::parse2::(quote::quote!(frame::xyz::frame_system)).unwrap(); - let path = - syn::parse2::(quote::quote!(frame::deps::frame_system::Config)).unwrap(); - assert!(!has_expected_system_config(path, &frame_system)); - } - - #[test] - fn has_expected_system_config_does_not_works_with_other_mixed_frame_full_path() { - let frame_system = - syn::parse2::(quote::quote!(frame::deps::frame_system)).unwrap(); - let path = - syn::parse2::(quote::quote!(frame::xyz::frame_system::Config)).unwrap(); - assert!(!has_expected_system_config(path, &frame_system)); - } - - #[test] - fn has_expected_system_config_does_not_work_with_frame_full_path_if_not_frame_crate() { - let frame_system = syn::parse2::(quote::quote!(frame_system)).unwrap(); - let path = - syn::parse2::(quote::quote!(frame::deps::frame_system::Config)).unwrap(); - assert!(!has_expected_system_config(path, &frame_system)); - } - - #[test] - fn has_expected_system_config_unexpected_frame_system() { - let frame_system = - syn::parse2::(quote::quote!(framez::deps::frame_system)).unwrap(); - let path = syn::parse2::(quote::quote!(frame_system::Config)).unwrap(); - assert!(!has_expected_system_config(path, &frame_system)); - } - - #[test] - fn has_expected_system_config_unexpected_path() { - let frame_system = syn::parse2::(quote::quote!(frame_system)).unwrap(); - let path = syn::parse2::(quote::quote!(frame_system::ConfigSystem)).unwrap(); - assert!(!has_expected_system_config(path, &frame_system)); - } - - #[test] - fn has_expected_system_config_not_frame_system() { - let frame_system = syn::parse2::(quote::quote!(something)).unwrap(); - let path = syn::parse2::(quote::quote!(something::Config)).unwrap(); - assert!(!has_expected_system_config(path, &frame_system)); - } + use super::*; + #[test] + fn has_expected_system_config_works() { + let frame_system = syn::parse2::(quote::quote!(frame_system)).unwrap(); + let path = syn::parse2::(quote::quote!(frame_system::Config)).unwrap(); + assert!(has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_works_with_assoc_type() { + let frame_system = syn::parse2::(quote::quote!(frame_system)).unwrap(); + let path = + syn::parse2::(quote::quote!(frame_system::Config)) + .unwrap(); + assert!(has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_works_with_frame() { + let frame_system = + syn::parse2::(quote::quote!(frame::deps::frame_system)).unwrap(); + let path = syn::parse2::(quote::quote!(frame_system::Config)).unwrap(); + assert!(has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_works_with_frame_full_path() { + let frame_system = + syn::parse2::(quote::quote!(frame::deps::frame_system)).unwrap(); + let path = + syn::parse2::(quote::quote!(frame::deps::frame_system::Config)).unwrap(); + assert!(has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_works_with_other_frame_full_path() { + let frame_system = + syn::parse2::(quote::quote!(frame::xyz::frame_system)).unwrap(); + let path = + syn::parse2::(quote::quote!(frame::xyz::frame_system::Config)).unwrap(); + assert!(has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_does_not_works_with_mixed_frame_full_path() { + let frame_system = + syn::parse2::(quote::quote!(frame::xyz::frame_system)).unwrap(); + let path = + syn::parse2::(quote::quote!(frame::deps::frame_system::Config)).unwrap(); + assert!(!has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_does_not_works_with_other_mixed_frame_full_path() { + let frame_system = + syn::parse2::(quote::quote!(frame::deps::frame_system)).unwrap(); + let path = + syn::parse2::(quote::quote!(frame::xyz::frame_system::Config)).unwrap(); + assert!(!has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_does_not_work_with_frame_full_path_if_not_frame_crate() { + let frame_system = syn::parse2::(quote::quote!(frame_system)).unwrap(); + let path = + syn::parse2::(quote::quote!(frame::deps::frame_system::Config)).unwrap(); + assert!(!has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_unexpected_frame_system() { + let frame_system = + syn::parse2::(quote::quote!(framez::deps::frame_system)).unwrap(); + let path = syn::parse2::(quote::quote!(frame_system::Config)).unwrap(); + assert!(!has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_unexpected_path() { + let frame_system = syn::parse2::(quote::quote!(frame_system)).unwrap(); + let path = syn::parse2::(quote::quote!(frame_system::ConfigSystem)).unwrap(); + assert!(!has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_not_frame_system() { + let frame_system = syn::parse2::(quote::quote!(something)).unwrap(); + let path = syn::parse2::(quote::quote!(something::Config)).unwrap(); + assert!(!has_expected_system_config(path, &frame_system)); + } } diff --git a/support/procedural-fork/src/pallet/parse/error.rs b/support/procedural-fork/src/pallet/parse/error.rs index 362df8d73..e93e2113f 100644 --- a/support/procedural-fork/src/pallet/parse/error.rs +++ b/support/procedural-fork/src/pallet/parse/error.rs @@ -22,94 +22,108 @@ use syn::{spanned::Spanned, Fields}; /// List of additional token to be used for parsing. mod keyword { - syn::custom_keyword!(Error); + syn::custom_keyword!(Error); } /// Records information about the error enum variant field. pub struct VariantField { - /// Whether or not the field is named, i.e. whether it is a tuple variant or struct variant. - pub is_named: bool, + /// Whether or not the field is named, i.e. whether it is a tuple variant or struct variant. + pub is_named: bool, } /// Records information about the error enum variants. pub struct VariantDef { - /// The variant ident. - pub ident: syn::Ident, - /// The variant field, if any. - pub field: Option, - /// The variant doc literals. - pub docs: Vec, - /// The `cfg` attributes. - pub cfg_attrs: Vec, + /// The variant ident. + pub ident: syn::Ident, + /// The variant field, if any. + pub field: Option, + /// The variant doc literals. + pub docs: Vec, + /// The `cfg` attributes. + pub cfg_attrs: Vec, } /// This checks error declaration as a enum declaration with only variants without fields nor /// discriminant. pub struct ErrorDef { - /// The index of error item in pallet module. - pub index: usize, - /// Variant definitions. - pub variants: Vec, - /// A set of usage of instance, must be check for consistency with trait. - pub instances: Vec, - /// The keyword error used (contains span). - pub error: keyword::Error, - /// The span of the pallet::error attribute. - pub attr_span: proc_macro2::Span, + /// The index of error item in pallet module. + pub index: usize, + /// Variant definitions. + pub variants: Vec, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Vec, + /// The keyword error used (contains span). + pub error: keyword::Error, + /// The span of the pallet::error attribute. + pub attr_span: proc_macro2::Span, } impl ErrorDef { - pub fn try_from( - attr_span: proc_macro2::Span, - index: usize, - item: &mut syn::Item, - ) -> syn::Result { - let item = if let syn::Item::Enum(item) = item { - item - } else { - return Err(syn::Error::new(item.span(), "Invalid pallet::error, expected item enum")) - }; - if !matches!(item.vis, syn::Visibility::Public(_)) { - let msg = "Invalid pallet::error, `Error` must be public"; - return Err(syn::Error::new(item.span(), msg)) - } + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + ) -> syn::Result { + let item = if let syn::Item::Enum(item) = item { + item + } else { + return Err(syn::Error::new( + item.span(), + "Invalid pallet::error, expected item enum", + )); + }; + if !matches!(item.vis, syn::Visibility::Public(_)) { + let msg = "Invalid pallet::error, `Error` must be public"; + return Err(syn::Error::new(item.span(), msg)); + } - let instances = - vec![helper::check_type_def_gen_no_bounds(&item.generics, item.ident.span())?]; + let instances = vec![helper::check_type_def_gen_no_bounds( + &item.generics, + item.ident.span(), + )?]; - if item.generics.where_clause.is_some() { - let msg = "Invalid pallet::error, where clause is not allowed on pallet error item"; - return Err(syn::Error::new(item.generics.where_clause.as_ref().unwrap().span(), msg)) - } + if item.generics.where_clause.is_some() { + let msg = "Invalid pallet::error, where clause is not allowed on pallet error item"; + return Err(syn::Error::new( + item.generics.where_clause.as_ref().unwrap().span(), + msg, + )); + } - let error = syn::parse2::(item.ident.to_token_stream())?; + let error = syn::parse2::(item.ident.to_token_stream())?; - let variants = item - .variants - .iter() - .map(|variant| { - let field_ty = match &variant.fields { - Fields::Unit => None, - Fields::Named(_) => Some(VariantField { is_named: true }), - Fields::Unnamed(_) => Some(VariantField { is_named: false }), - }; - if variant.discriminant.is_some() { - let msg = "Invalid pallet::error, unexpected discriminant, discriminants \ + let variants = item + .variants + .iter() + .map(|variant| { + let field_ty = match &variant.fields { + Fields::Unit => None, + Fields::Named(_) => Some(VariantField { is_named: true }), + Fields::Unnamed(_) => Some(VariantField { is_named: false }), + }; + if variant.discriminant.is_some() { + let msg = "Invalid pallet::error, unexpected discriminant, discriminants \ are not supported"; - let span = variant.discriminant.as_ref().unwrap().0.span(); - return Err(syn::Error::new(span, msg)) - } - let cfg_attrs: Vec = helper::get_item_cfg_attrs(&variant.attrs); + let span = variant.discriminant.as_ref().unwrap().0.span(); + return Err(syn::Error::new(span, msg)); + } + let cfg_attrs: Vec = helper::get_item_cfg_attrs(&variant.attrs); - Ok(VariantDef { - ident: variant.ident.clone(), - field: field_ty, - docs: get_doc_literals(&variant.attrs), - cfg_attrs, - }) - }) - .collect::>()?; + Ok(VariantDef { + ident: variant.ident.clone(), + field: field_ty, + docs: get_doc_literals(&variant.attrs), + cfg_attrs, + }) + }) + .collect::>()?; - Ok(ErrorDef { attr_span, index, variants, instances, error }) - } + Ok(ErrorDef { + attr_span, + index, + variants, + instances, + error, + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/event.rs b/support/procedural-fork/src/pallet/parse/event.rs index 0fb8ee4f5..6102dd31f 100644 --- a/support/procedural-fork/src/pallet/parse/event.rs +++ b/support/procedural-fork/src/pallet/parse/event.rs @@ -21,28 +21,28 @@ use syn::spanned::Spanned; /// List of additional token to be used for parsing. mod keyword { - syn::custom_keyword!(Event); - syn::custom_keyword!(pallet); - syn::custom_keyword!(generate_deposit); - syn::custom_keyword!(deposit_event); + syn::custom_keyword!(Event); + syn::custom_keyword!(pallet); + syn::custom_keyword!(generate_deposit); + syn::custom_keyword!(deposit_event); } /// Definition for pallet event enum. pub struct EventDef { - /// The index of event item in pallet module. - pub index: usize, - /// The keyword Event used (contains span). - pub event: keyword::Event, - /// A set of usage of instance, must be check for consistency with trait. - pub instances: Vec, - /// The kind of generic the type `Event` has. - pub gen_kind: super::GenericKind, - /// Whether the function `deposit_event` must be generated. - pub deposit_event: Option, - /// Where clause used in event definition. - pub where_clause: Option, - /// The span of the pallet::event attribute. - pub attr_span: proc_macro2::Span, + /// The index of event item in pallet module. + pub index: usize, + /// The keyword Event used (contains span). + pub event: keyword::Event, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Vec, + /// The kind of generic the type `Event` has. + pub gen_kind: super::GenericKind, + /// Whether the function `deposit_event` must be generated. + pub deposit_event: Option, + /// Where clause used in event definition. + pub where_clause: Option, + /// The span of the pallet::event attribute. + pub attr_span: proc_macro2::Span, } /// Attribute for a pallet's Event. @@ -50,92 +50,110 @@ pub struct EventDef { /// Syntax is: /// * `#[pallet::generate_deposit($vis fn deposit_event)]` pub struct PalletEventDepositAttr { - pub fn_vis: syn::Visibility, - // Span for the keyword deposit_event - pub fn_span: proc_macro2::Span, - // Span of the attribute - pub span: proc_macro2::Span, + pub fn_vis: syn::Visibility, + // Span for the keyword deposit_event + pub fn_span: proc_macro2::Span, + // Span of the attribute + pub span: proc_macro2::Span, } impl syn::parse::Parse for PalletEventDepositAttr { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - let content; - syn::bracketed!(content in input); - content.parse::()?; - content.parse::()?; - - let span = content.parse::()?.span(); - let generate_content; - syn::parenthesized!(generate_content in content); - let fn_vis = generate_content.parse::()?; - generate_content.parse::()?; - let fn_span = generate_content.parse::()?.span(); - - Ok(PalletEventDepositAttr { fn_vis, span, fn_span }) - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + let span = content.parse::()?.span(); + let generate_content; + syn::parenthesized!(generate_content in content); + let fn_vis = generate_content.parse::()?; + generate_content.parse::()?; + let fn_span = generate_content.parse::()?.span(); + + Ok(PalletEventDepositAttr { + fn_vis, + span, + fn_span, + }) + } } struct PalletEventAttrInfo { - deposit_event: Option, + deposit_event: Option, } impl PalletEventAttrInfo { - fn from_attrs(attrs: Vec) -> syn::Result { - let mut deposit_event = None; - for attr in attrs { - if deposit_event.is_none() { - deposit_event = Some(attr) - } else { - return Err(syn::Error::new(attr.span, "Duplicate attribute")) - } - } - - Ok(PalletEventAttrInfo { deposit_event }) - } + fn from_attrs(attrs: Vec) -> syn::Result { + let mut deposit_event = None; + for attr in attrs { + if deposit_event.is_none() { + deposit_event = Some(attr) + } else { + return Err(syn::Error::new(attr.span, "Duplicate attribute")); + } + } + + Ok(PalletEventAttrInfo { deposit_event }) + } } impl EventDef { - pub fn try_from( - attr_span: proc_macro2::Span, - index: usize, - item: &mut syn::Item, - ) -> syn::Result { - let item = if let syn::Item::Enum(item) = item { - item - } else { - return Err(syn::Error::new(item.span(), "Invalid pallet::event, expected enum item")) - }; - - let event_attrs: Vec = - helper::take_item_pallet_attrs(&mut item.attrs)?; - let attr_info = PalletEventAttrInfo::from_attrs(event_attrs)?; - let deposit_event = attr_info.deposit_event; - - if !matches!(item.vis, syn::Visibility::Public(_)) { - let msg = "Invalid pallet::event, `Event` must be public"; - return Err(syn::Error::new(item.span(), msg)) - } - - let where_clause = item.generics.where_clause.clone(); - - let mut instances = vec![]; - // NOTE: Event is not allowed to be only generic on I because it is not supported - // by construct_runtime. - if let Some(u) = helper::check_type_def_optional_gen(&item.generics, item.ident.span())? { - instances.push(u); - } else { - // construct_runtime only allow non generic event for non instantiable pallet. - instances.push(helper::InstanceUsage { has_instance: false, span: item.ident.span() }) - } - - let has_instance = item.generics.type_params().any(|t| t.ident == "I"); - let has_config = item.generics.type_params().any(|t| t.ident == "T"); - let gen_kind = super::GenericKind::from_gens(has_config, has_instance) - .expect("Checked by `helper::check_type_def_optional_gen` above"); - - let event = syn::parse2::(item.ident.to_token_stream())?; - - Ok(EventDef { attr_span, index, instances, deposit_event, event, gen_kind, where_clause }) - } + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + ) -> syn::Result { + let item = if let syn::Item::Enum(item) = item { + item + } else { + return Err(syn::Error::new( + item.span(), + "Invalid pallet::event, expected enum item", + )); + }; + + let event_attrs: Vec = + helper::take_item_pallet_attrs(&mut item.attrs)?; + let attr_info = PalletEventAttrInfo::from_attrs(event_attrs)?; + let deposit_event = attr_info.deposit_event; + + if !matches!(item.vis, syn::Visibility::Public(_)) { + let msg = "Invalid pallet::event, `Event` must be public"; + return Err(syn::Error::new(item.span(), msg)); + } + + let where_clause = item.generics.where_clause.clone(); + + let mut instances = vec![]; + // NOTE: Event is not allowed to be only generic on I because it is not supported + // by construct_runtime. + if let Some(u) = helper::check_type_def_optional_gen(&item.generics, item.ident.span())? { + instances.push(u); + } else { + // construct_runtime only allow non generic event for non instantiable pallet. + instances.push(helper::InstanceUsage { + has_instance: false, + span: item.ident.span(), + }) + } + + let has_instance = item.generics.type_params().any(|t| t.ident == "I"); + let has_config = item.generics.type_params().any(|t| t.ident == "T"); + let gen_kind = super::GenericKind::from_gens(has_config, has_instance) + .expect("Checked by `helper::check_type_def_optional_gen` above"); + + let event = syn::parse2::(item.ident.to_token_stream())?; + + Ok(EventDef { + attr_span, + index, + instances, + deposit_event, + event, + gen_kind, + where_clause, + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/extra_constants.rs b/support/procedural-fork/src/pallet/parse/extra_constants.rs index 2ba6c44b7..38acea21a 100644 --- a/support/procedural-fork/src/pallet/parse/extra_constants.rs +++ b/support/procedural-fork/src/pallet/parse/extra_constants.rs @@ -21,140 +21,148 @@ use syn::spanned::Spanned; /// List of additional token to be used for parsing. mod keyword { - syn::custom_keyword!(DispatchResultWithPostInfo); - syn::custom_keyword!(Call); - syn::custom_keyword!(OriginFor); - syn::custom_keyword!(weight); - syn::custom_keyword!(compact); - syn::custom_keyword!(T); - syn::custom_keyword!(pallet); - syn::custom_keyword!(constant_name); + syn::custom_keyword!(DispatchResultWithPostInfo); + syn::custom_keyword!(Call); + syn::custom_keyword!(OriginFor); + syn::custom_keyword!(weight); + syn::custom_keyword!(compact); + syn::custom_keyword!(T); + syn::custom_keyword!(pallet); + syn::custom_keyword!(constant_name); } /// Definition of extra constants typically `impl Pallet { ... }` pub struct ExtraConstantsDef { - /// The where_clause used. - pub where_clause: Option, - /// A set of usage of instance, must be check for consistency with trait. - pub instances: Vec, - /// The index of call item in pallet module. - pub index: usize, - /// The extra constant defined. - pub extra_constants: Vec, + /// The where_clause used. + pub where_clause: Option, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Vec, + /// The index of call item in pallet module. + pub index: usize, + /// The extra constant defined. + pub extra_constants: Vec, } /// Input definition for an constant in pallet. pub struct ExtraConstantDef { - /// Name of the function - pub ident: syn::Ident, - /// The type returned by the function - pub type_: syn::Type, - /// The doc associated - pub doc: Vec, - /// Optional MetaData Name - pub metadata_name: Option, + /// Name of the function + pub ident: syn::Ident, + /// The type returned by the function + pub type_: syn::Type, + /// The doc associated + pub doc: Vec, + /// Optional MetaData Name + pub metadata_name: Option, } /// Attributes for functions in extra_constants impl block. /// Parse for `#[pallet::constant_name(ConstantName)]` pub struct ExtraConstAttr { - metadata_name: syn::Ident, + metadata_name: syn::Ident, } impl syn::parse::Parse for ExtraConstAttr { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - let content; - syn::bracketed!(content in input); - content.parse::()?; - content.parse::()?; - content.parse::()?; - - let metadata_name; - syn::parenthesized!(metadata_name in content); - Ok(ExtraConstAttr { metadata_name: metadata_name.parse::()? }) - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + content.parse::()?; + + let metadata_name; + syn::parenthesized!(metadata_name in content); + Ok(ExtraConstAttr { + metadata_name: metadata_name.parse::()?, + }) + } } impl ExtraConstantsDef { - pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { - let item = if let syn::Item::Impl(item) = item { - item - } else { - return Err(syn::Error::new( - item.span(), - "Invalid pallet::extra_constants, expected item impl", - )) - }; - - let instances = vec![ - helper::check_impl_gen(&item.generics, item.impl_token.span())?, - helper::check_pallet_struct_usage(&item.self_ty)?, - ]; - - if let Some((_, _, for_)) = item.trait_ { - let msg = "Invalid pallet::call, expected no trait ident as in \ + pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { + let item = if let syn::Item::Impl(item) = item { + item + } else { + return Err(syn::Error::new( + item.span(), + "Invalid pallet::extra_constants, expected item impl", + )); + }; + + let instances = vec![ + helper::check_impl_gen(&item.generics, item.impl_token.span())?, + helper::check_pallet_struct_usage(&item.self_ty)?, + ]; + + if let Some((_, _, for_)) = item.trait_ { + let msg = "Invalid pallet::call, expected no trait ident as in \ `impl<..> Pallet<..> { .. }`"; - return Err(syn::Error::new(for_.span(), msg)) - } - - let mut extra_constants = vec![]; - for impl_item in &mut item.items { - let method = if let syn::ImplItem::Fn(method) = impl_item { - method - } else { - let msg = "Invalid pallet::call, only method accepted"; - return Err(syn::Error::new(impl_item.span(), msg)) - }; - - if !method.sig.inputs.is_empty() { - let msg = "Invalid pallet::extra_constants, method must have 0 args"; - return Err(syn::Error::new(method.sig.span(), msg)) - } - - if !method.sig.generics.params.is_empty() { - let msg = "Invalid pallet::extra_constants, method must have 0 generics"; - return Err(syn::Error::new(method.sig.generics.params[0].span(), msg)) - } - - if method.sig.generics.where_clause.is_some() { - let msg = "Invalid pallet::extra_constants, method must have no where clause"; - return Err(syn::Error::new(method.sig.generics.where_clause.span(), msg)) - } - - let type_ = match &method.sig.output { - syn::ReturnType::Default => { - let msg = "Invalid pallet::extra_constants, method must have a return type"; - return Err(syn::Error::new(method.span(), msg)) - }, - syn::ReturnType::Type(_, type_) => *type_.clone(), - }; - - // parse metadata_name - let mut extra_constant_attrs: Vec = - helper::take_item_pallet_attrs(method)?; - - if extra_constant_attrs.len() > 1 { - let msg = - "Invalid attribute in pallet::constant_name, only one attribute is expected"; - return Err(syn::Error::new(extra_constant_attrs[1].metadata_name.span(), msg)) - } - - let metadata_name = extra_constant_attrs.pop().map(|attr| attr.metadata_name); - - extra_constants.push(ExtraConstantDef { - ident: method.sig.ident.clone(), - type_, - doc: get_doc_literals(&method.attrs), - metadata_name, - }); - } - - Ok(Self { - index, - instances, - where_clause: item.generics.where_clause.clone(), - extra_constants, - }) - } + return Err(syn::Error::new(for_.span(), msg)); + } + + let mut extra_constants = vec![]; + for impl_item in &mut item.items { + let method = if let syn::ImplItem::Fn(method) = impl_item { + method + } else { + let msg = "Invalid pallet::call, only method accepted"; + return Err(syn::Error::new(impl_item.span(), msg)); + }; + + if !method.sig.inputs.is_empty() { + let msg = "Invalid pallet::extra_constants, method must have 0 args"; + return Err(syn::Error::new(method.sig.span(), msg)); + } + + if !method.sig.generics.params.is_empty() { + let msg = "Invalid pallet::extra_constants, method must have 0 generics"; + return Err(syn::Error::new(method.sig.generics.params[0].span(), msg)); + } + + if method.sig.generics.where_clause.is_some() { + let msg = "Invalid pallet::extra_constants, method must have no where clause"; + return Err(syn::Error::new( + method.sig.generics.where_clause.span(), + msg, + )); + } + + let type_ = match &method.sig.output { + syn::ReturnType::Default => { + let msg = "Invalid pallet::extra_constants, method must have a return type"; + return Err(syn::Error::new(method.span(), msg)); + } + syn::ReturnType::Type(_, type_) => *type_.clone(), + }; + + // parse metadata_name + let mut extra_constant_attrs: Vec = + helper::take_item_pallet_attrs(method)?; + + if extra_constant_attrs.len() > 1 { + let msg = + "Invalid attribute in pallet::constant_name, only one attribute is expected"; + return Err(syn::Error::new( + extra_constant_attrs[1].metadata_name.span(), + msg, + )); + } + + let metadata_name = extra_constant_attrs.pop().map(|attr| attr.metadata_name); + + extra_constants.push(ExtraConstantDef { + ident: method.sig.ident.clone(), + type_, + doc: get_doc_literals(&method.attrs), + metadata_name, + }); + } + + Ok(Self { + index, + instances, + where_clause: item.generics.where_clause.clone(), + extra_constants, + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/genesis_build.rs b/support/procedural-fork/src/pallet/parse/genesis_build.rs index d0e1d9ec9..670d4d5ef 100644 --- a/support/procedural-fork/src/pallet/parse/genesis_build.rs +++ b/support/procedural-fork/src/pallet/parse/genesis_build.rs @@ -20,42 +20,47 @@ use syn::spanned::Spanned; /// Definition for pallet genesis build implementation. pub struct GenesisBuildDef { - /// The index of item in pallet module. - pub index: usize, - /// A set of usage of instance, must be check for consistency with trait. - pub instances: Option>, - /// The where_clause used. - pub where_clause: Option, - /// The span of the pallet::genesis_build attribute. - pub attr_span: proc_macro2::Span, + /// The index of item in pallet module. + pub index: usize, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Option>, + /// The where_clause used. + pub where_clause: Option, + /// The span of the pallet::genesis_build attribute. + pub attr_span: proc_macro2::Span, } impl GenesisBuildDef { - pub fn try_from( - attr_span: proc_macro2::Span, - index: usize, - item: &mut syn::Item, - ) -> syn::Result { - let item = if let syn::Item::Impl(item) = item { - item - } else { - let msg = "Invalid pallet::genesis_build, expected item impl"; - return Err(syn::Error::new(item.span(), msg)) - }; - - let item_trait = &item - .trait_ - .as_ref() - .ok_or_else(|| { - let msg = "Invalid pallet::genesis_build, expected impl<..> GenesisBuild<..> \ + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + ) -> syn::Result { + let item = if let syn::Item::Impl(item) = item { + item + } else { + let msg = "Invalid pallet::genesis_build, expected item impl"; + return Err(syn::Error::new(item.span(), msg)); + }; + + let item_trait = &item + .trait_ + .as_ref() + .ok_or_else(|| { + let msg = "Invalid pallet::genesis_build, expected impl<..> GenesisBuild<..> \ for GenesisConfig<..>"; - syn::Error::new(item.span(), msg) - })? - .1; + syn::Error::new(item.span(), msg) + })? + .1; - let instances = - helper::check_genesis_builder_usage(item_trait)?.map(|instances| vec![instances]); + let instances = + helper::check_genesis_builder_usage(item_trait)?.map(|instances| vec![instances]); - Ok(Self { attr_span, index, instances, where_clause: item.generics.where_clause.clone() }) - } + Ok(Self { + attr_span, + index, + instances, + where_clause: item.generics.where_clause.clone(), + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/genesis_config.rs b/support/procedural-fork/src/pallet/parse/genesis_config.rs index 62da6ba13..1c52345eb 100644 --- a/support/procedural-fork/src/pallet/parse/genesis_config.rs +++ b/support/procedural-fork/src/pallet/parse/genesis_config.rs @@ -24,50 +24,55 @@ use syn::spanned::Spanned; /// * `struct GenesisConfig` /// * `enum GenesisConfig` pub struct GenesisConfigDef { - /// The index of item in pallet module. - pub index: usize, - /// The kind of generic the type `GenesisConfig` has. - pub gen_kind: super::GenericKind, - /// A set of usage of instance, must be check for consistency with trait. - pub instances: Vec, - /// The ident of genesis_config, can be used for span. - pub genesis_config: syn::Ident, + /// The index of item in pallet module. + pub index: usize, + /// The kind of generic the type `GenesisConfig` has. + pub gen_kind: super::GenericKind, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Vec, + /// The ident of genesis_config, can be used for span. + pub genesis_config: syn::Ident, } impl GenesisConfigDef { - pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { - let item_span = item.span(); - let (vis, ident, generics) = match &item { - syn::Item::Enum(item) => (&item.vis, &item.ident, &item.generics), - syn::Item::Struct(item) => (&item.vis, &item.ident, &item.generics), - _ => { - let msg = "Invalid pallet::genesis_config, expected enum or struct"; - return Err(syn::Error::new(item.span(), msg)) - }, - }; + pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { + let item_span = item.span(); + let (vis, ident, generics) = match &item { + syn::Item::Enum(item) => (&item.vis, &item.ident, &item.generics), + syn::Item::Struct(item) => (&item.vis, &item.ident, &item.generics), + _ => { + let msg = "Invalid pallet::genesis_config, expected enum or struct"; + return Err(syn::Error::new(item.span(), msg)); + } + }; - let mut instances = vec![]; - // NOTE: GenesisConfig is not allowed to be only generic on I because it is not supported - // by construct_runtime. - if let Some(u) = helper::check_type_def_optional_gen(generics, ident.span())? { - instances.push(u); - } + let mut instances = vec![]; + // NOTE: GenesisConfig is not allowed to be only generic on I because it is not supported + // by construct_runtime. + if let Some(u) = helper::check_type_def_optional_gen(generics, ident.span())? { + instances.push(u); + } - let has_instance = generics.type_params().any(|t| t.ident == "I"); - let has_config = generics.type_params().any(|t| t.ident == "T"); - let gen_kind = super::GenericKind::from_gens(has_config, has_instance) - .expect("Checked by `helper::check_type_def_optional_gen` above"); + let has_instance = generics.type_params().any(|t| t.ident == "I"); + let has_config = generics.type_params().any(|t| t.ident == "T"); + let gen_kind = super::GenericKind::from_gens(has_config, has_instance) + .expect("Checked by `helper::check_type_def_optional_gen` above"); - if !matches!(vis, syn::Visibility::Public(_)) { - let msg = "Invalid pallet::genesis_config, GenesisConfig must be public"; - return Err(syn::Error::new(item_span, msg)) - } + if !matches!(vis, syn::Visibility::Public(_)) { + let msg = "Invalid pallet::genesis_config, GenesisConfig must be public"; + return Err(syn::Error::new(item_span, msg)); + } - if ident != "GenesisConfig" { - let msg = "Invalid pallet::genesis_config, ident must `GenesisConfig`"; - return Err(syn::Error::new(ident.span(), msg)) - } + if ident != "GenesisConfig" { + let msg = "Invalid pallet::genesis_config, ident must `GenesisConfig`"; + return Err(syn::Error::new(ident.span(), msg)); + } - Ok(GenesisConfigDef { index, genesis_config: ident.clone(), instances, gen_kind }) - } + Ok(GenesisConfigDef { + index, + genesis_config: ident.clone(), + instances, + gen_kind, + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/helper.rs b/support/procedural-fork/src/pallet/parse/helper.rs index 3187c9139..f58c8d81c 100644 --- a/support/procedural-fork/src/pallet/parse/helper.rs +++ b/support/procedural-fork/src/pallet/parse/helper.rs @@ -21,164 +21,176 @@ use syn::spanned::Spanned; /// List of additional token to be used for parsing. mod keyword { - syn::custom_keyword!(I); - syn::custom_keyword!(compact); - syn::custom_keyword!(GenesisBuild); - syn::custom_keyword!(BuildGenesisConfig); - syn::custom_keyword!(Config); - syn::custom_keyword!(T); - syn::custom_keyword!(Pallet); - syn::custom_keyword!(origin); - syn::custom_keyword!(DispatchResult); - syn::custom_keyword!(DispatchResultWithPostInfo); + syn::custom_keyword!(I); + syn::custom_keyword!(compact); + syn::custom_keyword!(GenesisBuild); + syn::custom_keyword!(BuildGenesisConfig); + syn::custom_keyword!(Config); + syn::custom_keyword!(T); + syn::custom_keyword!(Pallet); + syn::custom_keyword!(origin); + syn::custom_keyword!(DispatchResult); + syn::custom_keyword!(DispatchResultWithPostInfo); } /// A usage of instance, either the trait `Config` has been used with instance or without instance. /// Used to check for consistency. #[derive(Clone)] pub struct InstanceUsage { - pub has_instance: bool, - pub span: proc_macro2::Span, + pub has_instance: bool, + pub span: proc_macro2::Span, } /// Trait implemented for syn items to get mutable references on their attributes. /// /// NOTE: verbatim variants are not supported. pub trait MutItemAttrs { - fn mut_item_attrs(&mut self) -> Option<&mut Vec>; + fn mut_item_attrs(&mut self) -> Option<&mut Vec>; } /// Take the first pallet attribute (e.g. attribute like `#[pallet..]`) and decode it to `Attr` pub(crate) fn take_first_item_pallet_attr( - item: &mut impl MutItemAttrs, + item: &mut impl MutItemAttrs, ) -> syn::Result> where - Attr: syn::parse::Parse, + Attr: syn::parse::Parse, { - let attrs = if let Some(attrs) = item.mut_item_attrs() { attrs } else { return Ok(None) }; - - if let Some(index) = attrs.iter().position(|attr| { - attr.path().segments.first().map_or(false, |segment| segment.ident == "pallet") - }) { - let pallet_attr = attrs.remove(index); - Ok(Some(syn::parse2(pallet_attr.into_token_stream())?)) - } else { - Ok(None) - } + let attrs = if let Some(attrs) = item.mut_item_attrs() { + attrs + } else { + return Ok(None); + }; + + if let Some(index) = attrs.iter().position(|attr| { + attr.path() + .segments + .first() + .map_or(false, |segment| segment.ident == "pallet") + }) { + let pallet_attr = attrs.remove(index); + Ok(Some(syn::parse2(pallet_attr.into_token_stream())?)) + } else { + Ok(None) + } } /// Take all the pallet attributes (e.g. attribute like `#[pallet..]`) and decode them to `Attr` pub(crate) fn take_item_pallet_attrs(item: &mut impl MutItemAttrs) -> syn::Result> where - Attr: syn::parse::Parse, + Attr: syn::parse::Parse, { - let mut pallet_attrs = Vec::new(); + let mut pallet_attrs = Vec::new(); - while let Some(attr) = take_first_item_pallet_attr(item)? { - pallet_attrs.push(attr) - } + while let Some(attr) = take_first_item_pallet_attr(item)? { + pallet_attrs.push(attr) + } - Ok(pallet_attrs) + Ok(pallet_attrs) } /// Get all the cfg attributes (e.g. attribute like `#[cfg..]`) and decode them to `Attr` pub fn get_item_cfg_attrs(attrs: &[syn::Attribute]) -> Vec { - attrs - .iter() - .filter_map(|attr| { - if attr.path().segments.first().map_or(false, |segment| segment.ident == "cfg") { - Some(attr.clone()) - } else { - None - } - }) - .collect::>() + attrs + .iter() + .filter_map(|attr| { + if attr + .path() + .segments + .first() + .map_or(false, |segment| segment.ident == "cfg") + { + Some(attr.clone()) + } else { + None + } + }) + .collect::>() } impl MutItemAttrs for syn::Item { - fn mut_item_attrs(&mut self) -> Option<&mut Vec> { - match self { - Self::Const(item) => Some(item.attrs.as_mut()), - Self::Enum(item) => Some(item.attrs.as_mut()), - Self::ExternCrate(item) => Some(item.attrs.as_mut()), - Self::Fn(item) => Some(item.attrs.as_mut()), - Self::ForeignMod(item) => Some(item.attrs.as_mut()), - Self::Impl(item) => Some(item.attrs.as_mut()), - Self::Macro(item) => Some(item.attrs.as_mut()), - Self::Mod(item) => Some(item.attrs.as_mut()), - Self::Static(item) => Some(item.attrs.as_mut()), - Self::Struct(item) => Some(item.attrs.as_mut()), - Self::Trait(item) => Some(item.attrs.as_mut()), - Self::TraitAlias(item) => Some(item.attrs.as_mut()), - Self::Type(item) => Some(item.attrs.as_mut()), - Self::Union(item) => Some(item.attrs.as_mut()), - Self::Use(item) => Some(item.attrs.as_mut()), - _ => None, - } - } + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + match self { + Self::Const(item) => Some(item.attrs.as_mut()), + Self::Enum(item) => Some(item.attrs.as_mut()), + Self::ExternCrate(item) => Some(item.attrs.as_mut()), + Self::Fn(item) => Some(item.attrs.as_mut()), + Self::ForeignMod(item) => Some(item.attrs.as_mut()), + Self::Impl(item) => Some(item.attrs.as_mut()), + Self::Macro(item) => Some(item.attrs.as_mut()), + Self::Mod(item) => Some(item.attrs.as_mut()), + Self::Static(item) => Some(item.attrs.as_mut()), + Self::Struct(item) => Some(item.attrs.as_mut()), + Self::Trait(item) => Some(item.attrs.as_mut()), + Self::TraitAlias(item) => Some(item.attrs.as_mut()), + Self::Type(item) => Some(item.attrs.as_mut()), + Self::Union(item) => Some(item.attrs.as_mut()), + Self::Use(item) => Some(item.attrs.as_mut()), + _ => None, + } + } } impl MutItemAttrs for syn::TraitItem { - fn mut_item_attrs(&mut self) -> Option<&mut Vec> { - match self { - Self::Const(item) => Some(item.attrs.as_mut()), - Self::Fn(item) => Some(item.attrs.as_mut()), - Self::Type(item) => Some(item.attrs.as_mut()), - Self::Macro(item) => Some(item.attrs.as_mut()), - _ => None, - } - } + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + match self { + Self::Const(item) => Some(item.attrs.as_mut()), + Self::Fn(item) => Some(item.attrs.as_mut()), + Self::Type(item) => Some(item.attrs.as_mut()), + Self::Macro(item) => Some(item.attrs.as_mut()), + _ => None, + } + } } impl MutItemAttrs for Vec { - fn mut_item_attrs(&mut self) -> Option<&mut Vec> { - Some(self) - } + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + Some(self) + } } impl MutItemAttrs for syn::ItemMod { - fn mut_item_attrs(&mut self) -> Option<&mut Vec> { - Some(&mut self.attrs) - } + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + Some(&mut self.attrs) + } } impl MutItemAttrs for syn::ImplItemFn { - fn mut_item_attrs(&mut self) -> Option<&mut Vec> { - Some(&mut self.attrs) - } + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + Some(&mut self.attrs) + } } impl MutItemAttrs for syn::ItemType { - fn mut_item_attrs(&mut self) -> Option<&mut Vec> { - Some(&mut self.attrs) - } + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + Some(&mut self.attrs) + } } /// Parse for `()` struct Unit; impl syn::parse::Parse for Unit { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let content; - syn::parenthesized!(content in input); - if !content.is_empty() { - let msg = "unexpected tokens, expected nothing inside parenthesis as `()`"; - return Err(syn::Error::new(content.span(), msg)) - } - Ok(Self) - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let content; + syn::parenthesized!(content in input); + if !content.is_empty() { + let msg = "unexpected tokens, expected nothing inside parenthesis as `()`"; + return Err(syn::Error::new(content.span(), msg)); + } + Ok(Self) + } } /// Parse for `'static` struct StaticLifetime; impl syn::parse::Parse for StaticLifetime { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let lifetime = input.parse::()?; - if lifetime.ident != "static" { - let msg = "unexpected tokens, expected `static`"; - return Err(syn::Error::new(lifetime.ident.span(), msg)) - } - Ok(Self) - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let lifetime = input.parse::()?; + if lifetime.ident != "static" { + let msg = "unexpected tokens, expected `static`"; + return Err(syn::Error::new(lifetime.ident.span(), msg)); + } + Ok(Self) + } } /// Check the syntax: `I: 'static = ()` @@ -187,28 +199,28 @@ impl syn::parse::Parse for StaticLifetime { /// /// return the instance if found. pub fn check_config_def_gen(gen: &syn::Generics, span: proc_macro2::Span) -> syn::Result<()> { - let expected = "expected `I: 'static = ()`"; - pub struct CheckTraitDefGenerics; - impl syn::parse::Parse for CheckTraitDefGenerics { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - - Ok(Self) - } - } - - syn::parse2::(gen.params.to_token_stream()).map_err(|e| { - let msg = format!("Invalid generics: {}", expected); - let mut err = syn::Error::new(span, msg); - err.combine(e); - err - })?; - - Ok(()) + let expected = "expected `I: 'static = ()`"; + pub struct CheckTraitDefGenerics; + impl syn::parse::Parse for CheckTraitDefGenerics { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + + Ok(Self) + } + } + + syn::parse2::(gen.params.to_token_stream()).map_err(|e| { + let msg = format!("Invalid generics: {}", expected); + let mut err = syn::Error::new(span, msg); + err.combine(e); + err + })?; + + Ok(()) } /// Check the syntax: @@ -219,38 +231,41 @@ pub fn check_config_def_gen(gen: &syn::Generics, span: proc_macro2::Span) -> syn /// /// return the instance if found. pub fn check_type_def_gen_no_bounds( - gen: &syn::Generics, - span: proc_macro2::Span, + gen: &syn::Generics, + span: proc_macro2::Span, ) -> syn::Result { - let expected = "expected `T` or `T, I = ()`"; - pub struct Checker(InstanceUsage); - impl syn::parse::Parse for Checker { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let mut instance_usage = InstanceUsage { has_instance: false, span: input.span() }; - - input.parse::()?; - if input.peek(syn::Token![,]) { - instance_usage.has_instance = true; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - } - - Ok(Self(instance_usage)) - } - } - - let i = syn::parse2::(gen.params.to_token_stream()) - .map_err(|e| { - let msg = format!("Invalid type def generics: {}", expected); - let mut err = syn::Error::new(span, msg); - err.combine(e); - err - })? - .0; - - Ok(i) + let expected = "expected `T` or `T, I = ()`"; + pub struct Checker(InstanceUsage); + impl syn::parse::Parse for Checker { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut instance_usage = InstanceUsage { + has_instance: false, + span: input.span(), + }; + + input.parse::()?; + if input.peek(syn::Token![,]) { + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + } + + Ok(Self(instance_usage)) + } + } + + let i = syn::parse2::(gen.params.to_token_stream()) + .map_err(|e| { + let msg = format!("Invalid type def generics: {}", expected); + let mut err = syn::Error::new(span, msg); + err.combine(e); + err + })? + .0; + + Ok(i) } /// Check the syntax: @@ -264,76 +279,79 @@ pub fn check_type_def_gen_no_bounds( /// /// return some instance usage if there is some generic, or none otherwise. pub fn check_type_def_optional_gen( - gen: &syn::Generics, - span: proc_macro2::Span, + gen: &syn::Generics, + span: proc_macro2::Span, ) -> syn::Result> { - let expected = "expected `` or `T` or `T: Config` or `T, I = ()` or \ + let expected = "expected `` or `T` or `T: Config` or `T, I = ()` or \ `T: Config, I: 'static = ()`"; - pub struct Checker(Option); - impl syn::parse::Parse for Checker { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - if input.is_empty() { - return Ok(Self(None)) - } - - let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false }; - - input.parse::()?; - - if input.is_empty() { - return Ok(Self(Some(instance_usage))) - } - - let lookahead = input.lookahead1(); - if lookahead.peek(syn::Token![,]) { - instance_usage.has_instance = true; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - - Ok(Self(Some(instance_usage))) - } else if lookahead.peek(syn::Token![:]) { - input.parse::()?; - input.parse::()?; - - if input.is_empty() { - return Ok(Self(Some(instance_usage))) - } - - instance_usage.has_instance = true; - input.parse::()?; - input.parse::()?; - input.parse::]>()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - - Ok(Self(Some(instance_usage))) - } else { - Err(lookahead.error()) - } - } - } - - let i = syn::parse2::(gen.params.to_token_stream()) - .map_err(|e| { - let msg = format!("Invalid type def generics: {}", expected); - let mut err = syn::Error::new(span, msg); - err.combine(e); - err - })? - .0 - // Span can be call_site if generic is empty. Thus we replace it. - .map(|mut i| { - i.span = span; - i - }); - - Ok(i) + pub struct Checker(Option); + impl syn::parse::Parse for Checker { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + if input.is_empty() { + return Ok(Self(None)); + } + + let mut instance_usage = InstanceUsage { + span: input.span(), + has_instance: false, + }; + + input.parse::()?; + + if input.is_empty() { + return Ok(Self(Some(instance_usage))); + } + + let lookahead = input.lookahead1(); + if lookahead.peek(syn::Token![,]) { + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + + Ok(Self(Some(instance_usage))) + } else if lookahead.peek(syn::Token![:]) { + input.parse::()?; + input.parse::()?; + + if input.is_empty() { + return Ok(Self(Some(instance_usage))); + } + + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + input.parse::]>()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + + Ok(Self(Some(instance_usage))) + } else { + Err(lookahead.error()) + } + } + } + + let i = syn::parse2::(gen.params.to_token_stream()) + .map_err(|e| { + let msg = format!("Invalid type def generics: {}", expected); + let mut err = syn::Error::new(span, msg); + err.combine(e); + err + })? + .0 + // Span can be call_site if generic is empty. Thus we replace it. + .map(|mut i| { + i.span = span; + i + }); + + Ok(i) } /// Check the syntax: @@ -342,36 +360,39 @@ pub fn check_type_def_optional_gen( /// /// return the instance if found. pub fn check_pallet_struct_usage(type_: &Box) -> syn::Result { - let expected = "expected `Pallet` or `Pallet`"; - pub struct Checker(InstanceUsage); - impl syn::parse::Parse for Checker { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false }; - - input.parse::()?; - input.parse::()?; - input.parse::()?; - if input.peek(syn::Token![,]) { - instance_usage.has_instance = true; - input.parse::()?; - input.parse::()?; - } - input.parse::]>()?; - - Ok(Self(instance_usage)) - } - } - - let i = syn::parse2::(type_.to_token_stream()) - .map_err(|e| { - let msg = format!("Invalid pallet struct: {}", expected); - let mut err = syn::Error::new(type_.span(), msg); - err.combine(e); - err - })? - .0; - - Ok(i) + let expected = "expected `Pallet` or `Pallet`"; + pub struct Checker(InstanceUsage); + impl syn::parse::Parse for Checker { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut instance_usage = InstanceUsage { + span: input.span(), + has_instance: false, + }; + + input.parse::()?; + input.parse::()?; + input.parse::()?; + if input.peek(syn::Token![,]) { + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + } + input.parse::]>()?; + + Ok(Self(instance_usage)) + } + } + + let i = syn::parse2::(type_.to_token_stream()) + .map_err(|e| { + let msg = format!("Invalid pallet struct: {}", expected); + let mut err = syn::Error::new(type_.span(), msg); + err.combine(e); + err + })? + .0; + + Ok(i) } /// Check the generic is: @@ -382,39 +403,42 @@ pub fn check_pallet_struct_usage(type_: &Box) -> syn::Result syn::Result { - let expected = "expected `impl` or `impl, I: 'static>`"; - pub struct Checker(InstanceUsage); - impl syn::parse::Parse for Checker { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false }; - - input.parse::()?; - input.parse::()?; - input.parse::()?; - if input.peek(syn::Token![<]) { - instance_usage.has_instance = true; - input.parse::()?; - input.parse::()?; - input.parse::]>()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - } - - Ok(Self(instance_usage)) - } - } - - let i = syn::parse2::(gen.params.to_token_stream()) - .map_err(|e| { - let mut err = syn::Error::new(span, format!("Invalid generics: {}", expected)); - err.combine(e); - err - })? - .0; - - Ok(i) + let expected = "expected `impl` or `impl, I: 'static>`"; + pub struct Checker(InstanceUsage); + impl syn::parse::Parse for Checker { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut instance_usage = InstanceUsage { + span: input.span(), + has_instance: false, + }; + + input.parse::()?; + input.parse::()?; + input.parse::()?; + if input.peek(syn::Token![<]) { + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + input.parse::]>()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + } + + Ok(Self(instance_usage)) + } + } + + let i = syn::parse2::(gen.params.to_token_stream()) + .map_err(|e| { + let mut err = syn::Error::new(span, format!("Invalid generics: {}", expected)); + err.combine(e); + err + })? + .0; + + Ok(i) } /// Check the syntax: @@ -427,70 +451,73 @@ pub fn check_impl_gen(gen: &syn::Generics, span: proc_macro2::Span) -> syn::Resu /// /// return the instance if found. pub fn check_type_def_gen( - gen: &syn::Generics, - span: proc_macro2::Span, + gen: &syn::Generics, + span: proc_macro2::Span, ) -> syn::Result { - let expected = "expected `T` or `T: Config` or `T, I = ()` or \ + let expected = "expected `T` or `T: Config` or `T, I = ()` or \ `T: Config, I: 'static = ()`"; - pub struct Checker(InstanceUsage); - impl syn::parse::Parse for Checker { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false }; - - input.parse::()?; - - if input.is_empty() { - return Ok(Self(instance_usage)) - } - - let lookahead = input.lookahead1(); - if lookahead.peek(syn::Token![,]) { - instance_usage.has_instance = true; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - - Ok(Self(instance_usage)) - } else if lookahead.peek(syn::Token![:]) { - input.parse::()?; - input.parse::()?; - - if input.is_empty() { - return Ok(Self(instance_usage)) - } - - instance_usage.has_instance = true; - input.parse::()?; - input.parse::()?; - input.parse::]>()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - - Ok(Self(instance_usage)) - } else { - Err(lookahead.error()) - } - } - } - - let mut i = syn::parse2::(gen.params.to_token_stream()) - .map_err(|e| { - let msg = format!("Invalid type def generics: {}", expected); - let mut err = syn::Error::new(span, msg); - err.combine(e); - err - })? - .0; - - // Span can be call_site if generic is empty. Thus we replace it. - i.span = span; - - Ok(i) + pub struct Checker(InstanceUsage); + impl syn::parse::Parse for Checker { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut instance_usage = InstanceUsage { + span: input.span(), + has_instance: false, + }; + + input.parse::()?; + + if input.is_empty() { + return Ok(Self(instance_usage)); + } + + let lookahead = input.lookahead1(); + if lookahead.peek(syn::Token![,]) { + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + + Ok(Self(instance_usage)) + } else if lookahead.peek(syn::Token![:]) { + input.parse::()?; + input.parse::()?; + + if input.is_empty() { + return Ok(Self(instance_usage)); + } + + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + input.parse::]>()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + + Ok(Self(instance_usage)) + } else { + Err(lookahead.error()) + } + } + } + + let mut i = syn::parse2::(gen.params.to_token_stream()) + .map_err(|e| { + let msg = format!("Invalid type def generics: {}", expected); + let mut err = syn::Error::new(span, msg); + err.combine(e); + err + })? + .0; + + // Span can be call_site if generic is empty. Thus we replace it. + i.span = span; + + Ok(i) } /// Check the syntax: @@ -501,40 +528,43 @@ pub fn check_type_def_gen( /// return the instance if found for `GenesisBuild` /// return None for BuildGenesisConfig pub fn check_genesis_builder_usage(type_: &syn::Path) -> syn::Result> { - let expected = "expected `BuildGenesisConfig` (or the deprecated `GenesisBuild` or `GenesisBuild`)"; - pub struct Checker(Option); - impl syn::parse::Parse for Checker { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false }; - - if input.peek(keyword::GenesisBuild) { - input.parse::()?; - input.parse::()?; - input.parse::()?; - if input.peek(syn::Token![,]) { - instance_usage.has_instance = true; - input.parse::()?; - input.parse::()?; - } - input.parse::]>()?; - return Ok(Self(Some(instance_usage))) - } else { - input.parse::()?; - return Ok(Self(None)) - } - } - } - - let i = syn::parse2::(type_.to_token_stream()) - .map_err(|e| { - let msg = format!("Invalid genesis builder: {}", expected); - let mut err = syn::Error::new(type_.span(), msg); - err.combine(e); - err - })? - .0; - - Ok(i) + let expected = "expected `BuildGenesisConfig` (or the deprecated `GenesisBuild` or `GenesisBuild`)"; + pub struct Checker(Option); + impl syn::parse::Parse for Checker { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut instance_usage = InstanceUsage { + span: input.span(), + has_instance: false, + }; + + if input.peek(keyword::GenesisBuild) { + input.parse::()?; + input.parse::()?; + input.parse::()?; + if input.peek(syn::Token![,]) { + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + } + input.parse::]>()?; + return Ok(Self(Some(instance_usage))); + } else { + input.parse::()?; + return Ok(Self(None)); + } + } + } + + let i = syn::parse2::(type_.to_token_stream()) + .map_err(|e| { + let msg = format!("Invalid genesis builder: {}", expected); + let mut err = syn::Error::new(type_.span(), msg); + err.combine(e); + err + })? + .0; + + Ok(i) } /// Check the syntax: @@ -546,87 +576,90 @@ pub fn check_genesis_builder_usage(type_: &syn::Path) -> syn::Result syn::Result> { - let expected = "expected `` or `T: Config` or `T: Config, I: 'static`"; - pub struct Checker(Option); - impl syn::parse::Parse for Checker { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - if input.is_empty() { - return Ok(Self(None)) - } - - input.parse::()?; - input.parse::()?; - input.parse::()?; - - let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false }; - - if input.is_empty() { - return Ok(Self(Some(instance_usage))) - } - - instance_usage.has_instance = true; - input.parse::()?; - input.parse::()?; - input.parse::]>()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - - Ok(Self(Some(instance_usage))) - } - } - - let i = syn::parse2::(gen.params.to_token_stream()) - .map_err(|e| { - let msg = format!("Invalid type def generics: {}", expected); - let mut err = syn::Error::new(span, msg); - err.combine(e); - err - })? - .0 - // Span can be call_site if generic is empty. Thus we replace it. - .map(|mut i| { - i.span = span; - i - }); - - Ok(i) + let expected = "expected `` or `T: Config` or `T: Config, I: 'static`"; + pub struct Checker(Option); + impl syn::parse::Parse for Checker { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + if input.is_empty() { + return Ok(Self(None)); + } + + input.parse::()?; + input.parse::()?; + input.parse::()?; + + let mut instance_usage = InstanceUsage { + span: input.span(), + has_instance: false, + }; + + if input.is_empty() { + return Ok(Self(Some(instance_usage))); + } + + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + input.parse::]>()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + + Ok(Self(Some(instance_usage))) + } + } + + let i = syn::parse2::(gen.params.to_token_stream()) + .map_err(|e| { + let msg = format!("Invalid type def generics: {}", expected); + let mut err = syn::Error::new(span, msg); + err.combine(e); + err + })? + .0 + // Span can be call_site if generic is empty. Thus we replace it. + .map(|mut i| { + i.span = span; + i + }); + + Ok(i) } /// Check the keyword `DispatchResultWithPostInfo` or `DispatchResult`. pub fn check_pallet_call_return_type(type_: &syn::Type) -> syn::Result<()> { - pub struct Checker; - impl syn::parse::Parse for Checker { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let lookahead = input.lookahead1(); - if lookahead.peek(keyword::DispatchResultWithPostInfo) { - input.parse::()?; - Ok(Self) - } else if lookahead.peek(keyword::DispatchResult) { - input.parse::()?; - Ok(Self) - } else { - Err(lookahead.error()) - } - } - } - - syn::parse2::(type_.to_token_stream()).map(|_| ()) + pub struct Checker; + impl syn::parse::Parse for Checker { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let lookahead = input.lookahead1(); + if lookahead.peek(keyword::DispatchResultWithPostInfo) { + input.parse::()?; + Ok(Self) + } else if lookahead.peek(keyword::DispatchResult) { + input.parse::()?; + Ok(Self) + } else { + Err(lookahead.error()) + } + } + } + + syn::parse2::(type_.to_token_stream()).map(|_| ()) } pub(crate) fn two128_str(s: &str) -> TokenStream { - bytes_to_array(sp_crypto_hashing::twox_128(s.as_bytes()).into_iter()) + bytes_to_array(sp_crypto_hashing::twox_128(s.as_bytes()).into_iter()) } pub(crate) fn bytes_to_array(bytes: impl IntoIterator) -> TokenStream { - let bytes = bytes.into_iter(); + let bytes = bytes.into_iter(); - quote!( - [ #( #bytes ),* ] - ) - .into() + quote!( + [ #( #bytes ),* ] + ) + .into() } diff --git a/support/procedural-fork/src/pallet/parse/hooks.rs b/support/procedural-fork/src/pallet/parse/hooks.rs index 37d7d22f4..1cf5c72cc 100644 --- a/support/procedural-fork/src/pallet/parse/hooks.rs +++ b/support/procedural-fork/src/pallet/parse/hooks.rs @@ -20,67 +20,67 @@ use syn::spanned::Spanned; /// Implementation of the pallet hooks. pub struct HooksDef { - /// The index of item in pallet. - pub index: usize, - /// A set of usage of instance, must be check for consistency with trait. - pub instances: Vec, - /// The where_clause used. - pub where_clause: Option, - /// The span of the pallet::hooks attribute. - pub attr_span: proc_macro2::Span, - /// Boolean flag, set to true if the `on_runtime_upgrade` method of hooks was implemented. - pub has_runtime_upgrade: bool, + /// The index of item in pallet. + pub index: usize, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Vec, + /// The where_clause used. + pub where_clause: Option, + /// The span of the pallet::hooks attribute. + pub attr_span: proc_macro2::Span, + /// Boolean flag, set to true if the `on_runtime_upgrade` method of hooks was implemented. + pub has_runtime_upgrade: bool, } impl HooksDef { - pub fn try_from( - attr_span: proc_macro2::Span, - index: usize, - item: &mut syn::Item, - ) -> syn::Result { - let item = if let syn::Item::Impl(item) = item { - item - } else { - let msg = "Invalid pallet::hooks, expected item impl"; - return Err(syn::Error::new(item.span(), msg)) - }; + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + ) -> syn::Result { + let item = if let syn::Item::Impl(item) = item { + item + } else { + let msg = "Invalid pallet::hooks, expected item impl"; + return Err(syn::Error::new(item.span(), msg)); + }; - let instances = vec![ - helper::check_pallet_struct_usage(&item.self_ty)?, - helper::check_impl_gen(&item.generics, item.impl_token.span())?, - ]; + let instances = vec![ + helper::check_pallet_struct_usage(&item.self_ty)?, + helper::check_impl_gen(&item.generics, item.impl_token.span())?, + ]; - let item_trait = &item - .trait_ - .as_ref() - .ok_or_else(|| { - let msg = "Invalid pallet::hooks, expected impl<..> Hooks \ + let item_trait = &item + .trait_ + .as_ref() + .ok_or_else(|| { + let msg = "Invalid pallet::hooks, expected impl<..> Hooks \ for Pallet<..>"; - syn::Error::new(item.span(), msg) - })? - .1; + syn::Error::new(item.span(), msg) + })? + .1; - if item_trait.segments.len() != 1 || item_trait.segments[0].ident != "Hooks" { - let msg = format!( - "Invalid pallet::hooks, expected trait to be `Hooks` found `{}`\ + if item_trait.segments.len() != 1 || item_trait.segments[0].ident != "Hooks" { + let msg = format!( + "Invalid pallet::hooks, expected trait to be `Hooks` found `{}`\ , you can import from `frame_support::pallet_prelude`", - quote::quote!(#item_trait) - ); + quote::quote!(#item_trait) + ); - return Err(syn::Error::new(item_trait.span(), msg)) - } + return Err(syn::Error::new(item_trait.span(), msg)); + } - let has_runtime_upgrade = item.items.iter().any(|i| match i { - syn::ImplItem::Fn(method) => method.sig.ident == "on_runtime_upgrade", - _ => false, - }); + let has_runtime_upgrade = item.items.iter().any(|i| match i { + syn::ImplItem::Fn(method) => method.sig.ident == "on_runtime_upgrade", + _ => false, + }); - Ok(Self { - attr_span, - index, - instances, - has_runtime_upgrade, - where_clause: item.generics.where_clause.clone(), - }) - } + Ok(Self { + attr_span, + index, + instances, + has_runtime_upgrade, + where_clause: item.generics.where_clause.clone(), + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/inherent.rs b/support/procedural-fork/src/pallet/parse/inherent.rs index d8641691a..4eb04e914 100644 --- a/support/procedural-fork/src/pallet/parse/inherent.rs +++ b/support/procedural-fork/src/pallet/parse/inherent.rs @@ -20,41 +20,41 @@ use syn::spanned::Spanned; /// The definition of the pallet inherent implementation. pub struct InherentDef { - /// The index of inherent item in pallet module. - pub index: usize, - /// A set of usage of instance, must be check for consistency with trait. - pub instances: Vec, + /// The index of inherent item in pallet module. + pub index: usize, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Vec, } impl InherentDef { - pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { - let item = if let syn::Item::Impl(item) = item { - item - } else { - let msg = "Invalid pallet::inherent, expected item impl"; - return Err(syn::Error::new(item.span(), msg)) - }; - - if item.trait_.is_none() { - let msg = "Invalid pallet::inherent, expected impl<..> ProvideInherent for Pallet<..>"; - return Err(syn::Error::new(item.span(), msg)) - } - - if let Some(last) = item.trait_.as_ref().unwrap().1.segments.last() { - if last.ident != "ProvideInherent" { - let msg = "Invalid pallet::inherent, expected trait ProvideInherent"; - return Err(syn::Error::new(last.span(), msg)) - } - } else { - let msg = "Invalid pallet::inherent, expected impl<..> ProvideInherent for Pallet<..>"; - return Err(syn::Error::new(item.span(), msg)) - } - - let instances = vec![ - helper::check_pallet_struct_usage(&item.self_ty)?, - helper::check_impl_gen(&item.generics, item.impl_token.span())?, - ]; - - Ok(InherentDef { index, instances }) - } + pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { + let item = if let syn::Item::Impl(item) = item { + item + } else { + let msg = "Invalid pallet::inherent, expected item impl"; + return Err(syn::Error::new(item.span(), msg)); + }; + + if item.trait_.is_none() { + let msg = "Invalid pallet::inherent, expected impl<..> ProvideInherent for Pallet<..>"; + return Err(syn::Error::new(item.span(), msg)); + } + + if let Some(last) = item.trait_.as_ref().unwrap().1.segments.last() { + if last.ident != "ProvideInherent" { + let msg = "Invalid pallet::inherent, expected trait ProvideInherent"; + return Err(syn::Error::new(last.span(), msg)); + } + } else { + let msg = "Invalid pallet::inherent, expected impl<..> ProvideInherent for Pallet<..>"; + return Err(syn::Error::new(item.span(), msg)); + } + + let instances = vec![ + helper::check_pallet_struct_usage(&item.self_ty)?, + helper::check_impl_gen(&item.generics, item.impl_token.span())?, + ]; + + Ok(InherentDef { index, instances }) + } } diff --git a/support/procedural-fork/src/pallet/parse/mod.rs b/support/procedural-fork/src/pallet/parse/mod.rs index 6e1277461..57c252473 100644 --- a/support/procedural-fork/src/pallet/parse/mod.rs +++ b/support/procedural-fork/src/pallet/parse/mod.rs @@ -47,68 +47,68 @@ use syn::spanned::Spanned; /// Parsed definition of a pallet. pub struct Def { - /// The module items. - /// (their order must not be modified because they are registered in individual definitions). - pub item: syn::ItemMod, - pub config: config::ConfigDef, - pub pallet_struct: pallet_struct::PalletStructDef, - pub hooks: Option, - pub call: Option, - pub tasks: Option, - pub task_enum: Option, - pub storages: Vec, - pub error: Option, - pub event: Option, - pub origin: Option, - pub inherent: Option, - pub genesis_config: Option, - pub genesis_build: Option, - pub validate_unsigned: Option, - pub extra_constants: Option, - pub composites: Vec, - pub type_values: Vec, - pub frame_system: syn::Path, - pub frame_support: syn::Path, - pub dev_mode: bool, + /// The module items. + /// (their order must not be modified because they are registered in individual definitions). + pub item: syn::ItemMod, + pub config: config::ConfigDef, + pub pallet_struct: pallet_struct::PalletStructDef, + pub hooks: Option, + pub call: Option, + pub tasks: Option, + pub task_enum: Option, + pub storages: Vec, + pub error: Option, + pub event: Option, + pub origin: Option, + pub inherent: Option, + pub genesis_config: Option, + pub genesis_build: Option, + pub validate_unsigned: Option, + pub extra_constants: Option, + pub composites: Vec, + pub type_values: Vec, + pub frame_system: syn::Path, + pub frame_support: syn::Path, + pub dev_mode: bool, } impl Def { - pub fn try_from(mut item: syn::ItemMod, dev_mode: bool) -> syn::Result { - let frame_system = generate_access_from_frame_or_crate("frame-system")?; - let frame_support = generate_access_from_frame_or_crate("frame-support")?; - - let item_span = item.span(); - let items = &mut item - .content - .as_mut() - .ok_or_else(|| { - let msg = "Invalid pallet definition, expected mod to be inlined."; - syn::Error::new(item_span, msg) - })? - .1; - - let mut config = None; - let mut pallet_struct = None; - let mut hooks = None; - let mut call = None; - let mut tasks = None; - let mut task_enum = None; - let mut error = None; - let mut event = None; - let mut origin = None; - let mut inherent = None; - let mut genesis_config = None; - let mut genesis_build = None; - let mut validate_unsigned = None; - let mut extra_constants = None; - let mut storages = vec![]; - let mut type_values = vec![]; - let mut composites: Vec = vec![]; - - for (index, item) in items.iter_mut().enumerate() { - let pallet_attr: Option = helper::take_first_item_pallet_attr(item)?; - - match pallet_attr { + pub fn try_from(mut item: syn::ItemMod, dev_mode: bool) -> syn::Result { + let frame_system = generate_access_from_frame_or_crate("frame-system")?; + let frame_support = generate_access_from_frame_or_crate("frame-support")?; + + let item_span = item.span(); + let items = &mut item + .content + .as_mut() + .ok_or_else(|| { + let msg = "Invalid pallet definition, expected mod to be inlined."; + syn::Error::new(item_span, msg) + })? + .1; + + let mut config = None; + let mut pallet_struct = None; + let mut hooks = None; + let mut call = None; + let mut tasks = None; + let mut task_enum = None; + let mut error = None; + let mut event = None; + let mut origin = None; + let mut inherent = None; + let mut genesis_config = None; + let mut genesis_build = None; + let mut validate_unsigned = None; + let mut extra_constants = None; + let mut storages = vec![]; + let mut type_values = vec![]; + let mut composites: Vec = vec![]; + + for (index, item) in items.iter_mut().enumerate() { + let pallet_attr: Option = helper::take_first_item_pallet_attr(item)?; + + match pallet_attr { Some(PalletAttr::Config(span, with_default)) if config.is_none() => config = Some(config::ConfigDef::try_from( &frame_system, @@ -212,538 +212,596 @@ impl Def { }, None => (), } - } + } - if genesis_config.is_some() != genesis_build.is_some() { - let msg = format!( - "`#[pallet::genesis_config]` and `#[pallet::genesis_build]` attributes must be \ + if genesis_config.is_some() != genesis_build.is_some() { + let msg = format!( + "`#[pallet::genesis_config]` and `#[pallet::genesis_build]` attributes must be \ either both used or both not used, instead genesis_config is {} and genesis_build \ is {}", - genesis_config.as_ref().map_or("unused", |_| "used"), - genesis_build.as_ref().map_or("unused", |_| "used"), - ); - return Err(syn::Error::new(item_span, msg)) - } - - Self::resolve_tasks(&item_span, &mut tasks, &mut task_enum, items)?; - - let def = Def { - item, - config: config - .ok_or_else(|| syn::Error::new(item_span, "Missing `#[pallet::config]`"))?, - pallet_struct: pallet_struct - .ok_or_else(|| syn::Error::new(item_span, "Missing `#[pallet::pallet]`"))?, - hooks, - call, - tasks, - task_enum, - extra_constants, - genesis_config, - genesis_build, - validate_unsigned, - error, - event, - origin, - inherent, - storages, - composites, - type_values, - frame_system, - frame_support, - dev_mode, - }; - - def.check_instance_usage()?; - def.check_event_usage()?; - - Ok(def) - } - - /// Performs extra logic checks necessary for the `#[pallet::tasks_experimental]` feature. - fn resolve_tasks( - item_span: &proc_macro2::Span, - tasks: &mut Option, - task_enum: &mut Option, - items: &mut Vec, - ) -> syn::Result<()> { - // fallback for manual (without macros) definition of tasks impl - Self::resolve_manual_tasks_impl(tasks, task_enum, items)?; - - // fallback for manual (without macros) definition of task enum - Self::resolve_manual_task_enum(tasks, task_enum, items)?; - - // ensure that if `task_enum` is specified, `tasks` is also specified - match (&task_enum, &tasks) { - (Some(_), None) => - return Err(syn::Error::new( - *item_span, - "Missing `#[pallet::tasks_experimental]` impl", - )), - (None, Some(tasks)) => - if tasks.tasks_attr.is_none() { - return Err(syn::Error::new( + genesis_config.as_ref().map_or("unused", |_| "used"), + genesis_build.as_ref().map_or("unused", |_| "used"), + ); + return Err(syn::Error::new(item_span, msg)); + } + + Self::resolve_tasks(&item_span, &mut tasks, &mut task_enum, items)?; + + let def = Def { + item, + config: config + .ok_or_else(|| syn::Error::new(item_span, "Missing `#[pallet::config]`"))?, + pallet_struct: pallet_struct + .ok_or_else(|| syn::Error::new(item_span, "Missing `#[pallet::pallet]`"))?, + hooks, + call, + tasks, + task_enum, + extra_constants, + genesis_config, + genesis_build, + validate_unsigned, + error, + event, + origin, + inherent, + storages, + composites, + type_values, + frame_system, + frame_support, + dev_mode, + }; + + def.check_instance_usage()?; + def.check_event_usage()?; + + Ok(def) + } + + /// Performs extra logic checks necessary for the `#[pallet::tasks_experimental]` feature. + fn resolve_tasks( + item_span: &proc_macro2::Span, + tasks: &mut Option, + task_enum: &mut Option, + items: &mut Vec, + ) -> syn::Result<()> { + // fallback for manual (without macros) definition of tasks impl + Self::resolve_manual_tasks_impl(tasks, task_enum, items)?; + + // fallback for manual (without macros) definition of task enum + Self::resolve_manual_task_enum(tasks, task_enum, items)?; + + // ensure that if `task_enum` is specified, `tasks` is also specified + match (&task_enum, &tasks) { + (Some(_), None) => { + return Err(syn::Error::new( + *item_span, + "Missing `#[pallet::tasks_experimental]` impl", + )) + } + (None, Some(tasks)) => { + if tasks.tasks_attr.is_none() { + return Err(syn::Error::new( tasks.item_impl.impl_token.span(), "A `#[pallet::tasks_experimental]` attribute must be attached to your `Task` impl if the \ task enum has been omitted", - )) - } else { - }, - _ => (), - } - - Ok(()) - } - - /// Tries to locate task enum based on the tasks impl target if attribute is not specified - /// but impl is present. If one is found, `task_enum` is set appropriately. - fn resolve_manual_task_enum( - tasks: &Option, - task_enum: &mut Option, - items: &mut Vec, - ) -> syn::Result<()> { - let (None, Some(tasks)) = (&task_enum, &tasks) else { return Ok(()) }; - let syn::Type::Path(type_path) = &*tasks.item_impl.self_ty else { return Ok(()) }; - let type_path = type_path.path.segments.iter().collect::>(); - let (Some(seg), None) = (type_path.get(0), type_path.get(1)) else { return Ok(()) }; - let mut result = None; - for item in items { - let syn::Item::Enum(item_enum) = item else { continue }; - if item_enum.ident == seg.ident { - result = Some(syn::parse2::(item_enum.to_token_stream())?); - // replace item with a no-op because it will be handled by the expansion of - // `task_enum`. We use a no-op instead of simply removing it from the vec - // so that any indices collected by `Def::try_from` remain accurate - *item = syn::Item::Verbatim(quote::quote!()); - break - } - } - *task_enum = result; - Ok(()) - } - - /// Tries to locate a manual tasks impl (an impl implementing a trait whose last path segment is - /// `Task`) in the event that one has not been found already via the attribute macro - pub fn resolve_manual_tasks_impl( - tasks: &mut Option, - task_enum: &Option, - items: &Vec, - ) -> syn::Result<()> { - let None = tasks else { return Ok(()) }; - let mut result = None; - for item in items { - let syn::Item::Impl(item_impl) = item else { continue }; - let Some((_, path, _)) = &item_impl.trait_ else { continue }; - let Some(trait_last_seg) = path.segments.last() else { continue }; - let syn::Type::Path(target_path) = &*item_impl.self_ty else { continue }; - let target_path = target_path.path.segments.iter().collect::>(); - let (Some(target_ident), None) = (target_path.get(0), target_path.get(1)) else { - continue - }; - let matches_task_enum = match task_enum { - Some(task_enum) => task_enum.item_enum.ident == target_ident.ident, - None => true, - }; - if trait_last_seg.ident == "Task" && matches_task_enum { - result = Some(syn::parse2::(item_impl.to_token_stream())?); - break - } - } - *tasks = result; - Ok(()) - } - - /// Check that usage of trait `Event` is consistent with the definition, i.e. it is declared - /// and trait defines type RuntimeEvent, or not declared and no trait associated type. - fn check_event_usage(&self) -> syn::Result<()> { - match (self.config.has_event_type, self.event.is_some()) { - (true, false) => { - let msg = "Invalid usage of RuntimeEvent, `Config` contains associated type `RuntimeEvent`, \ + )); + } else { + } + } + _ => (), + } + + Ok(()) + } + + /// Tries to locate task enum based on the tasks impl target if attribute is not specified + /// but impl is present. If one is found, `task_enum` is set appropriately. + fn resolve_manual_task_enum( + tasks: &Option, + task_enum: &mut Option, + items: &mut Vec, + ) -> syn::Result<()> { + let (None, Some(tasks)) = (&task_enum, &tasks) else { + return Ok(()); + }; + let syn::Type::Path(type_path) = &*tasks.item_impl.self_ty else { + return Ok(()); + }; + let type_path = type_path.path.segments.iter().collect::>(); + let (Some(seg), None) = (type_path.get(0), type_path.get(1)) else { + return Ok(()); + }; + let mut result = None; + for item in items { + let syn::Item::Enum(item_enum) = item else { + continue; + }; + if item_enum.ident == seg.ident { + result = Some(syn::parse2::( + item_enum.to_token_stream(), + )?); + // replace item with a no-op because it will be handled by the expansion of + // `task_enum`. We use a no-op instead of simply removing it from the vec + // so that any indices collected by `Def::try_from` remain accurate + *item = syn::Item::Verbatim(quote::quote!()); + break; + } + } + *task_enum = result; + Ok(()) + } + + /// Tries to locate a manual tasks impl (an impl implementing a trait whose last path segment is + /// `Task`) in the event that one has not been found already via the attribute macro + pub fn resolve_manual_tasks_impl( + tasks: &mut Option, + task_enum: &Option, + items: &Vec, + ) -> syn::Result<()> { + let None = tasks else { return Ok(()) }; + let mut result = None; + for item in items { + let syn::Item::Impl(item_impl) = item else { + continue; + }; + let Some((_, path, _)) = &item_impl.trait_ else { + continue; + }; + let Some(trait_last_seg) = path.segments.last() else { + continue; + }; + let syn::Type::Path(target_path) = &*item_impl.self_ty else { + continue; + }; + let target_path = target_path.path.segments.iter().collect::>(); + let (Some(target_ident), None) = (target_path.get(0), target_path.get(1)) else { + continue; + }; + let matches_task_enum = match task_enum { + Some(task_enum) => task_enum.item_enum.ident == target_ident.ident, + None => true, + }; + if trait_last_seg.ident == "Task" && matches_task_enum { + result = Some(syn::parse2::(item_impl.to_token_stream())?); + break; + } + } + *tasks = result; + Ok(()) + } + + /// Check that usage of trait `Event` is consistent with the definition, i.e. it is declared + /// and trait defines type RuntimeEvent, or not declared and no trait associated type. + fn check_event_usage(&self) -> syn::Result<()> { + match (self.config.has_event_type, self.event.is_some()) { + (true, false) => { + let msg = "Invalid usage of RuntimeEvent, `Config` contains associated type `RuntimeEvent`, \ but enum `Event` is not declared (i.e. no use of `#[pallet::event]`). \ Note that type `RuntimeEvent` in trait is reserved to work alongside pallet event."; - Err(syn::Error::new(proc_macro2::Span::call_site(), msg)) - }, - (false, true) => { - let msg = "Invalid usage of RuntimeEvent, `Config` contains no associated type \ + Err(syn::Error::new(proc_macro2::Span::call_site(), msg)) + } + (false, true) => { + let msg = "Invalid usage of RuntimeEvent, `Config` contains no associated type \ `RuntimeEvent`, but enum `Event` is declared (in use of `#[pallet::event]`). \ An RuntimeEvent associated type must be declare on trait `Config`."; - Err(syn::Error::new(proc_macro2::Span::call_site(), msg)) - }, - _ => Ok(()), - } - } - - /// Check that usage of trait `Config` is consistent with the definition, i.e. it is used with - /// instance iff it is defined with instance. - fn check_instance_usage(&self) -> syn::Result<()> { - let mut instances = vec![]; - instances.extend_from_slice(&self.pallet_struct.instances[..]); - instances.extend(&mut self.storages.iter().flat_map(|s| s.instances.clone())); - if let Some(call) = &self.call { - instances.extend_from_slice(&call.instances[..]); - } - if let Some(hooks) = &self.hooks { - instances.extend_from_slice(&hooks.instances[..]); - } - if let Some(event) = &self.event { - instances.extend_from_slice(&event.instances[..]); - } - if let Some(error) = &self.error { - instances.extend_from_slice(&error.instances[..]); - } - if let Some(inherent) = &self.inherent { - instances.extend_from_slice(&inherent.instances[..]); - } - if let Some(origin) = &self.origin { - instances.extend_from_slice(&origin.instances[..]); - } - if let Some(genesis_config) = &self.genesis_config { - instances.extend_from_slice(&genesis_config.instances[..]); - } - if let Some(genesis_build) = &self.genesis_build { - genesis_build.instances.as_ref().map(|i| instances.extend_from_slice(&i)); - } - if let Some(extra_constants) = &self.extra_constants { - instances.extend_from_slice(&extra_constants.instances[..]); - } - - let mut errors = instances.into_iter().filter_map(|instances| { - if instances.has_instance == self.config.has_instance { - return None - } - let msg = if self.config.has_instance { - "Invalid generic declaration, trait is defined with instance but generic use none" - } else { - "Invalid generic declaration, trait is defined without instance but generic use \ + Err(syn::Error::new(proc_macro2::Span::call_site(), msg)) + } + _ => Ok(()), + } + } + + /// Check that usage of trait `Config` is consistent with the definition, i.e. it is used with + /// instance iff it is defined with instance. + fn check_instance_usage(&self) -> syn::Result<()> { + let mut instances = vec![]; + instances.extend_from_slice(&self.pallet_struct.instances[..]); + instances.extend(&mut self.storages.iter().flat_map(|s| s.instances.clone())); + if let Some(call) = &self.call { + instances.extend_from_slice(&call.instances[..]); + } + if let Some(hooks) = &self.hooks { + instances.extend_from_slice(&hooks.instances[..]); + } + if let Some(event) = &self.event { + instances.extend_from_slice(&event.instances[..]); + } + if let Some(error) = &self.error { + instances.extend_from_slice(&error.instances[..]); + } + if let Some(inherent) = &self.inherent { + instances.extend_from_slice(&inherent.instances[..]); + } + if let Some(origin) = &self.origin { + instances.extend_from_slice(&origin.instances[..]); + } + if let Some(genesis_config) = &self.genesis_config { + instances.extend_from_slice(&genesis_config.instances[..]); + } + if let Some(genesis_build) = &self.genesis_build { + genesis_build + .instances + .as_ref() + .map(|i| instances.extend_from_slice(&i)); + } + if let Some(extra_constants) = &self.extra_constants { + instances.extend_from_slice(&extra_constants.instances[..]); + } + + let mut errors = instances.into_iter().filter_map(|instances| { + if instances.has_instance == self.config.has_instance { + return None; + } + let msg = if self.config.has_instance { + "Invalid generic declaration, trait is defined with instance but generic use none" + } else { + "Invalid generic declaration, trait is defined without instance but generic use \ some" - }; - Some(syn::Error::new(instances.span, msg)) - }); - - if let Some(mut first_error) = errors.next() { - for error in errors { - first_error.combine(error) - } - Err(first_error) - } else { - Ok(()) - } - } - - /// Depending on if pallet is instantiable: - /// * either `T: Config` - /// * or `T: Config, I: 'static` - pub fn type_impl_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { - if self.config.has_instance { - quote::quote_spanned!(span => T: Config, I: 'static) - } else { - quote::quote_spanned!(span => T: Config) - } - } - - /// Depending on if pallet is instantiable: - /// * either `T: Config` - /// * or `T: Config, I: 'static = ()` - pub fn type_decl_bounded_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { - if self.config.has_instance { - quote::quote_spanned!(span => T: Config, I: 'static = ()) - } else { - quote::quote_spanned!(span => T: Config) - } - } - - /// Depending on if pallet is instantiable: - /// * either `T` - /// * or `T, I = ()` - pub fn type_decl_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { - if self.config.has_instance { - quote::quote_spanned!(span => T, I = ()) - } else { - quote::quote_spanned!(span => T) - } - } - - /// Depending on if pallet is instantiable: - /// * either `` - /// * or `` - /// to be used when using pallet trait `Config` - pub fn trait_use_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { - if self.config.has_instance { - quote::quote_spanned!(span => ) - } else { - quote::quote_spanned!(span => ) - } - } - - /// Depending on if pallet is instantiable: - /// * either `T` - /// * or `T, I` - pub fn type_use_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { - if self.config.has_instance { - quote::quote_spanned!(span => T, I) - } else { - quote::quote_spanned!(span => T) - } - } + }; + Some(syn::Error::new(instances.span, msg)) + }); + + if let Some(mut first_error) = errors.next() { + for error in errors { + first_error.combine(error) + } + Err(first_error) + } else { + Ok(()) + } + } + + /// Depending on if pallet is instantiable: + /// * either `T: Config` + /// * or `T: Config, I: 'static` + pub fn type_impl_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { + if self.config.has_instance { + quote::quote_spanned!(span => T: Config, I: 'static) + } else { + quote::quote_spanned!(span => T: Config) + } + } + + /// Depending on if pallet is instantiable: + /// * either `T: Config` + /// * or `T: Config, I: 'static = ()` + pub fn type_decl_bounded_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { + if self.config.has_instance { + quote::quote_spanned!(span => T: Config, I: 'static = ()) + } else { + quote::quote_spanned!(span => T: Config) + } + } + + /// Depending on if pallet is instantiable: + /// * either `T` + /// * or `T, I = ()` + pub fn type_decl_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { + if self.config.has_instance { + quote::quote_spanned!(span => T, I = ()) + } else { + quote::quote_spanned!(span => T) + } + } + + /// Depending on if pallet is instantiable: + /// * either `` + /// * or `` + /// to be used when using pallet trait `Config` + pub fn trait_use_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { + if self.config.has_instance { + quote::quote_spanned!(span => ) + } else { + quote::quote_spanned!(span => ) + } + } + + /// Depending on if pallet is instantiable: + /// * either `T` + /// * or `T, I` + pub fn type_use_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { + if self.config.has_instance { + quote::quote_spanned!(span => T, I) + } else { + quote::quote_spanned!(span => T) + } + } } /// Some generic kind for type which can be not generic, or generic over config, /// or generic over config and instance, but not generic only over instance. pub enum GenericKind { - None, - Config, - ConfigAndInstance, + None, + Config, + ConfigAndInstance, } impl GenericKind { - /// Return Err if it is only generics over instance but not over config. - pub fn from_gens(has_config: bool, has_instance: bool) -> Result { - match (has_config, has_instance) { - (false, false) => Ok(GenericKind::None), - (true, false) => Ok(GenericKind::Config), - (true, true) => Ok(GenericKind::ConfigAndInstance), - (false, true) => Err(()), - } - } - - /// Return the generic to be used when using the type. - /// - /// Depending on its definition it can be: ``, `T` or `T, I` - pub fn type_use_gen(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { - match self { - GenericKind::None => quote::quote!(), - GenericKind::Config => quote::quote_spanned!(span => T), - GenericKind::ConfigAndInstance => quote::quote_spanned!(span => T, I), - } - } - - /// Return the generic to be used in `impl<..>` when implementing on the type. - pub fn type_impl_gen(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { - match self { - GenericKind::None => quote::quote!(), - GenericKind::Config => quote::quote_spanned!(span => T: Config), - GenericKind::ConfigAndInstance => { - quote::quote_spanned!(span => T: Config, I: 'static) - }, - } - } - - /// Return whereas the type has some generic. - pub fn is_generic(&self) -> bool { - match self { - GenericKind::None => false, - GenericKind::Config | GenericKind::ConfigAndInstance => true, - } - } + /// Return Err if it is only generics over instance but not over config. + pub fn from_gens(has_config: bool, has_instance: bool) -> Result { + match (has_config, has_instance) { + (false, false) => Ok(GenericKind::None), + (true, false) => Ok(GenericKind::Config), + (true, true) => Ok(GenericKind::ConfigAndInstance), + (false, true) => Err(()), + } + } + + /// Return the generic to be used when using the type. + /// + /// Depending on its definition it can be: ``, `T` or `T, I` + pub fn type_use_gen(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { + match self { + GenericKind::None => quote::quote!(), + GenericKind::Config => quote::quote_spanned!(span => T), + GenericKind::ConfigAndInstance => quote::quote_spanned!(span => T, I), + } + } + + /// Return the generic to be used in `impl<..>` when implementing on the type. + pub fn type_impl_gen(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { + match self { + GenericKind::None => quote::quote!(), + GenericKind::Config => quote::quote_spanned!(span => T: Config), + GenericKind::ConfigAndInstance => { + quote::quote_spanned!(span => T: Config, I: 'static) + } + } + } + + /// Return whereas the type has some generic. + pub fn is_generic(&self) -> bool { + match self { + GenericKind::None => false, + GenericKind::Config | GenericKind::ConfigAndInstance => true, + } + } } /// List of additional token to be used for parsing. mod keyword { - syn::custom_keyword!(origin); - syn::custom_keyword!(call); - syn::custom_keyword!(tasks_experimental); - syn::custom_keyword!(task_enum); - syn::custom_keyword!(task_list); - syn::custom_keyword!(task_condition); - syn::custom_keyword!(task_index); - syn::custom_keyword!(weight); - syn::custom_keyword!(event); - syn::custom_keyword!(config); - syn::custom_keyword!(with_default); - syn::custom_keyword!(hooks); - syn::custom_keyword!(inherent); - syn::custom_keyword!(error); - syn::custom_keyword!(storage); - syn::custom_keyword!(genesis_build); - syn::custom_keyword!(genesis_config); - syn::custom_keyword!(validate_unsigned); - syn::custom_keyword!(type_value); - syn::custom_keyword!(pallet); - syn::custom_keyword!(extra_constants); - syn::custom_keyword!(composite_enum); + syn::custom_keyword!(origin); + syn::custom_keyword!(call); + syn::custom_keyword!(tasks_experimental); + syn::custom_keyword!(task_enum); + syn::custom_keyword!(task_list); + syn::custom_keyword!(task_condition); + syn::custom_keyword!(task_index); + syn::custom_keyword!(weight); + syn::custom_keyword!(event); + syn::custom_keyword!(config); + syn::custom_keyword!(with_default); + syn::custom_keyword!(hooks); + syn::custom_keyword!(inherent); + syn::custom_keyword!(error); + syn::custom_keyword!(storage); + syn::custom_keyword!(genesis_build); + syn::custom_keyword!(genesis_config); + syn::custom_keyword!(validate_unsigned); + syn::custom_keyword!(type_value); + syn::custom_keyword!(pallet); + syn::custom_keyword!(extra_constants); + syn::custom_keyword!(composite_enum); } /// Parse attributes for item in pallet module /// syntax must be `pallet::` (e.g. `#[pallet::config]`) enum PalletAttr { - Config(proc_macro2::Span, bool), - Pallet(proc_macro2::Span), - Hooks(proc_macro2::Span), - /// A `#[pallet::call]` with optional attributes to specialize the behaviour. - /// - /// # Attributes - /// - /// Each attribute `attr` can take the form of `#[pallet::call(attr = …)]` or - /// `#[pallet::call(attr(…))]`. The possible attributes are: - /// - /// ## `weight` - /// - /// Can be used to reduce the repetitive weight annotation in the trivial case. It accepts one - /// argument that is expected to be an implementation of the `WeightInfo` or something that - /// behaves syntactically equivalent. This allows to annotate a `WeightInfo` for all the calls. - /// Now each call does not need to specify its own `#[pallet::weight]` but can instead use the - /// one from the `#[pallet::call]` definition. So instead of having to write it on each call: - /// - /// ```ignore - /// #[pallet::call] - /// impl Pallet { - /// #[pallet::weight(T::WeightInfo::create())] - /// pub fn create( - /// ``` - /// you can now omit it on the call itself, if the name of the weigh function matches the call: - /// - /// ```ignore - /// #[pallet::call(weight = ::WeightInfo)] - /// impl Pallet { - /// pub fn create( - /// ``` - /// - /// It is possible to use this syntax together with instantiated pallets by using `Config` - /// instead. - /// - /// ### Dev Mode - /// - /// Normally the `dev_mode` sets all weights of calls without a `#[pallet::weight]` annotation - /// to zero. Now when there is a `weight` attribute on the `#[pallet::call]`, then that is used - /// instead of the zero weight. So to say: it works together with `dev_mode`. - RuntimeCall(Option, proc_macro2::Span), - Error(proc_macro2::Span), - Tasks(proc_macro2::Span), - TaskList(proc_macro2::Span), - TaskCondition(proc_macro2::Span), - TaskIndex(proc_macro2::Span), - RuntimeTask(proc_macro2::Span), - RuntimeEvent(proc_macro2::Span), - RuntimeOrigin(proc_macro2::Span), - Inherent(proc_macro2::Span), - Storage(proc_macro2::Span), - GenesisConfig(proc_macro2::Span), - GenesisBuild(proc_macro2::Span), - ValidateUnsigned(proc_macro2::Span), - TypeValue(proc_macro2::Span), - ExtraConstants(proc_macro2::Span), - Composite(proc_macro2::Span), + Config(proc_macro2::Span, bool), + Pallet(proc_macro2::Span), + Hooks(proc_macro2::Span), + /// A `#[pallet::call]` with optional attributes to specialize the behaviour. + /// + /// # Attributes + /// + /// Each attribute `attr` can take the form of `#[pallet::call(attr = …)]` or + /// `#[pallet::call(attr(…))]`. The possible attributes are: + /// + /// ## `weight` + /// + /// Can be used to reduce the repetitive weight annotation in the trivial case. It accepts one + /// argument that is expected to be an implementation of the `WeightInfo` or something that + /// behaves syntactically equivalent. This allows to annotate a `WeightInfo` for all the calls. + /// Now each call does not need to specify its own `#[pallet::weight]` but can instead use the + /// one from the `#[pallet::call]` definition. So instead of having to write it on each call: + /// + /// ```ignore + /// #[pallet::call] + /// impl Pallet { + /// #[pallet::weight(T::WeightInfo::create())] + /// pub fn create( + /// ``` + /// you can now omit it on the call itself, if the name of the weigh function matches the call: + /// + /// ```ignore + /// #[pallet::call(weight = ::WeightInfo)] + /// impl Pallet { + /// pub fn create( + /// ``` + /// + /// It is possible to use this syntax together with instantiated pallets by using `Config` + /// instead. + /// + /// ### Dev Mode + /// + /// Normally the `dev_mode` sets all weights of calls without a `#[pallet::weight]` annotation + /// to zero. Now when there is a `weight` attribute on the `#[pallet::call]`, then that is used + /// instead of the zero weight. So to say: it works together with `dev_mode`. + RuntimeCall(Option, proc_macro2::Span), + Error(proc_macro2::Span), + Tasks(proc_macro2::Span), + TaskList(proc_macro2::Span), + TaskCondition(proc_macro2::Span), + TaskIndex(proc_macro2::Span), + RuntimeTask(proc_macro2::Span), + RuntimeEvent(proc_macro2::Span), + RuntimeOrigin(proc_macro2::Span), + Inherent(proc_macro2::Span), + Storage(proc_macro2::Span), + GenesisConfig(proc_macro2::Span), + GenesisBuild(proc_macro2::Span), + ValidateUnsigned(proc_macro2::Span), + TypeValue(proc_macro2::Span), + ExtraConstants(proc_macro2::Span), + Composite(proc_macro2::Span), } impl PalletAttr { - fn span(&self) -> proc_macro2::Span { - match self { - Self::Config(span, _) => *span, - Self::Pallet(span) => *span, - Self::Hooks(span) => *span, - Self::Tasks(span) => *span, - Self::TaskCondition(span) => *span, - Self::TaskIndex(span) => *span, - Self::TaskList(span) => *span, - Self::Error(span) => *span, - Self::RuntimeTask(span) => *span, - Self::RuntimeCall(_, span) => *span, - Self::RuntimeEvent(span) => *span, - Self::RuntimeOrigin(span) => *span, - Self::Inherent(span) => *span, - Self::Storage(span) => *span, - Self::GenesisConfig(span) => *span, - Self::GenesisBuild(span) => *span, - Self::ValidateUnsigned(span) => *span, - Self::TypeValue(span) => *span, - Self::ExtraConstants(span) => *span, - Self::Composite(span) => *span, - } - } + fn span(&self) -> proc_macro2::Span { + match self { + Self::Config(span, _) => *span, + Self::Pallet(span) => *span, + Self::Hooks(span) => *span, + Self::Tasks(span) => *span, + Self::TaskCondition(span) => *span, + Self::TaskIndex(span) => *span, + Self::TaskList(span) => *span, + Self::Error(span) => *span, + Self::RuntimeTask(span) => *span, + Self::RuntimeCall(_, span) => *span, + Self::RuntimeEvent(span) => *span, + Self::RuntimeOrigin(span) => *span, + Self::Inherent(span) => *span, + Self::Storage(span) => *span, + Self::GenesisConfig(span) => *span, + Self::GenesisBuild(span) => *span, + Self::ValidateUnsigned(span) => *span, + Self::TypeValue(span) => *span, + Self::ExtraConstants(span) => *span, + Self::Composite(span) => *span, + } + } } impl syn::parse::Parse for PalletAttr { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - let content; - syn::bracketed!(content in input); - content.parse::()?; - content.parse::()?; - - let lookahead = content.lookahead1(); - if lookahead.peek(keyword::config) { - let span = content.parse::()?.span(); - let with_default = content.peek(syn::token::Paren); - if with_default { - let inside_config; - let _paren = syn::parenthesized!(inside_config in content); - inside_config.parse::()?; - } - Ok(PalletAttr::Config(span, with_default)) - } else if lookahead.peek(keyword::pallet) { - Ok(PalletAttr::Pallet(content.parse::()?.span())) - } else if lookahead.peek(keyword::hooks) { - Ok(PalletAttr::Hooks(content.parse::()?.span())) - } else if lookahead.peek(keyword::call) { - let span = content.parse::().expect("peeked").span(); - let attr = match content.is_empty() { - true => None, - false => Some(InheritedCallWeightAttr::parse(&content)?), - }; - Ok(PalletAttr::RuntimeCall(attr, span)) - } else if lookahead.peek(keyword::tasks_experimental) { - Ok(PalletAttr::Tasks(content.parse::()?.span())) - } else if lookahead.peek(keyword::task_enum) { - Ok(PalletAttr::RuntimeTask(content.parse::()?.span())) - } else if lookahead.peek(keyword::task_condition) { - Ok(PalletAttr::TaskCondition(content.parse::()?.span())) - } else if lookahead.peek(keyword::task_index) { - Ok(PalletAttr::TaskIndex(content.parse::()?.span())) - } else if lookahead.peek(keyword::task_list) { - Ok(PalletAttr::TaskList(content.parse::()?.span())) - } else if lookahead.peek(keyword::error) { - Ok(PalletAttr::Error(content.parse::()?.span())) - } else if lookahead.peek(keyword::event) { - Ok(PalletAttr::RuntimeEvent(content.parse::()?.span())) - } else if lookahead.peek(keyword::origin) { - Ok(PalletAttr::RuntimeOrigin(content.parse::()?.span())) - } else if lookahead.peek(keyword::inherent) { - Ok(PalletAttr::Inherent(content.parse::()?.span())) - } else if lookahead.peek(keyword::storage) { - Ok(PalletAttr::Storage(content.parse::()?.span())) - } else if lookahead.peek(keyword::genesis_config) { - Ok(PalletAttr::GenesisConfig(content.parse::()?.span())) - } else if lookahead.peek(keyword::genesis_build) { - Ok(PalletAttr::GenesisBuild(content.parse::()?.span())) - } else if lookahead.peek(keyword::validate_unsigned) { - Ok(PalletAttr::ValidateUnsigned(content.parse::()?.span())) - } else if lookahead.peek(keyword::type_value) { - Ok(PalletAttr::TypeValue(content.parse::()?.span())) - } else if lookahead.peek(keyword::extra_constants) { - Ok(PalletAttr::ExtraConstants(content.parse::()?.span())) - } else if lookahead.peek(keyword::composite_enum) { - Ok(PalletAttr::Composite(content.parse::()?.span())) - } else { - Err(lookahead.error()) - } - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + let lookahead = content.lookahead1(); + if lookahead.peek(keyword::config) { + let span = content.parse::()?.span(); + let with_default = content.peek(syn::token::Paren); + if with_default { + let inside_config; + let _paren = syn::parenthesized!(inside_config in content); + inside_config.parse::()?; + } + Ok(PalletAttr::Config(span, with_default)) + } else if lookahead.peek(keyword::pallet) { + Ok(PalletAttr::Pallet( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::hooks) { + Ok(PalletAttr::Hooks(content.parse::()?.span())) + } else if lookahead.peek(keyword::call) { + let span = content.parse::().expect("peeked").span(); + let attr = match content.is_empty() { + true => None, + false => Some(InheritedCallWeightAttr::parse(&content)?), + }; + Ok(PalletAttr::RuntimeCall(attr, span)) + } else if lookahead.peek(keyword::tasks_experimental) { + Ok(PalletAttr::Tasks( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::task_enum) { + Ok(PalletAttr::RuntimeTask( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::task_condition) { + Ok(PalletAttr::TaskCondition( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::task_index) { + Ok(PalletAttr::TaskIndex( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::task_list) { + Ok(PalletAttr::TaskList( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::error) { + Ok(PalletAttr::Error(content.parse::()?.span())) + } else if lookahead.peek(keyword::event) { + Ok(PalletAttr::RuntimeEvent( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::origin) { + Ok(PalletAttr::RuntimeOrigin( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::inherent) { + Ok(PalletAttr::Inherent( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::storage) { + Ok(PalletAttr::Storage( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::genesis_config) { + Ok(PalletAttr::GenesisConfig( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::genesis_build) { + Ok(PalletAttr::GenesisBuild( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::validate_unsigned) { + Ok(PalletAttr::ValidateUnsigned( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::type_value) { + Ok(PalletAttr::TypeValue( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::extra_constants) { + Ok(PalletAttr::ExtraConstants( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::composite_enum) { + Ok(PalletAttr::Composite( + content.parse::()?.span(), + )) + } else { + Err(lookahead.error()) + } + } } /// The optional weight annotation on a `#[pallet::call]` like `#[pallet::call(weight($type))]`. #[derive(Clone)] pub struct InheritedCallWeightAttr { - pub typename: syn::Type, - pub span: proc_macro2::Span, + pub typename: syn::Type, + pub span: proc_macro2::Span, } impl syn::parse::Parse for InheritedCallWeightAttr { - // Parses `(weight($type))` or `(weight = $type)`. - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let content; - syn::parenthesized!(content in input); - content.parse::()?; - let lookahead = content.lookahead1(); - - let buffer = if lookahead.peek(syn::token::Paren) { - let inner; - syn::parenthesized!(inner in content); - inner - } else if lookahead.peek(syn::Token![=]) { - content.parse::().expect("peeked"); - content - } else { - return Err(lookahead.error()) - }; - - Ok(Self { typename: buffer.parse()?, span: input.span() }) - } + // Parses `(weight($type))` or `(weight = $type)`. + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let content; + syn::parenthesized!(content in input); + content.parse::()?; + let lookahead = content.lookahead1(); + + let buffer = if lookahead.peek(syn::token::Paren) { + let inner; + syn::parenthesized!(inner in content); + inner + } else if lookahead.peek(syn::Token![=]) { + content.parse::().expect("peeked"); + content + } else { + return Err(lookahead.error()); + }; + + Ok(Self { + typename: buffer.parse()?, + span: input.span(), + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/origin.rs b/support/procedural-fork/src/pallet/parse/origin.rs index 76e2a8841..2dd84c40d 100644 --- a/support/procedural-fork/src/pallet/parse/origin.rs +++ b/support/procedural-fork/src/pallet/parse/origin.rs @@ -25,48 +25,56 @@ use syn::spanned::Spanned; /// * `struct Origin` /// * `enum Origin` pub struct OriginDef { - /// The index of item in pallet module. - pub index: usize, - pub has_instance: bool, - pub is_generic: bool, - /// A set of usage of instance, must be check for consistency with trait. - pub instances: Vec, + /// The index of item in pallet module. + pub index: usize, + pub has_instance: bool, + pub is_generic: bool, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Vec, } impl OriginDef { - pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { - let item_span = item.span(); - let (vis, ident, generics) = match &item { - syn::Item::Enum(item) => (&item.vis, &item.ident, &item.generics), - syn::Item::Struct(item) => (&item.vis, &item.ident, &item.generics), - syn::Item::Type(item) => (&item.vis, &item.ident, &item.generics), - _ => { - let msg = "Invalid pallet::origin, expected enum or struct or type"; - return Err(syn::Error::new(item.span(), msg)) - }, - }; + pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { + let item_span = item.span(); + let (vis, ident, generics) = match &item { + syn::Item::Enum(item) => (&item.vis, &item.ident, &item.generics), + syn::Item::Struct(item) => (&item.vis, &item.ident, &item.generics), + syn::Item::Type(item) => (&item.vis, &item.ident, &item.generics), + _ => { + let msg = "Invalid pallet::origin, expected enum or struct or type"; + return Err(syn::Error::new(item.span(), msg)); + } + }; - let has_instance = generics.params.len() == 2; - let is_generic = !generics.params.is_empty(); + let has_instance = generics.params.len() == 2; + let is_generic = !generics.params.is_empty(); - let mut instances = vec![]; - if let Some(u) = helper::check_type_def_optional_gen(generics, item.span())? { - instances.push(u); - } else { - // construct_runtime only allow generic event for instantiable pallet. - instances.push(helper::InstanceUsage { has_instance: false, span: ident.span() }) - } + let mut instances = vec![]; + if let Some(u) = helper::check_type_def_optional_gen(generics, item.span())? { + instances.push(u); + } else { + // construct_runtime only allow generic event for instantiable pallet. + instances.push(helper::InstanceUsage { + has_instance: false, + span: ident.span(), + }) + } - if !matches!(vis, syn::Visibility::Public(_)) { - let msg = "Invalid pallet::origin, Origin must be public"; - return Err(syn::Error::new(item_span, msg)) - } + if !matches!(vis, syn::Visibility::Public(_)) { + let msg = "Invalid pallet::origin, Origin must be public"; + return Err(syn::Error::new(item_span, msg)); + } - if ident != "Origin" { - let msg = "Invalid pallet::origin, ident must `Origin`"; - return Err(syn::Error::new(ident.span(), msg)) - } + if ident != "Origin" { + let msg = "Invalid pallet::origin, ident must `Origin`"; + return Err(syn::Error::new(ident.span(), msg)); + } - Ok(OriginDef { index, has_instance, is_generic, instances }) - } + Ok(OriginDef { + index, + has_instance, + is_generic, + instances, + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/pallet_struct.rs b/support/procedural-fork/src/pallet/parse/pallet_struct.rs index b64576099..320cf01fa 100644 --- a/support/procedural-fork/src/pallet/parse/pallet_struct.rs +++ b/support/procedural-fork/src/pallet/parse/pallet_struct.rs @@ -21,129 +21,137 @@ use syn::spanned::Spanned; /// List of additional token to be used for parsing. mod keyword { - syn::custom_keyword!(pallet); - syn::custom_keyword!(Pallet); - syn::custom_keyword!(without_storage_info); - syn::custom_keyword!(storage_version); + syn::custom_keyword!(pallet); + syn::custom_keyword!(Pallet); + syn::custom_keyword!(without_storage_info); + syn::custom_keyword!(storage_version); } /// Definition of the pallet pallet. pub struct PalletStructDef { - /// The index of item in pallet pallet. - pub index: usize, - /// A set of usage of instance, must be check for consistency with config trait. - pub instances: Vec, - /// The keyword Pallet used (contains span). - pub pallet: keyword::Pallet, - /// The span of the pallet::pallet attribute. - pub attr_span: proc_macro2::Span, - /// Whether to specify the storages max encoded len when implementing `StorageInfoTrait`. - /// Contains the span of the attribute. - pub without_storage_info: Option, - /// The in-code storage version of the pallet. - pub storage_version: Option, + /// The index of item in pallet pallet. + pub index: usize, + /// A set of usage of instance, must be check for consistency with config trait. + pub instances: Vec, + /// The keyword Pallet used (contains span). + pub pallet: keyword::Pallet, + /// The span of the pallet::pallet attribute. + pub attr_span: proc_macro2::Span, + /// Whether to specify the storages max encoded len when implementing `StorageInfoTrait`. + /// Contains the span of the attribute. + pub without_storage_info: Option, + /// The in-code storage version of the pallet. + pub storage_version: Option, } /// Parse for one variant of: /// * `#[pallet::without_storage_info]` /// * `#[pallet::storage_version(STORAGE_VERSION)]` pub enum PalletStructAttr { - WithoutStorageInfoTrait(proc_macro2::Span), - StorageVersion { storage_version: syn::Path, span: proc_macro2::Span }, + WithoutStorageInfoTrait(proc_macro2::Span), + StorageVersion { + storage_version: syn::Path, + span: proc_macro2::Span, + }, } impl PalletStructAttr { - fn span(&self) -> proc_macro2::Span { - match self { - Self::WithoutStorageInfoTrait(span) | Self::StorageVersion { span, .. } => *span, - } - } + fn span(&self) -> proc_macro2::Span { + match self { + Self::WithoutStorageInfoTrait(span) | Self::StorageVersion { span, .. } => *span, + } + } } impl syn::parse::Parse for PalletStructAttr { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - let content; - syn::bracketed!(content in input); - content.parse::()?; - content.parse::()?; - - let lookahead = content.lookahead1(); - if lookahead.peek(keyword::without_storage_info) { - let span = content.parse::()?.span(); - Ok(Self::WithoutStorageInfoTrait(span)) - } else if lookahead.peek(keyword::storage_version) { - let span = content.parse::()?.span(); - - let version_content; - syn::parenthesized!(version_content in content); - let storage_version = version_content.parse::()?; - - Ok(Self::StorageVersion { storage_version, span }) - } else { - Err(lookahead.error()) - } - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + let lookahead = content.lookahead1(); + if lookahead.peek(keyword::without_storage_info) { + let span = content.parse::()?.span(); + Ok(Self::WithoutStorageInfoTrait(span)) + } else if lookahead.peek(keyword::storage_version) { + let span = content.parse::()?.span(); + + let version_content; + syn::parenthesized!(version_content in content); + let storage_version = version_content.parse::()?; + + Ok(Self::StorageVersion { + storage_version, + span, + }) + } else { + Err(lookahead.error()) + } + } } impl PalletStructDef { - pub fn try_from( - attr_span: proc_macro2::Span, - index: usize, - item: &mut syn::Item, - ) -> syn::Result { - let item = if let syn::Item::Struct(item) = item { - item - } else { - let msg = "Invalid pallet::pallet, expected struct definition"; - return Err(syn::Error::new(item.span(), msg)) - }; - - let mut without_storage_info = None; - let mut storage_version_found = None; - - let struct_attrs: Vec = helper::take_item_pallet_attrs(&mut item.attrs)?; - for attr in struct_attrs { - match attr { - PalletStructAttr::WithoutStorageInfoTrait(span) - if without_storage_info.is_none() => - { - without_storage_info = Some(span); - }, - PalletStructAttr::StorageVersion { storage_version, .. } - if storage_version_found.is_none() => - { - storage_version_found = Some(storage_version); - }, - attr => { - let msg = "Unexpected duplicated attribute"; - return Err(syn::Error::new(attr.span(), msg)) - }, - } - } - - let pallet = syn::parse2::(item.ident.to_token_stream())?; - - if !matches!(item.vis, syn::Visibility::Public(_)) { - let msg = "Invalid pallet::pallet, Pallet must be public"; - return Err(syn::Error::new(item.span(), msg)) - } - - if item.generics.where_clause.is_some() { - let msg = "Invalid pallet::pallet, where clause not supported on Pallet declaration"; - return Err(syn::Error::new(item.generics.where_clause.span(), msg)) - } - - let instances = - vec![helper::check_type_def_gen_no_bounds(&item.generics, item.ident.span())?]; - - Ok(Self { - index, - instances, - pallet, - attr_span, - without_storage_info, - storage_version: storage_version_found, - }) - } + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + ) -> syn::Result { + let item = if let syn::Item::Struct(item) = item { + item + } else { + let msg = "Invalid pallet::pallet, expected struct definition"; + return Err(syn::Error::new(item.span(), msg)); + }; + + let mut without_storage_info = None; + let mut storage_version_found = None; + + let struct_attrs: Vec = helper::take_item_pallet_attrs(&mut item.attrs)?; + for attr in struct_attrs { + match attr { + PalletStructAttr::WithoutStorageInfoTrait(span) + if without_storage_info.is_none() => + { + without_storage_info = Some(span); + } + PalletStructAttr::StorageVersion { + storage_version, .. + } if storage_version_found.is_none() => { + storage_version_found = Some(storage_version); + } + attr => { + let msg = "Unexpected duplicated attribute"; + return Err(syn::Error::new(attr.span(), msg)); + } + } + } + + let pallet = syn::parse2::(item.ident.to_token_stream())?; + + if !matches!(item.vis, syn::Visibility::Public(_)) { + let msg = "Invalid pallet::pallet, Pallet must be public"; + return Err(syn::Error::new(item.span(), msg)); + } + + if item.generics.where_clause.is_some() { + let msg = "Invalid pallet::pallet, where clause not supported on Pallet declaration"; + return Err(syn::Error::new(item.generics.where_clause.span(), msg)); + } + + let instances = vec![helper::check_type_def_gen_no_bounds( + &item.generics, + item.ident.span(), + )?]; + + Ok(Self { + index, + instances, + pallet, + attr_span, + without_storage_info, + storage_version: storage_version_found, + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/storage.rs b/support/procedural-fork/src/pallet/parse/storage.rs index 9d96a18b5..811832427 100644 --- a/support/procedural-fork/src/pallet/parse/storage.rs +++ b/support/procedural-fork/src/pallet/parse/storage.rs @@ -23,16 +23,16 @@ use syn::spanned::Spanned; /// List of additional token to be used for parsing. mod keyword { - syn::custom_keyword!(Error); - syn::custom_keyword!(pallet); - syn::custom_keyword!(getter); - syn::custom_keyword!(storage_prefix); - syn::custom_keyword!(unbounded); - syn::custom_keyword!(whitelist_storage); - syn::custom_keyword!(disable_try_decode_storage); - syn::custom_keyword!(OptionQuery); - syn::custom_keyword!(ResultQuery); - syn::custom_keyword!(ValueQuery); + syn::custom_keyword!(Error); + syn::custom_keyword!(pallet); + syn::custom_keyword!(getter); + syn::custom_keyword!(storage_prefix); + syn::custom_keyword!(unbounded); + syn::custom_keyword!(whitelist_storage); + syn::custom_keyword!(disable_try_decode_storage); + syn::custom_keyword!(OptionQuery); + syn::custom_keyword!(ResultQuery); + syn::custom_keyword!(ValueQuery); } /// Parse for one of the following: @@ -42,906 +42,1003 @@ mod keyword { /// * `#[pallet::whitelist_storage] /// * `#[pallet::disable_try_decode_storage]` pub enum PalletStorageAttr { - Getter(syn::Ident, proc_macro2::Span), - StorageName(syn::LitStr, proc_macro2::Span), - Unbounded(proc_macro2::Span), - WhitelistStorage(proc_macro2::Span), - DisableTryDecodeStorage(proc_macro2::Span), + Getter(syn::Ident, proc_macro2::Span), + StorageName(syn::LitStr, proc_macro2::Span), + Unbounded(proc_macro2::Span), + WhitelistStorage(proc_macro2::Span), + DisableTryDecodeStorage(proc_macro2::Span), } impl PalletStorageAttr { - fn attr_span(&self) -> proc_macro2::Span { - match self { - Self::Getter(_, span) | - Self::StorageName(_, span) | - Self::Unbounded(span) | - Self::WhitelistStorage(span) => *span, - Self::DisableTryDecodeStorage(span) => *span, - } - } + fn attr_span(&self) -> proc_macro2::Span { + match self { + Self::Getter(_, span) + | Self::StorageName(_, span) + | Self::Unbounded(span) + | Self::WhitelistStorage(span) => *span, + Self::DisableTryDecodeStorage(span) => *span, + } + } } impl syn::parse::Parse for PalletStorageAttr { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - let attr_span = input.span(); - let content; - syn::bracketed!(content in input); - content.parse::()?; - content.parse::()?; - - let lookahead = content.lookahead1(); - if lookahead.peek(keyword::getter) { - content.parse::()?; - - let generate_content; - syn::parenthesized!(generate_content in content); - generate_content.parse::()?; - Ok(Self::Getter(generate_content.parse::()?, attr_span)) - } else if lookahead.peek(keyword::storage_prefix) { - content.parse::()?; - content.parse::()?; - - let renamed_prefix = content.parse::()?; - // Ensure the renamed prefix is a proper Rust identifier - syn::parse_str::(&renamed_prefix.value()).map_err(|_| { - let msg = format!("`{}` is not a valid identifier", renamed_prefix.value()); - syn::Error::new(renamed_prefix.span(), msg) - })?; - - Ok(Self::StorageName(renamed_prefix, attr_span)) - } else if lookahead.peek(keyword::unbounded) { - content.parse::()?; - - Ok(Self::Unbounded(attr_span)) - } else if lookahead.peek(keyword::whitelist_storage) { - content.parse::()?; - Ok(Self::WhitelistStorage(attr_span)) - } else if lookahead.peek(keyword::disable_try_decode_storage) { - content.parse::()?; - Ok(Self::DisableTryDecodeStorage(attr_span)) - } else { - Err(lookahead.error()) - } - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let attr_span = input.span(); + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + let lookahead = content.lookahead1(); + if lookahead.peek(keyword::getter) { + content.parse::()?; + + let generate_content; + syn::parenthesized!(generate_content in content); + generate_content.parse::()?; + Ok(Self::Getter( + generate_content.parse::()?, + attr_span, + )) + } else if lookahead.peek(keyword::storage_prefix) { + content.parse::()?; + content.parse::()?; + + let renamed_prefix = content.parse::()?; + // Ensure the renamed prefix is a proper Rust identifier + syn::parse_str::(&renamed_prefix.value()).map_err(|_| { + let msg = format!("`{}` is not a valid identifier", renamed_prefix.value()); + syn::Error::new(renamed_prefix.span(), msg) + })?; + + Ok(Self::StorageName(renamed_prefix, attr_span)) + } else if lookahead.peek(keyword::unbounded) { + content.parse::()?; + + Ok(Self::Unbounded(attr_span)) + } else if lookahead.peek(keyword::whitelist_storage) { + content.parse::()?; + Ok(Self::WhitelistStorage(attr_span)) + } else if lookahead.peek(keyword::disable_try_decode_storage) { + content.parse::()?; + Ok(Self::DisableTryDecodeStorage(attr_span)) + } else { + Err(lookahead.error()) + } + } } struct PalletStorageAttrInfo { - getter: Option, - rename_as: Option, - unbounded: bool, - whitelisted: bool, - try_decode: bool, + getter: Option, + rename_as: Option, + unbounded: bool, + whitelisted: bool, + try_decode: bool, } impl PalletStorageAttrInfo { - fn from_attrs(attrs: Vec) -> syn::Result { - let mut getter = None; - let mut rename_as = None; - let mut unbounded = false; - let mut whitelisted = false; - let mut disable_try_decode_storage = false; - for attr in attrs { - match attr { - PalletStorageAttr::Getter(ident, ..) if getter.is_none() => getter = Some(ident), - PalletStorageAttr::StorageName(name, ..) if rename_as.is_none() => - rename_as = Some(name), - PalletStorageAttr::Unbounded(..) if !unbounded => unbounded = true, - PalletStorageAttr::WhitelistStorage(..) if !whitelisted => whitelisted = true, - PalletStorageAttr::DisableTryDecodeStorage(..) if !disable_try_decode_storage => - disable_try_decode_storage = true, - attr => - return Err(syn::Error::new( - attr.attr_span(), - "Invalid attribute: Duplicate attribute", - )), - } - } - - Ok(PalletStorageAttrInfo { - getter, - rename_as, - unbounded, - whitelisted, - try_decode: !disable_try_decode_storage, - }) - } + fn from_attrs(attrs: Vec) -> syn::Result { + let mut getter = None; + let mut rename_as = None; + let mut unbounded = false; + let mut whitelisted = false; + let mut disable_try_decode_storage = false; + for attr in attrs { + match attr { + PalletStorageAttr::Getter(ident, ..) if getter.is_none() => getter = Some(ident), + PalletStorageAttr::StorageName(name, ..) if rename_as.is_none() => { + rename_as = Some(name) + } + PalletStorageAttr::Unbounded(..) if !unbounded => unbounded = true, + PalletStorageAttr::WhitelistStorage(..) if !whitelisted => whitelisted = true, + PalletStorageAttr::DisableTryDecodeStorage(..) if !disable_try_decode_storage => { + disable_try_decode_storage = true + } + attr => { + return Err(syn::Error::new( + attr.attr_span(), + "Invalid attribute: Duplicate attribute", + )) + } + } + } + + Ok(PalletStorageAttrInfo { + getter, + rename_as, + unbounded, + whitelisted, + try_decode: !disable_try_decode_storage, + }) + } } /// The value and key types used by storages. Needed to expand metadata. pub enum Metadata { - Value { value: syn::Type }, - Map { value: syn::Type, key: syn::Type }, - CountedMap { value: syn::Type, key: syn::Type }, - DoubleMap { value: syn::Type, key1: syn::Type, key2: syn::Type }, - NMap { keys: Vec, keygen: syn::Type, value: syn::Type }, - CountedNMap { keys: Vec, keygen: syn::Type, value: syn::Type }, + Value { + value: syn::Type, + }, + Map { + value: syn::Type, + key: syn::Type, + }, + CountedMap { + value: syn::Type, + key: syn::Type, + }, + DoubleMap { + value: syn::Type, + key1: syn::Type, + key2: syn::Type, + }, + NMap { + keys: Vec, + keygen: syn::Type, + value: syn::Type, + }, + CountedNMap { + keys: Vec, + keygen: syn::Type, + value: syn::Type, + }, } pub enum QueryKind { - OptionQuery, - ResultQuery(syn::Path, syn::Ident), - ValueQuery, + OptionQuery, + ResultQuery(syn::Path, syn::Ident), + ValueQuery, } /// Definition of a storage, storage is a storage type like /// `type MyStorage = StorageValue` /// The keys and values types are parsed in order to get metadata pub struct StorageDef { - /// The index of storage item in pallet module. - pub index: usize, - /// Visibility of the storage type. - pub vis: syn::Visibility, - /// The type ident, to generate the StoragePrefix for. - pub ident: syn::Ident, - /// The keys and value metadata of the storage. - pub metadata: Metadata, - /// The doc associated to the storage. - pub docs: Vec, - /// A set of usage of instance, must be check for consistency with config. - pub instances: Vec, - /// Optional getter to generate. If some then query_kind is ensured to be some as well. - pub getter: Option, - /// Optional expression that evaluates to a type that can be used as StoragePrefix instead of - /// ident. - pub rename_as: Option, - /// Whereas the querytype of the storage is OptionQuery, ResultQuery or ValueQuery. - /// Note that this is best effort as it can't be determined when QueryKind is generic, and - /// result can be false if user do some unexpected type alias. - pub query_kind: Option, - /// Where clause of type definition. - pub where_clause: Option, - /// The span of the pallet::storage attribute. - pub attr_span: proc_macro2::Span, - /// The `cfg` attributes. - pub cfg_attrs: Vec, - /// If generics are named (e.g. `StorageValue`) then this contains all the - /// generics of the storage. - /// If generics are not named, this is none. - pub named_generics: Option, - /// If the value stored in this storage is unbounded. - pub unbounded: bool, - /// Whether or not reads to this storage key will be ignored by benchmarking - pub whitelisted: bool, - /// Whether or not to try to decode the storage key when running try-runtime checks. - pub try_decode: bool, - /// Whether or not a default hasher is allowed to replace `_` - pub use_default_hasher: bool, + /// The index of storage item in pallet module. + pub index: usize, + /// Visibility of the storage type. + pub vis: syn::Visibility, + /// The type ident, to generate the StoragePrefix for. + pub ident: syn::Ident, + /// The keys and value metadata of the storage. + pub metadata: Metadata, + /// The doc associated to the storage. + pub docs: Vec, + /// A set of usage of instance, must be check for consistency with config. + pub instances: Vec, + /// Optional getter to generate. If some then query_kind is ensured to be some as well. + pub getter: Option, + /// Optional expression that evaluates to a type that can be used as StoragePrefix instead of + /// ident. + pub rename_as: Option, + /// Whereas the querytype of the storage is OptionQuery, ResultQuery or ValueQuery. + /// Note that this is best effort as it can't be determined when QueryKind is generic, and + /// result can be false if user do some unexpected type alias. + pub query_kind: Option, + /// Where clause of type definition. + pub where_clause: Option, + /// The span of the pallet::storage attribute. + pub attr_span: proc_macro2::Span, + /// The `cfg` attributes. + pub cfg_attrs: Vec, + /// If generics are named (e.g. `StorageValue`) then this contains all the + /// generics of the storage. + /// If generics are not named, this is none. + pub named_generics: Option, + /// If the value stored in this storage is unbounded. + pub unbounded: bool, + /// Whether or not reads to this storage key will be ignored by benchmarking + pub whitelisted: bool, + /// Whether or not to try to decode the storage key when running try-runtime checks. + pub try_decode: bool, + /// Whether or not a default hasher is allowed to replace `_` + pub use_default_hasher: bool, } /// The parsed generic from the #[derive(Clone)] pub enum StorageGenerics { - DoubleMap { - hasher1: syn::Type, - key1: syn::Type, - hasher2: syn::Type, - key2: syn::Type, - value: syn::Type, - query_kind: Option, - on_empty: Option, - max_values: Option, - }, - Map { - hasher: syn::Type, - key: syn::Type, - value: syn::Type, - query_kind: Option, - on_empty: Option, - max_values: Option, - }, - CountedMap { - hasher: syn::Type, - key: syn::Type, - value: syn::Type, - query_kind: Option, - on_empty: Option, - max_values: Option, - }, - Value { - value: syn::Type, - query_kind: Option, - on_empty: Option, - }, - NMap { - keygen: syn::Type, - value: syn::Type, - query_kind: Option, - on_empty: Option, - max_values: Option, - }, - CountedNMap { - keygen: syn::Type, - value: syn::Type, - query_kind: Option, - on_empty: Option, - max_values: Option, - }, + DoubleMap { + hasher1: syn::Type, + key1: syn::Type, + hasher2: syn::Type, + key2: syn::Type, + value: syn::Type, + query_kind: Option, + on_empty: Option, + max_values: Option, + }, + Map { + hasher: syn::Type, + key: syn::Type, + value: syn::Type, + query_kind: Option, + on_empty: Option, + max_values: Option, + }, + CountedMap { + hasher: syn::Type, + key: syn::Type, + value: syn::Type, + query_kind: Option, + on_empty: Option, + max_values: Option, + }, + Value { + value: syn::Type, + query_kind: Option, + on_empty: Option, + }, + NMap { + keygen: syn::Type, + value: syn::Type, + query_kind: Option, + on_empty: Option, + max_values: Option, + }, + CountedNMap { + keygen: syn::Type, + value: syn::Type, + query_kind: Option, + on_empty: Option, + max_values: Option, + }, } impl StorageGenerics { - /// Return the metadata from the defined generics - fn metadata(&self) -> syn::Result { - let res = match self.clone() { - Self::DoubleMap { value, key1, key2, .. } => Metadata::DoubleMap { value, key1, key2 }, - Self::Map { value, key, .. } => Metadata::Map { value, key }, - Self::CountedMap { value, key, .. } => Metadata::CountedMap { value, key }, - Self::Value { value, .. } => Metadata::Value { value }, - Self::NMap { keygen, value, .. } => - Metadata::NMap { keys: collect_keys(&keygen)?, keygen, value }, - Self::CountedNMap { keygen, value, .. } => - Metadata::CountedNMap { keys: collect_keys(&keygen)?, keygen, value }, - }; - - Ok(res) - } - - /// Return the query kind from the defined generics - fn query_kind(&self) -> Option { - match &self { - Self::DoubleMap { query_kind, .. } | - Self::Map { query_kind, .. } | - Self::CountedMap { query_kind, .. } | - Self::Value { query_kind, .. } | - Self::NMap { query_kind, .. } | - Self::CountedNMap { query_kind, .. } => query_kind.clone(), - } - } + /// Return the metadata from the defined generics + fn metadata(&self) -> syn::Result { + let res = match self.clone() { + Self::DoubleMap { + value, key1, key2, .. + } => Metadata::DoubleMap { value, key1, key2 }, + Self::Map { value, key, .. } => Metadata::Map { value, key }, + Self::CountedMap { value, key, .. } => Metadata::CountedMap { value, key }, + Self::Value { value, .. } => Metadata::Value { value }, + Self::NMap { keygen, value, .. } => Metadata::NMap { + keys: collect_keys(&keygen)?, + keygen, + value, + }, + Self::CountedNMap { keygen, value, .. } => Metadata::CountedNMap { + keys: collect_keys(&keygen)?, + keygen, + value, + }, + }; + + Ok(res) + } + + /// Return the query kind from the defined generics + fn query_kind(&self) -> Option { + match &self { + Self::DoubleMap { query_kind, .. } + | Self::Map { query_kind, .. } + | Self::CountedMap { query_kind, .. } + | Self::Value { query_kind, .. } + | Self::NMap { query_kind, .. } + | Self::CountedNMap { query_kind, .. } => query_kind.clone(), + } + } } enum StorageKind { - Value, - Map, - CountedMap, - DoubleMap, - NMap, - CountedNMap, + Value, + Map, + CountedMap, + DoubleMap, + NMap, + CountedNMap, } /// Check the generics in the `map` contains the generics in `gen` may contains generics in /// `optional_gen`, and doesn't contains any other. fn check_generics( - map: &HashMap, - mandatory_generics: &[&str], - optional_generics: &[&str], - storage_type_name: &str, - args_span: proc_macro2::Span, + map: &HashMap, + mandatory_generics: &[&str], + optional_generics: &[&str], + storage_type_name: &str, + args_span: proc_macro2::Span, ) -> syn::Result<()> { - let mut errors = vec![]; - - let expectation = { - let mut e = format!( - "`{}` expect generics {}and optional generics {}", - storage_type_name, - mandatory_generics - .iter() - .map(|name| format!("`{}`, ", name)) - .collect::(), - &optional_generics.iter().map(|name| format!("`{}`, ", name)).collect::(), - ); - e.pop(); - e.pop(); - e.push('.'); - e - }; - - for (gen_name, gen_binding) in map { - if !mandatory_generics.contains(&gen_name.as_str()) && - !optional_generics.contains(&gen_name.as_str()) - { - let msg = format!( - "Invalid pallet::storage, Unexpected generic `{}` for `{}`. {}", - gen_name, storage_type_name, expectation, - ); - errors.push(syn::Error::new(gen_binding.span(), msg)); - } - } - - for mandatory_generic in mandatory_generics { - if !map.contains_key(&mandatory_generic.to_string()) { - let msg = format!( - "Invalid pallet::storage, cannot find `{}` generic, required for `{}`.", - mandatory_generic, storage_type_name - ); - errors.push(syn::Error::new(args_span, msg)); - } - } - - let mut errors = errors.drain(..); - if let Some(mut error) = errors.next() { - for other_error in errors { - error.combine(other_error); - } - Err(error) - } else { - Ok(()) - } + let mut errors = vec![]; + + let expectation = { + let mut e = format!( + "`{}` expect generics {}and optional generics {}", + storage_type_name, + mandatory_generics + .iter() + .map(|name| format!("`{}`, ", name)) + .collect::(), + &optional_generics + .iter() + .map(|name| format!("`{}`, ", name)) + .collect::(), + ); + e.pop(); + e.pop(); + e.push('.'); + e + }; + + for (gen_name, gen_binding) in map { + if !mandatory_generics.contains(&gen_name.as_str()) + && !optional_generics.contains(&gen_name.as_str()) + { + let msg = format!( + "Invalid pallet::storage, Unexpected generic `{}` for `{}`. {}", + gen_name, storage_type_name, expectation, + ); + errors.push(syn::Error::new(gen_binding.span(), msg)); + } + } + + for mandatory_generic in mandatory_generics { + if !map.contains_key(&mandatory_generic.to_string()) { + let msg = format!( + "Invalid pallet::storage, cannot find `{}` generic, required for `{}`.", + mandatory_generic, storage_type_name + ); + errors.push(syn::Error::new(args_span, msg)); + } + } + + let mut errors = errors.drain(..); + if let Some(mut error) = errors.next() { + for other_error in errors { + error.combine(other_error); + } + Err(error) + } else { + Ok(()) + } } /// Returns `(named generics, metadata, query kind, use_default_hasher)` fn process_named_generics( - storage: &StorageKind, - args_span: proc_macro2::Span, - args: &[syn::AssocType], - dev_mode: bool, + storage: &StorageKind, + args_span: proc_macro2::Span, + args: &[syn::AssocType], + dev_mode: bool, ) -> syn::Result<(Option, Metadata, Option, bool)> { - let mut parsed = HashMap::::new(); - - // Ensure no duplicate. - for arg in args { - if let Some(other) = parsed.get(&arg.ident.to_string()) { - let msg = "Invalid pallet::storage, Duplicated named generic"; - let mut err = syn::Error::new(arg.ident.span(), msg); - err.combine(syn::Error::new(other.ident.span(), msg)); - return Err(err) - } - parsed.insert(arg.ident.to_string(), arg.clone()); - } - - let mut map_mandatory_generics = vec!["Key", "Value"]; - let mut map_optional_generics = vec!["QueryKind", "OnEmpty", "MaxValues"]; - if dev_mode { - map_optional_generics.push("Hasher"); - } else { - map_mandatory_generics.push("Hasher"); - } - - let generics = match storage { - StorageKind::Value => { - check_generics( - &parsed, - &["Value"], - &["QueryKind", "OnEmpty"], - "StorageValue", - args_span, - )?; - - StorageGenerics::Value { - value: parsed - .remove("Value") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), - on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), - } - }, - StorageKind::Map => { - check_generics( - &parsed, - &map_mandatory_generics, - &map_optional_generics, - "StorageMap", - args_span, - )?; - - StorageGenerics::Map { - hasher: parsed - .remove("Hasher") - .map(|binding| binding.ty) - .unwrap_or(syn::parse_quote!(Blake2_128Concat)), - key: parsed - .remove("Key") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - value: parsed - .remove("Value") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), - on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), - max_values: parsed.remove("MaxValues").map(|binding| binding.ty), - } - }, - StorageKind::CountedMap => { - check_generics( - &parsed, - &map_mandatory_generics, - &map_optional_generics, - "CountedStorageMap", - args_span, - )?; - - StorageGenerics::CountedMap { - hasher: parsed - .remove("Hasher") - .map(|binding| binding.ty) - .unwrap_or(syn::Type::Verbatim(quote::quote! { Blake2_128Concat })), - key: parsed - .remove("Key") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - value: parsed - .remove("Value") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), - on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), - max_values: parsed.remove("MaxValues").map(|binding| binding.ty), - } - }, - StorageKind::DoubleMap => { - let mut double_map_mandatory_generics = vec!["Key1", "Key2", "Value"]; - if dev_mode { - map_optional_generics.extend(["Hasher1", "Hasher2"]); - } else { - double_map_mandatory_generics.extend(["Hasher1", "Hasher2"]); - } - - check_generics( - &parsed, - &double_map_mandatory_generics, - &map_optional_generics, - "StorageDoubleMap", - args_span, - )?; - - StorageGenerics::DoubleMap { - hasher1: parsed - .remove("Hasher1") - .map(|binding| binding.ty) - .unwrap_or(syn::parse_quote!(Blake2_128Concat)), - key1: parsed - .remove("Key1") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - hasher2: parsed - .remove("Hasher2") - .map(|binding| binding.ty) - .unwrap_or(syn::parse_quote!(Blake2_128Concat)), - key2: parsed - .remove("Key2") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - value: parsed - .remove("Value") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), - on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), - max_values: parsed.remove("MaxValues").map(|binding| binding.ty), - } - }, - StorageKind::NMap => { - check_generics( - &parsed, - &["Key", "Value"], - &["QueryKind", "OnEmpty", "MaxValues"], - "StorageNMap", - args_span, - )?; - - StorageGenerics::NMap { - keygen: parsed - .remove("Key") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - value: parsed - .remove("Value") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), - on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), - max_values: parsed.remove("MaxValues").map(|binding| binding.ty), - } - }, - StorageKind::CountedNMap => { - check_generics( - &parsed, - &["Key", "Value"], - &["QueryKind", "OnEmpty", "MaxValues"], - "CountedStorageNMap", - args_span, - )?; - - StorageGenerics::CountedNMap { - keygen: parsed - .remove("Key") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - value: parsed - .remove("Value") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), - on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), - max_values: parsed.remove("MaxValues").map(|binding| binding.ty), - } - }, - }; - - let metadata = generics.metadata()?; - let query_kind = generics.query_kind(); - - Ok((Some(generics), metadata, query_kind, false)) + let mut parsed = HashMap::::new(); + + // Ensure no duplicate. + for arg in args { + if let Some(other) = parsed.get(&arg.ident.to_string()) { + let msg = "Invalid pallet::storage, Duplicated named generic"; + let mut err = syn::Error::new(arg.ident.span(), msg); + err.combine(syn::Error::new(other.ident.span(), msg)); + return Err(err); + } + parsed.insert(arg.ident.to_string(), arg.clone()); + } + + let mut map_mandatory_generics = vec!["Key", "Value"]; + let mut map_optional_generics = vec!["QueryKind", "OnEmpty", "MaxValues"]; + if dev_mode { + map_optional_generics.push("Hasher"); + } else { + map_mandatory_generics.push("Hasher"); + } + + let generics = match storage { + StorageKind::Value => { + check_generics( + &parsed, + &["Value"], + &["QueryKind", "OnEmpty"], + "StorageValue", + args_span, + )?; + + StorageGenerics::Value { + value: parsed + .remove("Value") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), + on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), + } + } + StorageKind::Map => { + check_generics( + &parsed, + &map_mandatory_generics, + &map_optional_generics, + "StorageMap", + args_span, + )?; + + StorageGenerics::Map { + hasher: parsed + .remove("Hasher") + .map(|binding| binding.ty) + .unwrap_or(syn::parse_quote!(Blake2_128Concat)), + key: parsed + .remove("Key") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + value: parsed + .remove("Value") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), + on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), + max_values: parsed.remove("MaxValues").map(|binding| binding.ty), + } + } + StorageKind::CountedMap => { + check_generics( + &parsed, + &map_mandatory_generics, + &map_optional_generics, + "CountedStorageMap", + args_span, + )?; + + StorageGenerics::CountedMap { + hasher: parsed + .remove("Hasher") + .map(|binding| binding.ty) + .unwrap_or(syn::Type::Verbatim(quote::quote! { Blake2_128Concat })), + key: parsed + .remove("Key") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + value: parsed + .remove("Value") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), + on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), + max_values: parsed.remove("MaxValues").map(|binding| binding.ty), + } + } + StorageKind::DoubleMap => { + let mut double_map_mandatory_generics = vec!["Key1", "Key2", "Value"]; + if dev_mode { + map_optional_generics.extend(["Hasher1", "Hasher2"]); + } else { + double_map_mandatory_generics.extend(["Hasher1", "Hasher2"]); + } + + check_generics( + &parsed, + &double_map_mandatory_generics, + &map_optional_generics, + "StorageDoubleMap", + args_span, + )?; + + StorageGenerics::DoubleMap { + hasher1: parsed + .remove("Hasher1") + .map(|binding| binding.ty) + .unwrap_or(syn::parse_quote!(Blake2_128Concat)), + key1: parsed + .remove("Key1") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + hasher2: parsed + .remove("Hasher2") + .map(|binding| binding.ty) + .unwrap_or(syn::parse_quote!(Blake2_128Concat)), + key2: parsed + .remove("Key2") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + value: parsed + .remove("Value") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), + on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), + max_values: parsed.remove("MaxValues").map(|binding| binding.ty), + } + } + StorageKind::NMap => { + check_generics( + &parsed, + &["Key", "Value"], + &["QueryKind", "OnEmpty", "MaxValues"], + "StorageNMap", + args_span, + )?; + + StorageGenerics::NMap { + keygen: parsed + .remove("Key") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + value: parsed + .remove("Value") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), + on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), + max_values: parsed.remove("MaxValues").map(|binding| binding.ty), + } + } + StorageKind::CountedNMap => { + check_generics( + &parsed, + &["Key", "Value"], + &["QueryKind", "OnEmpty", "MaxValues"], + "CountedStorageNMap", + args_span, + )?; + + StorageGenerics::CountedNMap { + keygen: parsed + .remove("Key") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + value: parsed + .remove("Value") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), + on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), + max_values: parsed.remove("MaxValues").map(|binding| binding.ty), + } + } + }; + + let metadata = generics.metadata()?; + let query_kind = generics.query_kind(); + + Ok((Some(generics), metadata, query_kind, false)) } /// Returns `(named generics, metadata, query kind, use_default_hasher)` fn process_unnamed_generics( - storage: &StorageKind, - args_span: proc_macro2::Span, - args: &[syn::Type], - dev_mode: bool, + storage: &StorageKind, + args_span: proc_macro2::Span, + args: &[syn::Type], + dev_mode: bool, ) -> syn::Result<(Option, Metadata, Option, bool)> { - let retrieve_arg = |arg_pos| { - args.get(arg_pos).cloned().ok_or_else(|| { - let msg = format!( - "Invalid pallet::storage, unexpected number of generic argument, \ + let retrieve_arg = |arg_pos| { + args.get(arg_pos).cloned().ok_or_else(|| { + let msg = format!( + "Invalid pallet::storage, unexpected number of generic argument, \ expect at least {} args, found {}.", - arg_pos + 1, - args.len(), - ); - syn::Error::new(args_span, msg) - }) - }; - - let prefix_arg = retrieve_arg(0)?; - syn::parse2::(prefix_arg.to_token_stream()).map_err(|e| { - let msg = "Invalid pallet::storage, for unnamed generic arguments the type \ + arg_pos + 1, + args.len(), + ); + syn::Error::new(args_span, msg) + }) + }; + + let prefix_arg = retrieve_arg(0)?; + syn::parse2::(prefix_arg.to_token_stream()).map_err(|e| { + let msg = "Invalid pallet::storage, for unnamed generic arguments the type \ first generic argument must be `_`, the argument is then replaced by macro."; - let mut err = syn::Error::new(prefix_arg.span(), msg); - err.combine(e); - err - })?; - - let use_default_hasher = |arg_pos| { - let arg = retrieve_arg(arg_pos)?; - if syn::parse2::(arg.to_token_stream()).is_ok() { - if dev_mode { - Ok(true) - } else { - let msg = "`_` can only be used in dev_mode. Please specify an appropriate hasher."; - Err(syn::Error::new(arg.span(), msg)) - } - } else { - Ok(false) - } - }; - - let res = match storage { - StorageKind::Value => - (None, Metadata::Value { value: retrieve_arg(1)? }, retrieve_arg(2).ok(), false), - StorageKind::Map => ( - None, - Metadata::Map { key: retrieve_arg(2)?, value: retrieve_arg(3)? }, - retrieve_arg(4).ok(), - use_default_hasher(1)?, - ), - StorageKind::CountedMap => ( - None, - Metadata::CountedMap { key: retrieve_arg(2)?, value: retrieve_arg(3)? }, - retrieve_arg(4).ok(), - use_default_hasher(1)?, - ), - StorageKind::DoubleMap => ( - None, - Metadata::DoubleMap { - key1: retrieve_arg(2)?, - key2: retrieve_arg(4)?, - value: retrieve_arg(5)?, - }, - retrieve_arg(6).ok(), - use_default_hasher(1)? && use_default_hasher(3)?, - ), - StorageKind::NMap => { - let keygen = retrieve_arg(1)?; - let keys = collect_keys(&keygen)?; - ( - None, - Metadata::NMap { keys, keygen, value: retrieve_arg(2)? }, - retrieve_arg(3).ok(), - false, - ) - }, - StorageKind::CountedNMap => { - let keygen = retrieve_arg(1)?; - let keys = collect_keys(&keygen)?; - ( - None, - Metadata::CountedNMap { keys, keygen, value: retrieve_arg(2)? }, - retrieve_arg(3).ok(), - false, - ) - }, - }; - - Ok(res) + let mut err = syn::Error::new(prefix_arg.span(), msg); + err.combine(e); + err + })?; + + let use_default_hasher = |arg_pos| { + let arg = retrieve_arg(arg_pos)?; + if syn::parse2::(arg.to_token_stream()).is_ok() { + if dev_mode { + Ok(true) + } else { + let msg = "`_` can only be used in dev_mode. Please specify an appropriate hasher."; + Err(syn::Error::new(arg.span(), msg)) + } + } else { + Ok(false) + } + }; + + let res = match storage { + StorageKind::Value => ( + None, + Metadata::Value { + value: retrieve_arg(1)?, + }, + retrieve_arg(2).ok(), + false, + ), + StorageKind::Map => ( + None, + Metadata::Map { + key: retrieve_arg(2)?, + value: retrieve_arg(3)?, + }, + retrieve_arg(4).ok(), + use_default_hasher(1)?, + ), + StorageKind::CountedMap => ( + None, + Metadata::CountedMap { + key: retrieve_arg(2)?, + value: retrieve_arg(3)?, + }, + retrieve_arg(4).ok(), + use_default_hasher(1)?, + ), + StorageKind::DoubleMap => ( + None, + Metadata::DoubleMap { + key1: retrieve_arg(2)?, + key2: retrieve_arg(4)?, + value: retrieve_arg(5)?, + }, + retrieve_arg(6).ok(), + use_default_hasher(1)? && use_default_hasher(3)?, + ), + StorageKind::NMap => { + let keygen = retrieve_arg(1)?; + let keys = collect_keys(&keygen)?; + ( + None, + Metadata::NMap { + keys, + keygen, + value: retrieve_arg(2)?, + }, + retrieve_arg(3).ok(), + false, + ) + } + StorageKind::CountedNMap => { + let keygen = retrieve_arg(1)?; + let keys = collect_keys(&keygen)?; + ( + None, + Metadata::CountedNMap { + keys, + keygen, + value: retrieve_arg(2)?, + }, + retrieve_arg(3).ok(), + false, + ) + } + }; + + Ok(res) } /// Returns `(named generics, metadata, query kind, use_default_hasher)` fn process_generics( - segment: &syn::PathSegment, - dev_mode: bool, + segment: &syn::PathSegment, + dev_mode: bool, ) -> syn::Result<(Option, Metadata, Option, bool)> { - let storage_kind = match &*segment.ident.to_string() { - "StorageValue" => StorageKind::Value, - "StorageMap" => StorageKind::Map, - "CountedStorageMap" => StorageKind::CountedMap, - "StorageDoubleMap" => StorageKind::DoubleMap, - "StorageNMap" => StorageKind::NMap, - "CountedStorageNMap" => StorageKind::CountedNMap, - found => { - let msg = format!( + let storage_kind = match &*segment.ident.to_string() { + "StorageValue" => StorageKind::Value, + "StorageMap" => StorageKind::Map, + "CountedStorageMap" => StorageKind::CountedMap, + "StorageDoubleMap" => StorageKind::DoubleMap, + "StorageNMap" => StorageKind::NMap, + "CountedStorageNMap" => StorageKind::CountedNMap, + found => { + let msg = format!( "Invalid pallet::storage, expected ident: `StorageValue` or \ `StorageMap` or `CountedStorageMap` or `StorageDoubleMap` or `StorageNMap` or `CountedStorageNMap` \ in order to expand metadata, found `{}`.", found, ); - return Err(syn::Error::new(segment.ident.span(), msg)) - }, - }; + return Err(syn::Error::new(segment.ident.span(), msg)); + } + }; - let args_span = segment.arguments.span(); + let args_span = segment.arguments.span(); - let args = match &segment.arguments { - syn::PathArguments::AngleBracketed(args) if !args.args.is_empty() => args, - _ => { - let msg = "Invalid pallet::storage, invalid number of generic generic arguments, \ + let args = match &segment.arguments { + syn::PathArguments::AngleBracketed(args) if !args.args.is_empty() => args, + _ => { + let msg = "Invalid pallet::storage, invalid number of generic generic arguments, \ expect more that 0 generic arguments."; - return Err(syn::Error::new(segment.span(), msg)) - }, - }; - - if args.args.iter().all(|gen| matches!(gen, syn::GenericArgument::Type(_))) { - let args = args - .args - .iter() - .map(|gen| match gen { - syn::GenericArgument::Type(gen) => gen.clone(), - _ => unreachable!("It is asserted above that all generics are types"), - }) - .collect::>(); - process_unnamed_generics(&storage_kind, args_span, &args, dev_mode) - } else if args.args.iter().all(|gen| matches!(gen, syn::GenericArgument::AssocType(_))) { - let args = args - .args - .iter() - .map(|gen| match gen { - syn::GenericArgument::AssocType(gen) => gen.clone(), - _ => unreachable!("It is asserted above that all generics are bindings"), - }) - .collect::>(); - process_named_generics(&storage_kind, args_span, &args, dev_mode) - } else { - let msg = "Invalid pallet::storage, invalid generic declaration for storage. Expect only \ + return Err(syn::Error::new(segment.span(), msg)); + } + }; + + if args + .args + .iter() + .all(|gen| matches!(gen, syn::GenericArgument::Type(_))) + { + let args = args + .args + .iter() + .map(|gen| match gen { + syn::GenericArgument::Type(gen) => gen.clone(), + _ => unreachable!("It is asserted above that all generics are types"), + }) + .collect::>(); + process_unnamed_generics(&storage_kind, args_span, &args, dev_mode) + } else if args + .args + .iter() + .all(|gen| matches!(gen, syn::GenericArgument::AssocType(_))) + { + let args = args + .args + .iter() + .map(|gen| match gen { + syn::GenericArgument::AssocType(gen) => gen.clone(), + _ => unreachable!("It is asserted above that all generics are bindings"), + }) + .collect::>(); + process_named_generics(&storage_kind, args_span, &args, dev_mode) + } else { + let msg = "Invalid pallet::storage, invalid generic declaration for storage. Expect only \ type generics or binding generics, e.g. `` or \ ``."; - Err(syn::Error::new(segment.span(), msg)) - } + Err(syn::Error::new(segment.span(), msg)) + } } /// Parse the 2nd type argument to `StorageNMap` and return its keys. fn collect_keys(keygen: &syn::Type) -> syn::Result> { - if let syn::Type::Tuple(tup) = keygen { - tup.elems.iter().map(extract_key).collect::>>() - } else { - Ok(vec![extract_key(keygen)?]) - } + if let syn::Type::Tuple(tup) = keygen { + tup.elems + .iter() + .map(extract_key) + .collect::>>() + } else { + Ok(vec![extract_key(keygen)?]) + } } /// In `Key`, extract K and return it. fn extract_key(ty: &syn::Type) -> syn::Result { - let typ = if let syn::Type::Path(typ) = ty { - typ - } else { - let msg = "Invalid pallet::storage, expected type path"; - return Err(syn::Error::new(ty.span(), msg)) - }; - - let key_struct = typ.path.segments.last().ok_or_else(|| { - let msg = "Invalid pallet::storage, expected type path with at least one segment"; - syn::Error::new(typ.path.span(), msg) - })?; - if key_struct.ident != "Key" && key_struct.ident != "NMapKey" { - let msg = "Invalid pallet::storage, expected Key or NMapKey struct"; - return Err(syn::Error::new(key_struct.ident.span(), msg)) - } - - let ty_params = if let syn::PathArguments::AngleBracketed(args) = &key_struct.arguments { - args - } else { - let msg = "Invalid pallet::storage, expected angle bracketed arguments"; - return Err(syn::Error::new(key_struct.arguments.span(), msg)) - }; - - if ty_params.args.len() != 2 { - let msg = format!( - "Invalid pallet::storage, unexpected number of generic arguments \ + let typ = if let syn::Type::Path(typ) = ty { + typ + } else { + let msg = "Invalid pallet::storage, expected type path"; + return Err(syn::Error::new(ty.span(), msg)); + }; + + let key_struct = typ.path.segments.last().ok_or_else(|| { + let msg = "Invalid pallet::storage, expected type path with at least one segment"; + syn::Error::new(typ.path.span(), msg) + })?; + if key_struct.ident != "Key" && key_struct.ident != "NMapKey" { + let msg = "Invalid pallet::storage, expected Key or NMapKey struct"; + return Err(syn::Error::new(key_struct.ident.span(), msg)); + } + + let ty_params = if let syn::PathArguments::AngleBracketed(args) = &key_struct.arguments { + args + } else { + let msg = "Invalid pallet::storage, expected angle bracketed arguments"; + return Err(syn::Error::new(key_struct.arguments.span(), msg)); + }; + + if ty_params.args.len() != 2 { + let msg = format!( + "Invalid pallet::storage, unexpected number of generic arguments \ for Key struct, expected 2 args, found {}", - ty_params.args.len() - ); - return Err(syn::Error::new(ty_params.span(), msg)) - } - - let key = match &ty_params.args[1] { - syn::GenericArgument::Type(key_ty) => key_ty.clone(), - _ => { - let msg = "Invalid pallet::storage, expected type"; - return Err(syn::Error::new(ty_params.args[1].span(), msg)) - }, - }; - - Ok(key) + ty_params.args.len() + ); + return Err(syn::Error::new(ty_params.span(), msg)); + } + + let key = match &ty_params.args[1] { + syn::GenericArgument::Type(key_ty) => key_ty.clone(), + _ => { + let msg = "Invalid pallet::storage, expected type"; + return Err(syn::Error::new(ty_params.args[1].span(), msg)); + } + }; + + Ok(key) } impl StorageDef { - /// Return the storage prefix for this storage item - pub fn prefix(&self) -> String { - self.rename_as - .as_ref() - .map(syn::LitStr::value) - .unwrap_or_else(|| self.ident.to_string()) - } - - /// Return either the span of the ident or the span of the literal in the - /// #[storage_prefix] attribute - pub fn prefix_span(&self) -> proc_macro2::Span { - self.rename_as - .as_ref() - .map(syn::LitStr::span) - .unwrap_or_else(|| self.ident.span()) - } - - pub fn try_from( - attr_span: proc_macro2::Span, - index: usize, - item: &mut syn::Item, - dev_mode: bool, - ) -> syn::Result { - let item = if let syn::Item::Type(item) = item { - item - } else { - return Err(syn::Error::new(item.span(), "Invalid pallet::storage, expect item type.")) - }; - - let attrs: Vec = helper::take_item_pallet_attrs(&mut item.attrs)?; - let PalletStorageAttrInfo { getter, rename_as, mut unbounded, whitelisted, try_decode } = - PalletStorageAttrInfo::from_attrs(attrs)?; - - // set all storages to be unbounded if dev_mode is enabled - unbounded |= dev_mode; - let cfg_attrs = helper::get_item_cfg_attrs(&item.attrs); - - let instances = vec![helper::check_type_def_gen(&item.generics, item.ident.span())?]; - - let where_clause = item.generics.where_clause.clone(); - let docs = get_doc_literals(&item.attrs); - - let typ = if let syn::Type::Path(typ) = &*item.ty { - typ - } else { - let msg = "Invalid pallet::storage, expected type path"; - return Err(syn::Error::new(item.ty.span(), msg)) - }; - - if typ.path.segments.len() != 1 { - let msg = "Invalid pallet::storage, expected type path with one segment"; - return Err(syn::Error::new(item.ty.span(), msg)) - } - - let (named_generics, metadata, query_kind, use_default_hasher) = - process_generics(&typ.path.segments[0], dev_mode)?; - - let query_kind = query_kind - .map(|query_kind| { - use syn::{ - AngleBracketedGenericArguments, GenericArgument, Path, PathArguments, Type, - TypePath, - }; - - let result_query = match query_kind { - Type::Path(path) - if path - .path - .segments - .last() - .map_or(false, |s| s.ident == "OptionQuery") => - return Ok(Some(QueryKind::OptionQuery)), - Type::Path(TypePath { path: Path { segments, .. }, .. }) - if segments.last().map_or(false, |s| s.ident == "ResultQuery") => - segments - .last() - .expect("segments is checked to have the last value; qed") - .clone(), - Type::Path(path) - if path.path.segments.last().map_or(false, |s| s.ident == "ValueQuery") => - return Ok(Some(QueryKind::ValueQuery)), - _ => return Ok(None), - }; - - let error_type = match result_query.arguments { - PathArguments::AngleBracketed(AngleBracketedGenericArguments { - args, .. - }) => { - if args.len() != 1 { - let msg = format!( - "Invalid pallet::storage, unexpected number of generic arguments \ + /// Return the storage prefix for this storage item + pub fn prefix(&self) -> String { + self.rename_as + .as_ref() + .map(syn::LitStr::value) + .unwrap_or_else(|| self.ident.to_string()) + } + + /// Return either the span of the ident or the span of the literal in the + /// #[storage_prefix] attribute + pub fn prefix_span(&self) -> proc_macro2::Span { + self.rename_as + .as_ref() + .map(syn::LitStr::span) + .unwrap_or_else(|| self.ident.span()) + } + + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + dev_mode: bool, + ) -> syn::Result { + let item = if let syn::Item::Type(item) = item { + item + } else { + return Err(syn::Error::new( + item.span(), + "Invalid pallet::storage, expect item type.", + )); + }; + + let attrs: Vec = helper::take_item_pallet_attrs(&mut item.attrs)?; + let PalletStorageAttrInfo { + getter, + rename_as, + mut unbounded, + whitelisted, + try_decode, + } = PalletStorageAttrInfo::from_attrs(attrs)?; + + // set all storages to be unbounded if dev_mode is enabled + unbounded |= dev_mode; + let cfg_attrs = helper::get_item_cfg_attrs(&item.attrs); + + let instances = vec![helper::check_type_def_gen( + &item.generics, + item.ident.span(), + )?]; + + let where_clause = item.generics.where_clause.clone(); + let docs = get_doc_literals(&item.attrs); + + let typ = if let syn::Type::Path(typ) = &*item.ty { + typ + } else { + let msg = "Invalid pallet::storage, expected type path"; + return Err(syn::Error::new(item.ty.span(), msg)); + }; + + if typ.path.segments.len() != 1 { + let msg = "Invalid pallet::storage, expected type path with one segment"; + return Err(syn::Error::new(item.ty.span(), msg)); + } + + let (named_generics, metadata, query_kind, use_default_hasher) = + process_generics(&typ.path.segments[0], dev_mode)?; + + let query_kind = query_kind + .map(|query_kind| { + use syn::{ + AngleBracketedGenericArguments, GenericArgument, Path, PathArguments, Type, + TypePath, + }; + + let result_query = match query_kind { + Type::Path(path) + if path + .path + .segments + .last() + .map_or(false, |s| s.ident == "OptionQuery") => + { + return Ok(Some(QueryKind::OptionQuery)) + } + Type::Path(TypePath { + path: Path { segments, .. }, + .. + }) if segments.last().map_or(false, |s| s.ident == "ResultQuery") => segments + .last() + .expect("segments is checked to have the last value; qed") + .clone(), + Type::Path(path) + if path + .path + .segments + .last() + .map_or(false, |s| s.ident == "ValueQuery") => + { + return Ok(Some(QueryKind::ValueQuery)) + } + _ => return Ok(None), + }; + + let error_type = match result_query.arguments { + PathArguments::AngleBracketed(AngleBracketedGenericArguments { + args, .. + }) => { + if args.len() != 1 { + let msg = format!( + "Invalid pallet::storage, unexpected number of generic arguments \ for ResultQuery, expected 1 type argument, found {}", - args.len(), - ); - return Err(syn::Error::new(args.span(), msg)) - } - - args[0].clone() - }, - args => { - let msg = format!( - "Invalid pallet::storage, unexpected generic args for ResultQuery, \ + args.len(), + ); + return Err(syn::Error::new(args.span(), msg)); + } + + args[0].clone() + } + args => { + let msg = format!( + "Invalid pallet::storage, unexpected generic args for ResultQuery, \ expected angle-bracketed arguments, found `{}`", - args.to_token_stream().to_string() - ); - return Err(syn::Error::new(args.span(), msg)) - }, - }; - - match error_type { - GenericArgument::Type(Type::Path(TypePath { - path: Path { segments: err_variant, leading_colon }, - .. - })) => { - if err_variant.len() < 2 { - let msg = format!( - "Invalid pallet::storage, unexpected number of path segments for \ + args.to_token_stream().to_string() + ); + return Err(syn::Error::new(args.span(), msg)); + } + }; + + match error_type { + GenericArgument::Type(Type::Path(TypePath { + path: + Path { + segments: err_variant, + leading_colon, + }, + .. + })) => { + if err_variant.len() < 2 { + let msg = format!( + "Invalid pallet::storage, unexpected number of path segments for \ the generics in ResultQuery, expected a path with at least 2 \ segments, found {}", - err_variant.len(), - ); - return Err(syn::Error::new(err_variant.span(), msg)) - } - let mut error = err_variant.clone(); - let err_variant = error - .pop() - .expect("Checked to have at least 2; qed") - .into_value() - .ident; - - // Necessary here to eliminate the last double colon - let last = - error.pop().expect("Checked to have at least 2; qed").into_value(); - error.push_value(last); - - Ok(Some(QueryKind::ResultQuery( - syn::Path { leading_colon, segments: error }, - err_variant, - ))) - }, - gen_arg => { - let msg = format!( + err_variant.len(), + ); + return Err(syn::Error::new(err_variant.span(), msg)); + } + let mut error = err_variant.clone(); + let err_variant = error + .pop() + .expect("Checked to have at least 2; qed") + .into_value() + .ident; + + // Necessary here to eliminate the last double colon + let last = error + .pop() + .expect("Checked to have at least 2; qed") + .into_value(); + error.push_value(last); + + Ok(Some(QueryKind::ResultQuery( + syn::Path { + leading_colon, + segments: error, + }, + err_variant, + ))) + } + gen_arg => { + let msg = format!( "Invalid pallet::storage, unexpected generic argument kind, expected a \ type path to a `PalletError` enum variant, found `{}`", gen_arg.to_token_stream().to_string(), ); - Err(syn::Error::new(gen_arg.span(), msg)) - }, - } - }) - .transpose()? - .unwrap_or(Some(QueryKind::OptionQuery)); - - if let (None, Some(getter)) = (query_kind.as_ref(), getter.as_ref()) { - let msg = "Invalid pallet::storage, cannot generate getter because QueryKind is not \ + Err(syn::Error::new(gen_arg.span(), msg)) + } + } + }) + .transpose()? + .unwrap_or(Some(QueryKind::OptionQuery)); + + if let (None, Some(getter)) = (query_kind.as_ref(), getter.as_ref()) { + let msg = "Invalid pallet::storage, cannot generate getter because QueryKind is not \ identifiable. QueryKind must be `OptionQuery`, `ResultQuery`, `ValueQuery`, or default \ one to be identifiable."; - return Err(syn::Error::new(getter.span(), msg)) - } - - Ok(StorageDef { - attr_span, - index, - vis: item.vis.clone(), - ident: item.ident.clone(), - instances, - metadata, - docs, - getter, - rename_as, - query_kind, - where_clause, - cfg_attrs, - named_generics, - unbounded, - whitelisted, - try_decode, - use_default_hasher, - }) - } + return Err(syn::Error::new(getter.span(), msg)); + } + + Ok(StorageDef { + attr_span, + index, + vis: item.vis.clone(), + ident: item.ident.clone(), + instances, + metadata, + docs, + getter, + rename_as, + query_kind, + where_clause, + cfg_attrs, + named_generics, + unbounded, + whitelisted, + try_decode, + use_default_hasher, + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/type_value.rs b/support/procedural-fork/src/pallet/parse/type_value.rs index 4d9db30b3..d5c85248f 100644 --- a/support/procedural-fork/src/pallet/parse/type_value.rs +++ b/support/procedural-fork/src/pallet/parse/type_value.rs @@ -20,104 +20,104 @@ use syn::spanned::Spanned; /// Definition of type value. Just a function which is expanded to a struct implementing `Get`. pub struct TypeValueDef { - /// The index of error item in pallet module. - pub index: usize, - /// Visibility of the struct to generate. - pub vis: syn::Visibility, - /// Ident of the struct to generate. - pub ident: syn::Ident, - /// The type return by Get. - pub type_: Box, - /// The block returning the value to get - pub block: Box, - /// If type value is generic over `T` (or `T` and `I` for instantiable pallet) - pub is_generic: bool, - /// A set of usage of instance, must be check for consistency with config. - pub instances: Vec, - /// The where clause of the function. - pub where_clause: Option, - /// The span of the pallet::type_value attribute. - pub attr_span: proc_macro2::Span, - /// Docs on the item. - pub docs: Vec, + /// The index of error item in pallet module. + pub index: usize, + /// Visibility of the struct to generate. + pub vis: syn::Visibility, + /// Ident of the struct to generate. + pub ident: syn::Ident, + /// The type return by Get. + pub type_: Box, + /// The block returning the value to get + pub block: Box, + /// If type value is generic over `T` (or `T` and `I` for instantiable pallet) + pub is_generic: bool, + /// A set of usage of instance, must be check for consistency with config. + pub instances: Vec, + /// The where clause of the function. + pub where_clause: Option, + /// The span of the pallet::type_value attribute. + pub attr_span: proc_macro2::Span, + /// Docs on the item. + pub docs: Vec, } impl TypeValueDef { - pub fn try_from( - attr_span: proc_macro2::Span, - index: usize, - item: &mut syn::Item, - ) -> syn::Result { - let item = if let syn::Item::Fn(item) = item { - item - } else { - let msg = "Invalid pallet::type_value, expected item fn"; - return Err(syn::Error::new(item.span(), msg)) - }; + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + ) -> syn::Result { + let item = if let syn::Item::Fn(item) = item { + item + } else { + let msg = "Invalid pallet::type_value, expected item fn"; + return Err(syn::Error::new(item.span(), msg)); + }; - let mut docs = vec![]; - for attr in &item.attrs { - if let syn::Meta::NameValue(meta) = &attr.meta { - if meta.path.get_ident().map_or(false, |ident| ident == "doc") { - docs.push(meta.value.clone()); - continue - } - } + let mut docs = vec![]; + for attr in &item.attrs { + if let syn::Meta::NameValue(meta) = &attr.meta { + if meta.path.get_ident().map_or(false, |ident| ident == "doc") { + docs.push(meta.value.clone()); + continue; + } + } - let msg = "Invalid pallet::type_value, unexpected attribute, only doc attribute are \ + let msg = "Invalid pallet::type_value, unexpected attribute, only doc attribute are \ allowed"; - return Err(syn::Error::new(attr.span(), msg)) - } + return Err(syn::Error::new(attr.span(), msg)); + } - if let Some(span) = item - .sig - .constness - .as_ref() - .map(|t| t.span()) - .or_else(|| item.sig.asyncness.as_ref().map(|t| t.span())) - .or_else(|| item.sig.unsafety.as_ref().map(|t| t.span())) - .or_else(|| item.sig.abi.as_ref().map(|t| t.span())) - .or_else(|| item.sig.variadic.as_ref().map(|t| t.span())) - { - let msg = "Invalid pallet::type_value, unexpected token"; - return Err(syn::Error::new(span, msg)) - } + if let Some(span) = item + .sig + .constness + .as_ref() + .map(|t| t.span()) + .or_else(|| item.sig.asyncness.as_ref().map(|t| t.span())) + .or_else(|| item.sig.unsafety.as_ref().map(|t| t.span())) + .or_else(|| item.sig.abi.as_ref().map(|t| t.span())) + .or_else(|| item.sig.variadic.as_ref().map(|t| t.span())) + { + let msg = "Invalid pallet::type_value, unexpected token"; + return Err(syn::Error::new(span, msg)); + } - if !item.sig.inputs.is_empty() { - let msg = "Invalid pallet::type_value, unexpected argument"; - return Err(syn::Error::new(item.sig.inputs[0].span(), msg)) - } + if !item.sig.inputs.is_empty() { + let msg = "Invalid pallet::type_value, unexpected argument"; + return Err(syn::Error::new(item.sig.inputs[0].span(), msg)); + } - let vis = item.vis.clone(); - let ident = item.sig.ident.clone(); - let block = item.block.clone(); - let type_ = match item.sig.output.clone() { - syn::ReturnType::Type(_, type_) => type_, - syn::ReturnType::Default => { - let msg = "Invalid pallet::type_value, expected return type"; - return Err(syn::Error::new(item.sig.span(), msg)) - }, - }; + let vis = item.vis.clone(); + let ident = item.sig.ident.clone(); + let block = item.block.clone(); + let type_ = match item.sig.output.clone() { + syn::ReturnType::Type(_, type_) => type_, + syn::ReturnType::Default => { + let msg = "Invalid pallet::type_value, expected return type"; + return Err(syn::Error::new(item.sig.span(), msg)); + } + }; - let mut instances = vec![]; - if let Some(usage) = helper::check_type_value_gen(&item.sig.generics, item.sig.span())? { - instances.push(usage); - } + let mut instances = vec![]; + if let Some(usage) = helper::check_type_value_gen(&item.sig.generics, item.sig.span())? { + instances.push(usage); + } - let is_generic = item.sig.generics.type_params().count() > 0; - let where_clause = item.sig.generics.where_clause.clone(); + let is_generic = item.sig.generics.type_params().count() > 0; + let where_clause = item.sig.generics.where_clause.clone(); - Ok(TypeValueDef { - attr_span, - index, - is_generic, - vis, - ident, - block, - type_, - instances, - where_clause, - docs, - }) - } + Ok(TypeValueDef { + attr_span, + index, + is_generic, + vis, + ident, + block, + type_, + instances, + where_clause, + docs, + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/validate_unsigned.rs b/support/procedural-fork/src/pallet/parse/validate_unsigned.rs index 2bf0a1b6c..6e5109a74 100644 --- a/support/procedural-fork/src/pallet/parse/validate_unsigned.rs +++ b/support/procedural-fork/src/pallet/parse/validate_unsigned.rs @@ -20,43 +20,43 @@ use syn::spanned::Spanned; /// The definition of the pallet validate unsigned implementation. pub struct ValidateUnsignedDef { - /// The index of validate unsigned item in pallet module. - pub index: usize, - /// A set of usage of instance, must be check for consistency with config. - pub instances: Vec, + /// The index of validate unsigned item in pallet module. + pub index: usize, + /// A set of usage of instance, must be check for consistency with config. + pub instances: Vec, } impl ValidateUnsignedDef { - pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { - let item = if let syn::Item::Impl(item) = item { - item - } else { - let msg = "Invalid pallet::validate_unsigned, expected item impl"; - return Err(syn::Error::new(item.span(), msg)) - }; - - if item.trait_.is_none() { - let msg = "Invalid pallet::validate_unsigned, expected impl<..> ValidateUnsigned for \ + pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { + let item = if let syn::Item::Impl(item) = item { + item + } else { + let msg = "Invalid pallet::validate_unsigned, expected item impl"; + return Err(syn::Error::new(item.span(), msg)); + }; + + if item.trait_.is_none() { + let msg = "Invalid pallet::validate_unsigned, expected impl<..> ValidateUnsigned for \ Pallet<..>"; - return Err(syn::Error::new(item.span(), msg)) - } - - if let Some(last) = item.trait_.as_ref().unwrap().1.segments.last() { - if last.ident != "ValidateUnsigned" { - let msg = "Invalid pallet::validate_unsigned, expected trait ValidateUnsigned"; - return Err(syn::Error::new(last.span(), msg)) - } - } else { - let msg = "Invalid pallet::validate_unsigned, expected impl<..> ValidateUnsigned for \ + return Err(syn::Error::new(item.span(), msg)); + } + + if let Some(last) = item.trait_.as_ref().unwrap().1.segments.last() { + if last.ident != "ValidateUnsigned" { + let msg = "Invalid pallet::validate_unsigned, expected trait ValidateUnsigned"; + return Err(syn::Error::new(last.span(), msg)); + } + } else { + let msg = "Invalid pallet::validate_unsigned, expected impl<..> ValidateUnsigned for \ Pallet<..>"; - return Err(syn::Error::new(item.span(), msg)) - } + return Err(syn::Error::new(item.span(), msg)); + } - let instances = vec![ - helper::check_pallet_struct_usage(&item.self_ty)?, - helper::check_impl_gen(&item.generics, item.impl_token.span())?, - ]; + let instances = vec![ + helper::check_pallet_struct_usage(&item.self_ty)?, + helper::check_impl_gen(&item.generics, item.impl_token.span())?, + ]; - Ok(ValidateUnsignedDef { index, instances }) - } + Ok(ValidateUnsignedDef { index, instances }) + } } diff --git a/support/procedural-fork/src/pallet_error.rs b/support/procedural-fork/src/pallet_error.rs index 693a1e982..bdf8330cd 100644 --- a/support/procedural-fork/src/pallet_error.rs +++ b/support/procedural-fork/src/pallet_error.rs @@ -20,159 +20,172 @@ use quote::ToTokens; // Derive `PalletError` pub fn derive_pallet_error(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let syn::DeriveInput { ident: name, generics, data, .. } = match syn::parse(input) { - Ok(input) => input, - Err(e) => return e.to_compile_error().into(), - }; - - let frame_support = match generate_access_from_frame_or_crate("frame-support") { - Ok(c) => c, - Err(e) => return e.into_compile_error().into(), - }; - let frame_support = &frame_support; - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - - let max_encoded_size = match data { - syn::Data::Struct(syn::DataStruct { fields, .. }) => match fields { - syn::Fields::Named(syn::FieldsNamed { named: fields, .. }) | - syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed: fields, .. }) => { - let maybe_field_tys = fields - .iter() - .map(|f| generate_field_types(f, &frame_support)) - .collect::>>(); - let field_tys = match maybe_field_tys { - Ok(tys) => tys.into_iter().flatten(), - Err(e) => return e.into_compile_error().into(), - }; - quote::quote! { - 0_usize - #( - .saturating_add(< - #field_tys as #frame_support::traits::PalletError - >::MAX_ENCODED_SIZE) - )* - } - }, - syn::Fields::Unit => quote::quote!(0), - }, - syn::Data::Enum(syn::DataEnum { variants, .. }) => { - let field_tys = variants - .iter() - .map(|variant| generate_variant_field_types(variant, &frame_support)) - .collect::>>, syn::Error>>(); - - let field_tys = match field_tys { - Ok(tys) => tys.into_iter().flatten().collect::>(), - Err(e) => return e.to_compile_error().into(), - }; - - // We start with `1`, because the discriminant of an enum is stored as u8 - if field_tys.is_empty() { - quote::quote!(1) - } else { - let variant_sizes = field_tys.into_iter().map(|variant_field_tys| { - quote::quote! { - 1_usize - #(.saturating_add(< - #variant_field_tys as #frame_support::traits::PalletError - >::MAX_ENCODED_SIZE))* - } - }); - - quote::quote! {{ - let mut size = 1_usize; - let mut tmp = 0_usize; - #( - tmp = #variant_sizes; - size = if tmp > size { tmp } else { size }; - tmp = 0_usize; - )* - size - }} - } - }, - syn::Data::Union(syn::DataUnion { union_token, .. }) => { - let msg = "Cannot derive `PalletError` for union; please implement it directly"; - return syn::Error::new(union_token.span, msg).into_compile_error().into() - }, - }; - - quote::quote!( - const _: () = { - impl #impl_generics #frame_support::traits::PalletError - for #name #ty_generics #where_clause - { - const MAX_ENCODED_SIZE: usize = #max_encoded_size; - } - }; - ) - .into() + let syn::DeriveInput { + ident: name, + generics, + data, + .. + } = match syn::parse(input) { + Ok(input) => input, + Err(e) => return e.to_compile_error().into(), + }; + + let frame_support = match generate_access_from_frame_or_crate("frame-support") { + Ok(c) => c, + Err(e) => return e.into_compile_error().into(), + }; + let frame_support = &frame_support; + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + let max_encoded_size = match data { + syn::Data::Struct(syn::DataStruct { fields, .. }) => match fields { + syn::Fields::Named(syn::FieldsNamed { named: fields, .. }) + | syn::Fields::Unnamed(syn::FieldsUnnamed { + unnamed: fields, .. + }) => { + let maybe_field_tys = fields + .iter() + .map(|f| generate_field_types(f, &frame_support)) + .collect::>>(); + let field_tys = match maybe_field_tys { + Ok(tys) => tys.into_iter().flatten(), + Err(e) => return e.into_compile_error().into(), + }; + quote::quote! { + 0_usize + #( + .saturating_add(< + #field_tys as #frame_support::traits::PalletError + >::MAX_ENCODED_SIZE) + )* + } + } + syn::Fields::Unit => quote::quote!(0), + }, + syn::Data::Enum(syn::DataEnum { variants, .. }) => { + let field_tys = variants + .iter() + .map(|variant| generate_variant_field_types(variant, &frame_support)) + .collect::>>, syn::Error>>(); + + let field_tys = match field_tys { + Ok(tys) => tys.into_iter().flatten().collect::>(), + Err(e) => return e.to_compile_error().into(), + }; + + // We start with `1`, because the discriminant of an enum is stored as u8 + if field_tys.is_empty() { + quote::quote!(1) + } else { + let variant_sizes = field_tys.into_iter().map(|variant_field_tys| { + quote::quote! { + 1_usize + #(.saturating_add(< + #variant_field_tys as #frame_support::traits::PalletError + >::MAX_ENCODED_SIZE))* + } + }); + + quote::quote! {{ + let mut size = 1_usize; + let mut tmp = 0_usize; + #( + tmp = #variant_sizes; + size = if tmp > size { tmp } else { size }; + tmp = 0_usize; + )* + size + }} + } + } + syn::Data::Union(syn::DataUnion { union_token, .. }) => { + let msg = "Cannot derive `PalletError` for union; please implement it directly"; + return syn::Error::new(union_token.span, msg) + .into_compile_error() + .into(); + } + }; + + quote::quote!( + const _: () = { + impl #impl_generics #frame_support::traits::PalletError + for #name #ty_generics #where_clause + { + const MAX_ENCODED_SIZE: usize = #max_encoded_size; + } + }; + ) + .into() } fn generate_field_types( - field: &syn::Field, - scrate: &syn::Path, + field: &syn::Field, + scrate: &syn::Path, ) -> syn::Result> { - let attrs = &field.attrs; - - for attr in attrs { - if attr.path().is_ident("codec") { - let mut res = None; - - attr.parse_nested_meta(|meta| { - if meta.path.is_ident("skip") { - res = Some(None); - } else if meta.path.is_ident("compact") { - let field_ty = &field.ty; - res = Some(Some(quote::quote!(#scrate::__private::codec::Compact<#field_ty>))); - } else if meta.path.is_ident("compact") { - res = Some(Some(meta.value()?.parse()?)); - } - - Ok(()) - })?; - - if let Some(v) = res { - return Ok(v) - } - } - } - - Ok(Some(field.ty.to_token_stream())) + let attrs = &field.attrs; + + for attr in attrs { + if attr.path().is_ident("codec") { + let mut res = None; + + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("skip") { + res = Some(None); + } else if meta.path.is_ident("compact") { + let field_ty = &field.ty; + res = Some(Some( + quote::quote!(#scrate::__private::codec::Compact<#field_ty>), + )); + } else if meta.path.is_ident("compact") { + res = Some(Some(meta.value()?.parse()?)); + } + + Ok(()) + })?; + + if let Some(v) = res { + return Ok(v); + } + } + } + + Ok(Some(field.ty.to_token_stream())) } fn generate_variant_field_types( - variant: &syn::Variant, - scrate: &syn::Path, + variant: &syn::Variant, + scrate: &syn::Path, ) -> syn::Result>> { - let attrs = &variant.attrs; - - for attr in attrs { - if attr.path().is_ident("codec") { - let mut skip = false; - - // We ignore the error intentionally as this isn't `codec(skip)` when - // `parse_nested_meta` fails. - let _ = attr.parse_nested_meta(|meta| { - skip = meta.path.is_ident("skip"); - Ok(()) - }); - - if skip { - return Ok(None) - } - } - } - - match &variant.fields { - syn::Fields::Named(syn::FieldsNamed { named: fields, .. }) | - syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed: fields, .. }) => { - let field_tys = fields - .iter() - .map(|field| generate_field_types(field, scrate)) - .collect::>>()?; - Ok(Some(field_tys.into_iter().flatten().collect())) - }, - syn::Fields::Unit => Ok(None), - } + let attrs = &variant.attrs; + + for attr in attrs { + if attr.path().is_ident("codec") { + let mut skip = false; + + // We ignore the error intentionally as this isn't `codec(skip)` when + // `parse_nested_meta` fails. + let _ = attr.parse_nested_meta(|meta| { + skip = meta.path.is_ident("skip"); + Ok(()) + }); + + if skip { + return Ok(None); + } + } + } + + match &variant.fields { + syn::Fields::Named(syn::FieldsNamed { named: fields, .. }) + | syn::Fields::Unnamed(syn::FieldsUnnamed { + unnamed: fields, .. + }) => { + let field_tys = fields + .iter() + .map(|field| generate_field_types(field, scrate)) + .collect::>>()?; + Ok(Some(field_tys.into_iter().flatten().collect())) + } + syn::Fields::Unit => Ok(None), + } } diff --git a/support/procedural-fork/src/runtime/expand/mod.rs b/support/procedural-fork/src/runtime/expand/mod.rs index 93c88fce9..c26cbccb7 100644 --- a/support/procedural-fork/src/runtime/expand/mod.rs +++ b/support/procedural-fork/src/runtime/expand/mod.rs @@ -17,20 +17,20 @@ use super::parse::runtime_types::RuntimeType; use crate::{ - construct_runtime::{ - check_pallet_number, decl_all_pallets, decl_integrity_test, decl_pallet_runtime_setup, - decl_static_assertions, expand, - }, - runtime::{ - parse::{ - AllPalletsDeclaration, ExplicitAllPalletsDeclaration, ImplicitAllPalletsDeclaration, - }, - Def, - }, + construct_runtime::{ + check_pallet_number, decl_all_pallets, decl_integrity_test, decl_pallet_runtime_setup, + decl_static_assertions, expand, + }, + runtime::{ + parse::{ + AllPalletsDeclaration, ExplicitAllPalletsDeclaration, ImplicitAllPalletsDeclaration, + }, + Def, + }, }; use cfg_expr::Predicate; use frame_support_procedural_tools::{ - generate_access_from_frame_or_crate, generate_crate_access, generate_hidden_includes, + generate_access_from_frame_or_crate, generate_crate_access, generate_hidden_includes, }; use proc_macro2::TokenStream as TokenStream2; use quote::quote; @@ -41,280 +41,300 @@ use syn::{Ident, Result}; const SYSTEM_PALLET_NAME: &str = "System"; pub fn expand(def: Def, legacy_ordering: bool) -> TokenStream2 { - let input = def.input; - - let (check_pallet_number_res, res) = match def.pallets { - AllPalletsDeclaration::Implicit(ref decl) => ( - check_pallet_number(input.clone(), decl.pallet_count), - construct_runtime_implicit_to_explicit(input.into(), decl.clone(), legacy_ordering), - ), - AllPalletsDeclaration::Explicit(ref decl) => ( - check_pallet_number(input, decl.pallets.len()), - construct_runtime_final_expansion( - def.runtime_struct.ident.clone(), - decl.clone(), - def.runtime_types.clone(), - legacy_ordering, - ), - ), - }; - - let res = res.unwrap_or_else(|e| e.to_compile_error()); - - // We want to provide better error messages to the user and thus, handle the error here - // separately. If there is an error, we print the error and still generate all of the code to - // get in overall less errors for the user. - let res = if let Err(error) = check_pallet_number_res { - let error = error.to_compile_error(); - - quote! { - #error - - #res - } - } else { - res - }; - - let res = expander::Expander::new("construct_runtime") - .dry(std::env::var("FRAME_EXPAND").is_err()) - .verbose(true) - .write_to_out_dir(res) - .expect("Does not fail because of IO in OUT_DIR; qed"); - - res.into() + let input = def.input; + + let (check_pallet_number_res, res) = match def.pallets { + AllPalletsDeclaration::Implicit(ref decl) => ( + check_pallet_number(input.clone(), decl.pallet_count), + construct_runtime_implicit_to_explicit(input.into(), decl.clone(), legacy_ordering), + ), + AllPalletsDeclaration::Explicit(ref decl) => ( + check_pallet_number(input, decl.pallets.len()), + construct_runtime_final_expansion( + def.runtime_struct.ident.clone(), + decl.clone(), + def.runtime_types.clone(), + legacy_ordering, + ), + ), + }; + + let res = res.unwrap_or_else(|e| e.to_compile_error()); + + // We want to provide better error messages to the user and thus, handle the error here + // separately. If there is an error, we print the error and still generate all of the code to + // get in overall less errors for the user. + let res = if let Err(error) = check_pallet_number_res { + let error = error.to_compile_error(); + + quote! { + #error + + #res + } + } else { + res + }; + + let res = expander::Expander::new("construct_runtime") + .dry(std::env::var("FRAME_EXPAND").is_err()) + .verbose(true) + .write_to_out_dir(res) + .expect("Does not fail because of IO in OUT_DIR; qed"); + + res.into() } fn construct_runtime_implicit_to_explicit( - input: TokenStream2, - definition: ImplicitAllPalletsDeclaration, - legacy_ordering: bool, + input: TokenStream2, + definition: ImplicitAllPalletsDeclaration, + legacy_ordering: bool, ) -> Result { - let frame_support = generate_access_from_frame_or_crate("frame-support")?; - let attr = if legacy_ordering { quote!((legacy_ordering)) } else { quote!() }; - let mut expansion = quote::quote!( - #[frame_support::runtime #attr] - #input - ); - for pallet in definition.pallet_decls.iter() { - let pallet_path = &pallet.path; - let pallet_name = &pallet.name; - let pallet_instance = pallet.instance.as_ref().map(|instance| quote::quote!(<#instance>)); - expansion = quote::quote!( - #frame_support::__private::tt_call! { - macro = [{ #pallet_path::tt_default_parts_v2 }] - frame_support = [{ #frame_support }] - ~~> #frame_support::match_and_insert! { - target = [{ #expansion }] - pattern = [{ #pallet_name = #pallet_path #pallet_instance }] - } - } - ); - } - - Ok(expansion) + let frame_support = generate_access_from_frame_or_crate("frame-support")?; + let attr = if legacy_ordering { + quote!((legacy_ordering)) + } else { + quote!() + }; + let mut expansion = quote::quote!( + #[frame_support::runtime #attr] + #input + ); + for pallet in definition.pallet_decls.iter() { + let pallet_path = &pallet.path; + let pallet_name = &pallet.name; + let pallet_instance = pallet + .instance + .as_ref() + .map(|instance| quote::quote!(<#instance>)); + expansion = quote::quote!( + #frame_support::__private::tt_call! { + macro = [{ #pallet_path::tt_default_parts_v2 }] + frame_support = [{ #frame_support }] + ~~> #frame_support::match_and_insert! { + target = [{ #expansion }] + pattern = [{ #pallet_name = #pallet_path #pallet_instance }] + } + } + ); + } + + Ok(expansion) } fn construct_runtime_final_expansion( - name: Ident, - definition: ExplicitAllPalletsDeclaration, - runtime_types: Vec, - legacy_ordering: bool, + name: Ident, + definition: ExplicitAllPalletsDeclaration, + runtime_types: Vec, + legacy_ordering: bool, ) -> Result { - let ExplicitAllPalletsDeclaration { mut pallets, name: pallets_name } = definition; - - if !legacy_ordering { - // Ensure that order of hooks is based on the pallet index - pallets.sort_by_key(|p| p.index); - } - - let system_pallet = - pallets.iter().find(|decl| decl.name == SYSTEM_PALLET_NAME).ok_or_else(|| { - syn::Error::new( - pallets_name.span(), - "`System` pallet declaration is missing. \ + let ExplicitAllPalletsDeclaration { + mut pallets, + name: pallets_name, + } = definition; + + if !legacy_ordering { + // Ensure that order of hooks is based on the pallet index + pallets.sort_by_key(|p| p.index); + } + + let system_pallet = pallets + .iter() + .find(|decl| decl.name == SYSTEM_PALLET_NAME) + .ok_or_else(|| { + syn::Error::new( + pallets_name.span(), + "`System` pallet declaration is missing. \ Please add this line: `pub type System = frame_system;`", - ) - })?; - if !system_pallet.cfg_pattern.is_empty() { - return Err(syn::Error::new( - system_pallet.name.span(), - "`System` pallet declaration is feature gated, please remove any `#[cfg]` attributes", - )) - } - - let features = pallets - .iter() - .filter_map(|decl| { - (!decl.cfg_pattern.is_empty()).then(|| { - decl.cfg_pattern.iter().flat_map(|attr| { - attr.predicates().filter_map(|pred| match pred { - Predicate::Feature(feat) => Some(feat), - Predicate::Test => Some("test"), - _ => None, - }) - }) - }) - }) - .flatten() - .collect::>(); - - 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 frame_system = generate_access_from_frame_or_crate("frame-system")?; - let block = quote!(<#name as #frame_system::Config>::Block); - let unchecked_extrinsic = quote!(<#block as #scrate::sp_runtime::traits::Block>::Extrinsic); - - let mut dispatch = None; - let mut outer_event = None; - let mut outer_error = None; - let mut outer_origin = None; - let mut freeze_reason = None; - let mut hold_reason = None; - let mut slash_reason = None; - let mut lock_id = None; - let mut task = None; - - for runtime_type in runtime_types.iter() { - match runtime_type { - RuntimeType::RuntimeCall(_) => { - dispatch = - Some(expand::expand_outer_dispatch(&name, system_pallet, &pallets, &scrate)); - }, - RuntimeType::RuntimeEvent(_) => { - outer_event = Some(expand::expand_outer_enum( - &name, - &pallets, - &scrate, - expand::OuterEnumType::Event, - )?); - }, - RuntimeType::RuntimeError(_) => { - outer_error = Some(expand::expand_outer_enum( - &name, - &pallets, - &scrate, - expand::OuterEnumType::Error, - )?); - }, - RuntimeType::RuntimeOrigin(_) => { - outer_origin = - Some(expand::expand_outer_origin(&name, system_pallet, &pallets, &scrate)?); - }, - RuntimeType::RuntimeFreezeReason(_) => { - freeze_reason = Some(expand::expand_outer_freeze_reason(&pallets, &scrate)); - }, - RuntimeType::RuntimeHoldReason(_) => { - hold_reason = Some(expand::expand_outer_hold_reason(&pallets, &scrate)); - }, - RuntimeType::RuntimeSlashReason(_) => { - slash_reason = Some(expand::expand_outer_slash_reason(&pallets, &scrate)); - }, - RuntimeType::RuntimeLockId(_) => { - lock_id = Some(expand::expand_outer_lock_id(&pallets, &scrate)); - }, - RuntimeType::RuntimeTask(_) => { - task = Some(expand::expand_outer_task(&name, &pallets, &scrate)); - }, - } - } - - let all_pallets = decl_all_pallets(&name, pallets.iter(), &features); - let pallet_to_index = decl_pallet_runtime_setup(&name, &pallets, &scrate); - - let metadata = expand::expand_runtime_metadata( - &name, - &pallets, - &scrate, - &unchecked_extrinsic, - &system_pallet.path, - ); - let outer_config = expand::expand_outer_config(&name, &pallets, &scrate); - let inherent = - expand::expand_outer_inherent(&name, &block, &unchecked_extrinsic, &pallets, &scrate); - let validate_unsigned = expand::expand_outer_validate_unsigned(&name, &pallets, &scrate); - let integrity_test = decl_integrity_test(&scrate); - let static_assertions = decl_static_assertions(&name, &pallets, &scrate); - - let res = quote!( - #scrate_decl - - // Prevent UncheckedExtrinsic to print unused warning. - const _: () = { - #[allow(unused)] - type __hidden_use_of_unchecked_extrinsic = #unchecked_extrinsic; - }; - - #[derive( - Clone, Copy, PartialEq, Eq, #scrate::sp_runtime::RuntimeDebug, - #scrate::__private::scale_info::TypeInfo - )] - pub struct #name; - impl #scrate::sp_runtime::traits::GetRuntimeBlockType for #name { - type RuntimeBlock = #block; - } - - // Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata. - // The function is implemented by calling `impl_runtime_apis!`. - // - // However, the `runtime` may be used without calling `impl_runtime_apis!`. - // Rely on the `Deref` trait to differentiate between a runtime that implements - // APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro runtime). - // - // Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function. - // `InternalConstructRuntime` is implemented by the `runtime` for Runtime references (`& Runtime`), - // while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`). - // - // Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!` - // when both macros are called; and will resolve an empty `runtime_metadata` when only the `runtime` - // is used. - - #[doc(hidden)] - trait InternalConstructRuntime { - #[inline(always)] - fn runtime_metadata(&self) -> #scrate::__private::sp_std::vec::Vec<#scrate::__private::metadata_ir::RuntimeApiMetadataIR> { - Default::default() - } - } - #[doc(hidden)] - impl InternalConstructRuntime for &#name {} - - #outer_event - - #outer_error - - #outer_origin - - #all_pallets - - #pallet_to_index - - #dispatch - - #task - - #metadata - - #outer_config - - #inherent - - #validate_unsigned - - #freeze_reason - - #hold_reason - - #lock_id - - #slash_reason - - #integrity_test - - #static_assertions - ); + ) + })?; + if !system_pallet.cfg_pattern.is_empty() { + return Err(syn::Error::new( + system_pallet.name.span(), + "`System` pallet declaration is feature gated, please remove any `#[cfg]` attributes", + )); + } + + let features = pallets + .iter() + .filter_map(|decl| { + (!decl.cfg_pattern.is_empty()).then(|| { + decl.cfg_pattern.iter().flat_map(|attr| { + attr.predicates().filter_map(|pred| match pred { + Predicate::Feature(feat) => Some(feat), + Predicate::Test => Some("test"), + _ => None, + }) + }) + }) + }) + .flatten() + .collect::>(); + + 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 frame_system = generate_access_from_frame_or_crate("frame-system")?; + let block = quote!(<#name as #frame_system::Config>::Block); + let unchecked_extrinsic = quote!(<#block as #scrate::sp_runtime::traits::Block>::Extrinsic); + + let mut dispatch = None; + let mut outer_event = None; + let mut outer_error = None; + let mut outer_origin = None; + let mut freeze_reason = None; + let mut hold_reason = None; + let mut slash_reason = None; + let mut lock_id = None; + let mut task = None; + + for runtime_type in runtime_types.iter() { + match runtime_type { + RuntimeType::RuntimeCall(_) => { + dispatch = Some(expand::expand_outer_dispatch( + &name, + system_pallet, + &pallets, + &scrate, + )); + } + RuntimeType::RuntimeEvent(_) => { + outer_event = Some(expand::expand_outer_enum( + &name, + &pallets, + &scrate, + expand::OuterEnumType::Event, + )?); + } + RuntimeType::RuntimeError(_) => { + outer_error = Some(expand::expand_outer_enum( + &name, + &pallets, + &scrate, + expand::OuterEnumType::Error, + )?); + } + RuntimeType::RuntimeOrigin(_) => { + outer_origin = Some(expand::expand_outer_origin( + &name, + system_pallet, + &pallets, + &scrate, + )?); + } + RuntimeType::RuntimeFreezeReason(_) => { + freeze_reason = Some(expand::expand_outer_freeze_reason(&pallets, &scrate)); + } + RuntimeType::RuntimeHoldReason(_) => { + hold_reason = Some(expand::expand_outer_hold_reason(&pallets, &scrate)); + } + RuntimeType::RuntimeSlashReason(_) => { + slash_reason = Some(expand::expand_outer_slash_reason(&pallets, &scrate)); + } + RuntimeType::RuntimeLockId(_) => { + lock_id = Some(expand::expand_outer_lock_id(&pallets, &scrate)); + } + RuntimeType::RuntimeTask(_) => { + task = Some(expand::expand_outer_task(&name, &pallets, &scrate)); + } + } + } + + let all_pallets = decl_all_pallets(&name, pallets.iter(), &features); + let pallet_to_index = decl_pallet_runtime_setup(&name, &pallets, &scrate); + + let metadata = expand::expand_runtime_metadata( + &name, + &pallets, + &scrate, + &unchecked_extrinsic, + &system_pallet.path, + ); + let outer_config = expand::expand_outer_config(&name, &pallets, &scrate); + let inherent = + expand::expand_outer_inherent(&name, &block, &unchecked_extrinsic, &pallets, &scrate); + let validate_unsigned = expand::expand_outer_validate_unsigned(&name, &pallets, &scrate); + let integrity_test = decl_integrity_test(&scrate); + let static_assertions = decl_static_assertions(&name, &pallets, &scrate); + + let res = quote!( + #scrate_decl + + // Prevent UncheckedExtrinsic to print unused warning. + const _: () = { + #[allow(unused)] + type __hidden_use_of_unchecked_extrinsic = #unchecked_extrinsic; + }; + + #[derive( + Clone, Copy, PartialEq, Eq, #scrate::sp_runtime::RuntimeDebug, + #scrate::__private::scale_info::TypeInfo + )] + pub struct #name; + impl #scrate::sp_runtime::traits::GetRuntimeBlockType for #name { + type RuntimeBlock = #block; + } + + // Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata. + // The function is implemented by calling `impl_runtime_apis!`. + // + // However, the `runtime` may be used without calling `impl_runtime_apis!`. + // Rely on the `Deref` trait to differentiate between a runtime that implements + // APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro runtime). + // + // Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function. + // `InternalConstructRuntime` is implemented by the `runtime` for Runtime references (`& Runtime`), + // while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`). + // + // Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!` + // when both macros are called; and will resolve an empty `runtime_metadata` when only the `runtime` + // is used. + + #[doc(hidden)] + trait InternalConstructRuntime { + #[inline(always)] + fn runtime_metadata(&self) -> #scrate::__private::sp_std::vec::Vec<#scrate::__private::metadata_ir::RuntimeApiMetadataIR> { + Default::default() + } + } + #[doc(hidden)] + impl InternalConstructRuntime for &#name {} + + #outer_event + + #outer_error + + #outer_origin + + #all_pallets + + #pallet_to_index + + #dispatch + + #task + + #metadata + + #outer_config + + #inherent + + #validate_unsigned + + #freeze_reason + + #hold_reason + + #lock_id + + #slash_reason + + #integrity_test + + #static_assertions + ); - Ok(res) + Ok(res) } diff --git a/support/procedural-fork/src/runtime/mod.rs b/support/procedural-fork/src/runtime/mod.rs index aaae579eb..589acff6c 100644 --- a/support/procedural-fork/src/runtime/mod.rs +++ b/support/procedural-fork/src/runtime/mod.rs @@ -210,27 +210,27 @@ mod expand; mod parse; mod keyword { - syn::custom_keyword!(legacy_ordering); + syn::custom_keyword!(legacy_ordering); } pub fn runtime(attr: TokenStream, tokens: TokenStream) -> TokenStream { - let mut legacy_ordering = false; - if !attr.is_empty() { - if let Ok(_) = syn::parse::(attr.clone()) { - legacy_ordering = true; - } else { - let msg = "Invalid runtime macro call: unexpected attribute. Macro call must be \ + let mut legacy_ordering = false; + if !attr.is_empty() { + if let Ok(_) = syn::parse::(attr.clone()) { + legacy_ordering = true; + } else { + let msg = "Invalid runtime macro call: unexpected attribute. Macro call must be \ bare, such as `#[frame_support::runtime]` or `#[runtime]`, or must specify the \ `legacy_ordering` attribute, such as `#[frame_support::runtime(legacy_ordering)]` or \ #[runtime(legacy_ordering)]."; - let span = proc_macro2::TokenStream::from(attr).span(); - return syn::Error::new(span, msg).to_compile_error().into() - } - } + let span = proc_macro2::TokenStream::from(attr).span(); + return syn::Error::new(span, msg).to_compile_error().into(); + } + } - let item = syn::parse_macro_input!(tokens as syn::ItemMod); - match parse::Def::try_from(item) { - Ok(def) => expand::expand(def, legacy_ordering).into(), - Err(e) => e.to_compile_error().into(), - } + let item = syn::parse_macro_input!(tokens as syn::ItemMod); + match parse::Def::try_from(item) { + Ok(def) => expand::expand(def, legacy_ordering).into(), + Err(e) => e.to_compile_error().into(), + } } diff --git a/support/procedural-fork/src/runtime/parse/helper.rs b/support/procedural-fork/src/runtime/parse/helper.rs index f05395f9b..17e362410 100644 --- a/support/procedural-fork/src/runtime/parse/helper.rs +++ b/support/procedural-fork/src/runtime/parse/helper.rs @@ -19,19 +19,26 @@ use crate::pallet::parse::helper::MutItemAttrs; use quote::ToTokens; pub(crate) fn take_first_item_runtime_attr( - item: &mut impl MutItemAttrs, + item: &mut impl MutItemAttrs, ) -> syn::Result> where - Attr: syn::parse::Parse, + Attr: syn::parse::Parse, { - let attrs = if let Some(attrs) = item.mut_item_attrs() { attrs } else { return Ok(None) }; + let attrs = if let Some(attrs) = item.mut_item_attrs() { + attrs + } else { + return Ok(None); + }; - if let Some(index) = attrs.iter().position(|attr| { - attr.path().segments.first().map_or(false, |segment| segment.ident == "runtime") - }) { - let runtime_attr = attrs.remove(index); - Ok(Some(syn::parse2(runtime_attr.into_token_stream())?)) - } else { - Ok(None) - } + if let Some(index) = attrs.iter().position(|attr| { + attr.path() + .segments + .first() + .map_or(false, |segment| segment.ident == "runtime") + }) { + let runtime_attr = attrs.remove(index); + Ok(Some(syn::parse2(runtime_attr.into_token_stream())?)) + } else { + Ok(None) + } } diff --git a/support/procedural-fork/src/runtime/parse/mod.rs b/support/procedural-fork/src/runtime/parse/mod.rs index 893cb4726..01245187f 100644 --- a/support/procedural-fork/src/runtime/parse/mod.rs +++ b/support/procedural-fork/src/runtime/parse/mod.rs @@ -32,220 +32,229 @@ use frame_support_procedural_tools::syn_ext as ext; use runtime_types::RuntimeType; mod keyword { - use syn::custom_keyword; + use syn::custom_keyword; - custom_keyword!(runtime); - custom_keyword!(derive); - custom_keyword!(pallet_index); - custom_keyword!(disable_call); - custom_keyword!(disable_unsigned); + custom_keyword!(runtime); + custom_keyword!(derive); + custom_keyword!(pallet_index); + custom_keyword!(disable_call); + custom_keyword!(disable_unsigned); } enum RuntimeAttr { - Runtime(proc_macro2::Span), - Derive(proc_macro2::Span, Vec), - PalletIndex(proc_macro2::Span, u8), - DisableCall(proc_macro2::Span), - DisableUnsigned(proc_macro2::Span), + Runtime(proc_macro2::Span), + Derive(proc_macro2::Span, Vec), + PalletIndex(proc_macro2::Span, u8), + DisableCall(proc_macro2::Span), + DisableUnsigned(proc_macro2::Span), } impl RuntimeAttr { - fn span(&self) -> proc_macro2::Span { - match self { - Self::Runtime(span) => *span, - Self::Derive(span, _) => *span, - Self::PalletIndex(span, _) => *span, - Self::DisableCall(span) => *span, - Self::DisableUnsigned(span) => *span, - } - } + fn span(&self) -> proc_macro2::Span { + match self { + Self::Runtime(span) => *span, + Self::Derive(span, _) => *span, + Self::PalletIndex(span, _) => *span, + Self::DisableCall(span) => *span, + Self::DisableUnsigned(span) => *span, + } + } } impl syn::parse::Parse for RuntimeAttr { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - let content; - syn::bracketed!(content in input); - content.parse::()?; - content.parse::()?; + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; - let lookahead = content.lookahead1(); - if lookahead.peek(keyword::runtime) { - Ok(RuntimeAttr::Runtime(content.parse::()?.span())) - } else if lookahead.peek(keyword::derive) { - let _ = content.parse::(); - let derive_content; - syn::parenthesized!(derive_content in content); - let runtime_types = - derive_content.parse::>()?; - let runtime_types = runtime_types.inner.into_iter().collect(); - Ok(RuntimeAttr::Derive(derive_content.span(), runtime_types)) - } else if lookahead.peek(keyword::pallet_index) { - let _ = content.parse::(); - let pallet_index_content; - syn::parenthesized!(pallet_index_content in content); - let pallet_index = pallet_index_content.parse::()?; - if !pallet_index.suffix().is_empty() { - let msg = "Number literal must not have a suffix"; - return Err(syn::Error::new(pallet_index.span(), msg)) - } - Ok(RuntimeAttr::PalletIndex(pallet_index.span(), pallet_index.base10_parse()?)) - } else if lookahead.peek(keyword::disable_call) { - Ok(RuntimeAttr::DisableCall(content.parse::()?.span())) - } else if lookahead.peek(keyword::disable_unsigned) { - Ok(RuntimeAttr::DisableUnsigned(content.parse::()?.span())) - } else { - Err(lookahead.error()) - } - } + let lookahead = content.lookahead1(); + if lookahead.peek(keyword::runtime) { + Ok(RuntimeAttr::Runtime( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::derive) { + let _ = content.parse::(); + let derive_content; + syn::parenthesized!(derive_content in content); + let runtime_types = + derive_content.parse::>()?; + let runtime_types = runtime_types.inner.into_iter().collect(); + Ok(RuntimeAttr::Derive(derive_content.span(), runtime_types)) + } else if lookahead.peek(keyword::pallet_index) { + let _ = content.parse::(); + let pallet_index_content; + syn::parenthesized!(pallet_index_content in content); + let pallet_index = pallet_index_content.parse::()?; + if !pallet_index.suffix().is_empty() { + let msg = "Number literal must not have a suffix"; + return Err(syn::Error::new(pallet_index.span(), msg)); + } + Ok(RuntimeAttr::PalletIndex( + pallet_index.span(), + pallet_index.base10_parse()?, + )) + } else if lookahead.peek(keyword::disable_call) { + Ok(RuntimeAttr::DisableCall( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::disable_unsigned) { + Ok(RuntimeAttr::DisableUnsigned( + content.parse::()?.span(), + )) + } else { + Err(lookahead.error()) + } + } } #[derive(Debug, Clone)] pub enum AllPalletsDeclaration { - Implicit(ImplicitAllPalletsDeclaration), - Explicit(ExplicitAllPalletsDeclaration), + Implicit(ImplicitAllPalletsDeclaration), + Explicit(ExplicitAllPalletsDeclaration), } /// Declaration of a runtime with some pallet with implicit declaration of parts. #[derive(Debug, Clone)] pub struct ImplicitAllPalletsDeclaration { - pub name: Ident, - pub pallet_decls: Vec, - pub pallet_count: usize, + pub name: Ident, + pub pallet_decls: Vec, + pub pallet_count: usize, } /// Declaration of a runtime with all pallet having explicit declaration of parts. #[derive(Debug, Clone)] pub struct ExplicitAllPalletsDeclaration { - pub name: Ident, - pub pallets: Vec, + pub name: Ident, + pub pallets: Vec, } pub struct Def { - pub input: TokenStream2, - pub item: syn::ItemMod, - pub runtime_struct: runtime_struct::RuntimeStructDef, - pub pallets: AllPalletsDeclaration, - pub runtime_types: Vec, + pub input: TokenStream2, + pub item: syn::ItemMod, + pub runtime_struct: runtime_struct::RuntimeStructDef, + pub pallets: AllPalletsDeclaration, + pub runtime_types: Vec, } impl Def { - pub fn try_from(mut item: syn::ItemMod) -> syn::Result { - let input: TokenStream2 = item.to_token_stream().into(); - let item_span = item.span(); - let items = &mut item - .content - .as_mut() - .ok_or_else(|| { - let msg = "Invalid runtime definition, expected mod to be inlined."; - syn::Error::new(item_span, msg) - })? - .1; + pub fn try_from(mut item: syn::ItemMod) -> syn::Result { + let input: TokenStream2 = item.to_token_stream().into(); + let item_span = item.span(); + let items = &mut item + .content + .as_mut() + .ok_or_else(|| { + let msg = "Invalid runtime definition, expected mod to be inlined."; + syn::Error::new(item_span, msg) + })? + .1; - let mut runtime_struct = None; - let mut runtime_types = None; + let mut runtime_struct = None; + let mut runtime_types = None; - let mut indices = HashMap::new(); - let mut names = HashMap::new(); + let mut indices = HashMap::new(); + let mut names = HashMap::new(); - let mut pallet_decls = vec![]; - let mut pallets = vec![]; + let mut pallet_decls = vec![]; + let mut pallets = vec![]; - for item in items.iter_mut() { - let mut pallet_item = None; - let mut pallet_index = 0; + for item in items.iter_mut() { + let mut pallet_item = None; + let mut pallet_index = 0; - let mut disable_call = false; - let mut disable_unsigned = false; + let mut disable_call = false; + let mut disable_unsigned = false; - while let Some(runtime_attr) = - helper::take_first_item_runtime_attr::(item)? - { - match runtime_attr { - RuntimeAttr::Runtime(span) if runtime_struct.is_none() => { - let p = runtime_struct::RuntimeStructDef::try_from(span, item)?; - runtime_struct = Some(p); - }, - RuntimeAttr::Derive(_, types) if runtime_types.is_none() => { - runtime_types = Some(types); - }, - RuntimeAttr::PalletIndex(span, index) => { - pallet_index = index; - pallet_item = if let syn::Item::Type(item) = item { - Some(item.clone()) - } else { - let msg = "Invalid runtime::pallet_index, expected type definition"; - return Err(syn::Error::new(span, msg)) - }; - }, - RuntimeAttr::DisableCall(_) => disable_call = true, - RuntimeAttr::DisableUnsigned(_) => disable_unsigned = true, - attr => { - let msg = "Invalid duplicated attribute"; - return Err(syn::Error::new(attr.span(), msg)) - }, - } - } + while let Some(runtime_attr) = + helper::take_first_item_runtime_attr::(item)? + { + match runtime_attr { + RuntimeAttr::Runtime(span) if runtime_struct.is_none() => { + let p = runtime_struct::RuntimeStructDef::try_from(span, item)?; + runtime_struct = Some(p); + } + RuntimeAttr::Derive(_, types) if runtime_types.is_none() => { + runtime_types = Some(types); + } + RuntimeAttr::PalletIndex(span, index) => { + pallet_index = index; + pallet_item = if let syn::Item::Type(item) = item { + Some(item.clone()) + } else { + let msg = "Invalid runtime::pallet_index, expected type definition"; + return Err(syn::Error::new(span, msg)); + }; + } + RuntimeAttr::DisableCall(_) => disable_call = true, + RuntimeAttr::DisableUnsigned(_) => disable_unsigned = true, + attr => { + let msg = "Invalid duplicated attribute"; + return Err(syn::Error::new(attr.span(), msg)); + } + } + } - if let Some(pallet_item) = pallet_item { - match *pallet_item.ty.clone() { - syn::Type::Path(ref path) => { - let pallet_decl = - PalletDeclaration::try_from(item.span(), &pallet_item, path)?; + if let Some(pallet_item) = pallet_item { + match *pallet_item.ty.clone() { + syn::Type::Path(ref path) => { + let pallet_decl = + PalletDeclaration::try_from(item.span(), &pallet_item, path)?; - if let Some(used_pallet) = - names.insert(pallet_decl.name.clone(), pallet_decl.name.span()) - { - let msg = "Two pallets with the same name!"; + if let Some(used_pallet) = + names.insert(pallet_decl.name.clone(), pallet_decl.name.span()) + { + let msg = "Two pallets with the same name!"; - let mut err = syn::Error::new(used_pallet, &msg); - err.combine(syn::Error::new(pallet_decl.name.span(), &msg)); - return Err(err) - } + let mut err = syn::Error::new(used_pallet, &msg); + err.combine(syn::Error::new(pallet_decl.name.span(), &msg)); + return Err(err); + } - pallet_decls.push(pallet_decl); - }, - syn::Type::TraitObject(syn::TypeTraitObject { bounds, .. }) => { - let pallet = Pallet::try_from( - item.span(), - &pallet_item, - pallet_index, - disable_call, - disable_unsigned, - &bounds, - )?; + pallet_decls.push(pallet_decl); + } + syn::Type::TraitObject(syn::TypeTraitObject { bounds, .. }) => { + let pallet = Pallet::try_from( + item.span(), + &pallet_item, + pallet_index, + disable_call, + disable_unsigned, + &bounds, + )?; - if let Some(used_pallet) = indices.insert(pallet.index, pallet.name.clone()) - { - let msg = format!( + if let Some(used_pallet) = indices.insert(pallet.index, pallet.name.clone()) + { + let msg = format!( "Pallet indices are conflicting: Both pallets {} and {} are at index {}", used_pallet, pallet.name, pallet.index, ); - let mut err = syn::Error::new(used_pallet.span(), &msg); - err.combine(syn::Error::new(pallet.name.span(), msg)); - return Err(err) - } + let mut err = syn::Error::new(used_pallet.span(), &msg); + err.combine(syn::Error::new(pallet.name.span(), msg)); + return Err(err); + } - pallets.push(pallet); - }, - _ => continue, - } - } - } + pallets.push(pallet); + } + _ => continue, + } + } + } - let name = item.ident.clone(); - let decl_count = pallet_decls.len(); - let pallets = if decl_count > 0 { - AllPalletsDeclaration::Implicit(ImplicitAllPalletsDeclaration { - name, - pallet_decls, - pallet_count: decl_count.saturating_add(pallets.len()), - }) - } else { - AllPalletsDeclaration::Explicit(ExplicitAllPalletsDeclaration { name, pallets }) - }; + let name = item.ident.clone(); + let decl_count = pallet_decls.len(); + let pallets = if decl_count > 0 { + AllPalletsDeclaration::Implicit(ImplicitAllPalletsDeclaration { + name, + pallet_decls, + pallet_count: decl_count.saturating_add(pallets.len()), + }) + } else { + AllPalletsDeclaration::Explicit(ExplicitAllPalletsDeclaration { name, pallets }) + }; - let def = Def { + let def = Def { input, item, runtime_struct: runtime_struct.ok_or_else(|| { @@ -261,6 +270,6 @@ impl Def { })?, }; - Ok(def) - } + Ok(def) + } } diff --git a/support/procedural-fork/src/runtime/parse/pallet.rs b/support/procedural-fork/src/runtime/parse/pallet.rs index d2f1857fb..039e2631b 100644 --- a/support/procedural-fork/src/runtime/parse/pallet.rs +++ b/support/procedural-fork/src/runtime/parse/pallet.rs @@ -20,80 +20,88 @@ use quote::ToTokens; use syn::{punctuated::Punctuated, spanned::Spanned, token, Error, Ident, PathArguments}; impl Pallet { - pub fn try_from( - attr_span: proc_macro2::Span, - item: &syn::ItemType, - pallet_index: u8, - disable_call: bool, - disable_unsigned: bool, - bounds: &Punctuated, - ) -> syn::Result { - let name = item.ident.clone(); + pub fn try_from( + attr_span: proc_macro2::Span, + item: &syn::ItemType, + pallet_index: u8, + disable_call: bool, + disable_unsigned: bool, + bounds: &Punctuated, + ) -> syn::Result { + let name = item.ident.clone(); - let mut pallet_path = None; - let mut pallet_parts = vec![]; + let mut pallet_path = None; + let mut pallet_parts = vec![]; - for (index, bound) in bounds.into_iter().enumerate() { - if let syn::TypeParamBound::Trait(syn::TraitBound { path, .. }) = bound { - if index == 0 { - pallet_path = Some(PalletPath { inner: path.clone() }); - } else { - let pallet_part = syn::parse2::(bound.into_token_stream())?; - pallet_parts.push(pallet_part); - } - } else { - return Err(Error::new( - attr_span, - "Invalid pallet declaration, expected a path or a trait object", - )) - }; - } + for (index, bound) in bounds.into_iter().enumerate() { + if let syn::TypeParamBound::Trait(syn::TraitBound { path, .. }) = bound { + if index == 0 { + pallet_path = Some(PalletPath { + inner: path.clone(), + }); + } else { + let pallet_part = syn::parse2::(bound.into_token_stream())?; + pallet_parts.push(pallet_part); + } + } else { + return Err(Error::new( + attr_span, + "Invalid pallet declaration, expected a path or a trait object", + )); + }; + } - let mut path = pallet_path.ok_or(Error::new( - attr_span, - "Invalid pallet declaration, expected a path or a trait object", - ))?; + let mut path = pallet_path.ok_or(Error::new( + attr_span, + "Invalid pallet declaration, expected a path or a trait object", + ))?; - let mut instance = None; - if let Some(segment) = path.inner.segments.iter_mut().find(|seg| !seg.arguments.is_empty()) - { - if let PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { - args, .. - }) = segment.arguments.clone() - { - if let Some(syn::GenericArgument::Type(syn::Type::Path(arg_path))) = args.first() { - instance = - Some(Ident::new(&arg_path.to_token_stream().to_string(), arg_path.span())); - segment.arguments = PathArguments::None; - } - } - } + let mut instance = None; + if let Some(segment) = path + .inner + .segments + .iter_mut() + .find(|seg| !seg.arguments.is_empty()) + { + if let PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { + args, .. + }) = segment.arguments.clone() + { + if let Some(syn::GenericArgument::Type(syn::Type::Path(arg_path))) = args.first() { + instance = Some(Ident::new( + &arg_path.to_token_stream().to_string(), + arg_path.span(), + )); + segment.arguments = PathArguments::None; + } + } + } - pallet_parts = pallet_parts - .into_iter() - .filter(|part| { - if let (true, &PalletPartKeyword::Call(_)) = (disable_call, &part.keyword) { - false - } else if let (true, &PalletPartKeyword::ValidateUnsigned(_)) = - (disable_unsigned, &part.keyword) - { - false - } else { - true - } - }) - .collect(); + pallet_parts = pallet_parts + .into_iter() + .filter(|part| { + if let (true, &PalletPartKeyword::Call(_)) = (disable_call, &part.keyword) { + false + } else if let (true, &PalletPartKeyword::ValidateUnsigned(_)) = + (disable_unsigned, &part.keyword) + { + false + } else { + true + } + }) + .collect(); - let cfg_pattern = vec![]; + let cfg_pattern = vec![]; - Ok(Pallet { - is_expanded: true, - name, - index: pallet_index, - path, - instance, - cfg_pattern, - pallet_parts, - }) - } + Ok(Pallet { + is_expanded: true, + name, + index: pallet_index, + path, + instance, + cfg_pattern, + pallet_parts, + }) + } } diff --git a/support/procedural-fork/src/runtime/parse/pallet_decl.rs b/support/procedural-fork/src/runtime/parse/pallet_decl.rs index 437a163cf..bb1246606 100644 --- a/support/procedural-fork/src/runtime/parse/pallet_decl.rs +++ b/support/procedural-fork/src/runtime/parse/pallet_decl.rs @@ -21,40 +21,51 @@ use syn::{spanned::Spanned, Attribute, Ident, PathArguments}; /// The declaration of a pallet. #[derive(Debug, Clone)] pub struct PalletDeclaration { - /// The name of the pallet, e.g.`System` in `System: frame_system`. - pub name: Ident, - /// Optional attributes tagged right above a pallet declaration. - pub attrs: Vec, - /// The path of the pallet, e.g. `frame_system` in `System: frame_system`. - pub path: syn::Path, - /// The instance of the pallet, e.g. `Instance1` in `Council: pallet_collective::`. - pub instance: Option, + /// The name of the pallet, e.g.`System` in `System: frame_system`. + pub name: Ident, + /// Optional attributes tagged right above a pallet declaration. + pub attrs: Vec, + /// The path of the pallet, e.g. `frame_system` in `System: frame_system`. + pub path: syn::Path, + /// The instance of the pallet, e.g. `Instance1` in `Council: pallet_collective::`. + pub instance: Option, } impl PalletDeclaration { - pub fn try_from( - _attr_span: proc_macro2::Span, - item: &syn::ItemType, - path: &syn::TypePath, - ) -> syn::Result { - let name = item.ident.clone(); - - let mut path = path.path.clone(); - - let mut instance = None; - if let Some(segment) = path.segments.iter_mut().find(|seg| !seg.arguments.is_empty()) { - if let PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { - args, .. - }) = segment.arguments.clone() - { - if let Some(syn::GenericArgument::Type(syn::Type::Path(arg_path))) = args.first() { - instance = - Some(Ident::new(&arg_path.to_token_stream().to_string(), arg_path.span())); - segment.arguments = PathArguments::None; - } - } - } - - Ok(Self { name, path, instance, attrs: item.attrs.clone() }) - } + pub fn try_from( + _attr_span: proc_macro2::Span, + item: &syn::ItemType, + path: &syn::TypePath, + ) -> syn::Result { + let name = item.ident.clone(); + + let mut path = path.path.clone(); + + let mut instance = None; + if let Some(segment) = path + .segments + .iter_mut() + .find(|seg| !seg.arguments.is_empty()) + { + if let PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { + args, .. + }) = segment.arguments.clone() + { + if let Some(syn::GenericArgument::Type(syn::Type::Path(arg_path))) = args.first() { + instance = Some(Ident::new( + &arg_path.to_token_stream().to_string(), + arg_path.span(), + )); + segment.arguments = PathArguments::None; + } + } + } + + Ok(Self { + name, + path, + instance, + attrs: item.attrs.clone(), + }) + } } diff --git a/support/procedural-fork/src/runtime/parse/runtime_struct.rs b/support/procedural-fork/src/runtime/parse/runtime_struct.rs index 8fa746ee8..7ddbdcfeb 100644 --- a/support/procedural-fork/src/runtime/parse/runtime_struct.rs +++ b/support/procedural-fork/src/runtime/parse/runtime_struct.rs @@ -17,19 +17,22 @@ use syn::spanned::Spanned; pub struct RuntimeStructDef { - pub ident: syn::Ident, - pub attr_span: proc_macro2::Span, + pub ident: syn::Ident, + pub attr_span: proc_macro2::Span, } impl RuntimeStructDef { - pub fn try_from(attr_span: proc_macro2::Span, item: &mut syn::Item) -> syn::Result { - let item = if let syn::Item::Struct(item) = item { - item - } else { - let msg = "Invalid runtime::runtime, expected struct definition"; - return Err(syn::Error::new(item.span(), msg)) - }; + pub fn try_from(attr_span: proc_macro2::Span, item: &mut syn::Item) -> syn::Result { + let item = if let syn::Item::Struct(item) = item { + item + } else { + let msg = "Invalid runtime::runtime, expected struct definition"; + return Err(syn::Error::new(item.span(), msg)); + }; - Ok(Self { ident: item.ident.clone(), attr_span }) - } + Ok(Self { + ident: item.ident.clone(), + attr_span, + }) + } } diff --git a/support/procedural-fork/src/runtime/parse/runtime_types.rs b/support/procedural-fork/src/runtime/parse/runtime_types.rs index a4480e2a1..4d8c8358c 100644 --- a/support/procedural-fork/src/runtime/parse/runtime_types.rs +++ b/support/procedural-fork/src/runtime/parse/runtime_types.rs @@ -16,61 +16,61 @@ // limitations under the License. use syn::{ - parse::{Parse, ParseStream}, - Result, + parse::{Parse, ParseStream}, + Result, }; mod keyword { - use syn::custom_keyword; + use syn::custom_keyword; - custom_keyword!(RuntimeCall); - custom_keyword!(RuntimeEvent); - custom_keyword!(RuntimeError); - custom_keyword!(RuntimeOrigin); - custom_keyword!(RuntimeFreezeReason); - custom_keyword!(RuntimeHoldReason); - custom_keyword!(RuntimeSlashReason); - custom_keyword!(RuntimeLockId); - custom_keyword!(RuntimeTask); + custom_keyword!(RuntimeCall); + custom_keyword!(RuntimeEvent); + custom_keyword!(RuntimeError); + custom_keyword!(RuntimeOrigin); + custom_keyword!(RuntimeFreezeReason); + custom_keyword!(RuntimeHoldReason); + custom_keyword!(RuntimeSlashReason); + custom_keyword!(RuntimeLockId); + custom_keyword!(RuntimeTask); } #[derive(Debug, Clone, PartialEq)] pub enum RuntimeType { - RuntimeCall(keyword::RuntimeCall), - RuntimeEvent(keyword::RuntimeEvent), - RuntimeError(keyword::RuntimeError), - RuntimeOrigin(keyword::RuntimeOrigin), - RuntimeFreezeReason(keyword::RuntimeFreezeReason), - RuntimeHoldReason(keyword::RuntimeHoldReason), - RuntimeSlashReason(keyword::RuntimeSlashReason), - RuntimeLockId(keyword::RuntimeLockId), - RuntimeTask(keyword::RuntimeTask), + RuntimeCall(keyword::RuntimeCall), + RuntimeEvent(keyword::RuntimeEvent), + RuntimeError(keyword::RuntimeError), + RuntimeOrigin(keyword::RuntimeOrigin), + RuntimeFreezeReason(keyword::RuntimeFreezeReason), + RuntimeHoldReason(keyword::RuntimeHoldReason), + RuntimeSlashReason(keyword::RuntimeSlashReason), + RuntimeLockId(keyword::RuntimeLockId), + RuntimeTask(keyword::RuntimeTask), } impl Parse for RuntimeType { - fn parse(input: ParseStream) -> Result { - let lookahead = input.lookahead1(); + fn parse(input: ParseStream) -> Result { + let lookahead = input.lookahead1(); - if lookahead.peek(keyword::RuntimeCall) { - Ok(Self::RuntimeCall(input.parse()?)) - } else if lookahead.peek(keyword::RuntimeEvent) { - Ok(Self::RuntimeEvent(input.parse()?)) - } else if lookahead.peek(keyword::RuntimeError) { - Ok(Self::RuntimeError(input.parse()?)) - } else if lookahead.peek(keyword::RuntimeOrigin) { - Ok(Self::RuntimeOrigin(input.parse()?)) - } else if lookahead.peek(keyword::RuntimeFreezeReason) { - Ok(Self::RuntimeFreezeReason(input.parse()?)) - } else if lookahead.peek(keyword::RuntimeHoldReason) { - Ok(Self::RuntimeHoldReason(input.parse()?)) - } else if lookahead.peek(keyword::RuntimeSlashReason) { - Ok(Self::RuntimeSlashReason(input.parse()?)) - } else if lookahead.peek(keyword::RuntimeLockId) { - Ok(Self::RuntimeLockId(input.parse()?)) - } else if lookahead.peek(keyword::RuntimeTask) { - Ok(Self::RuntimeTask(input.parse()?)) - } else { - Err(lookahead.error()) - } - } + if lookahead.peek(keyword::RuntimeCall) { + Ok(Self::RuntimeCall(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeEvent) { + Ok(Self::RuntimeEvent(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeError) { + Ok(Self::RuntimeError(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeOrigin) { + Ok(Self::RuntimeOrigin(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeFreezeReason) { + Ok(Self::RuntimeFreezeReason(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeHoldReason) { + Ok(Self::RuntimeHoldReason(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeSlashReason) { + Ok(Self::RuntimeSlashReason(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeLockId) { + Ok(Self::RuntimeLockId(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeTask) { + Ok(Self::RuntimeTask(input.parse()?)) + } else { + Err(lookahead.error()) + } + } } diff --git a/support/procedural-fork/src/storage_alias.rs b/support/procedural-fork/src/storage_alias.rs index 06f62768f..7099239f9 100644 --- a/support/procedural-fork/src/storage_alias.rs +++ b/support/procedural-fork/src/storage_alias.rs @@ -22,655 +22,688 @@ use frame_support_procedural_tools::generate_access_from_frame_or_crate; use proc_macro2::{Span, TokenStream}; use quote::{quote, ToTokens}; use syn::{ - parenthesized, - parse::{Parse, ParseStream}, - punctuated::Punctuated, - spanned::Spanned, - token, - visit::Visit, - Attribute, Error, Ident, Result, Token, Type, TypeParam, Visibility, WhereClause, + parenthesized, + parse::{Parse, ParseStream}, + punctuated::Punctuated, + spanned::Spanned, + token, + visit::Visit, + Attribute, Error, Ident, Result, Token, Type, TypeParam, Visibility, WhereClause, }; /// Extension trait for [`Type`]. trait TypeExt { - fn get_ident(&self) -> Option<&Ident>; - fn contains_ident(&self, ident: &Ident) -> bool; + fn get_ident(&self) -> Option<&Ident>; + fn contains_ident(&self, ident: &Ident) -> bool; } impl TypeExt for Type { - fn get_ident(&self) -> Option<&Ident> { - match self { - Type::Path(p) => match &p.qself { - Some(qself) => qself.ty.get_ident(), - None => p.path.get_ident(), - }, - _ => None, - } - } - - fn contains_ident(&self, ident: &Ident) -> bool { - struct ContainsIdent<'a> { - ident: &'a Ident, - found: bool, - } - impl<'a, 'ast> Visit<'ast> for ContainsIdent<'a> { - fn visit_ident(&mut self, i: &'ast Ident) { - if i == self.ident { - self.found = true; - } - } - } - - let mut visitor = ContainsIdent { ident, found: false }; - syn::visit::visit_type(&mut visitor, self); - visitor.found - } + fn get_ident(&self) -> Option<&Ident> { + match self { + Type::Path(p) => match &p.qself { + Some(qself) => qself.ty.get_ident(), + None => p.path.get_ident(), + }, + _ => None, + } + } + + fn contains_ident(&self, ident: &Ident) -> bool { + struct ContainsIdent<'a> { + ident: &'a Ident, + found: bool, + } + impl<'a, 'ast> Visit<'ast> for ContainsIdent<'a> { + fn visit_ident(&mut self, i: &'ast Ident) { + if i == self.ident { + self.found = true; + } + } + } + + let mut visitor = ContainsIdent { + ident, + found: false, + }; + syn::visit::visit_type(&mut visitor, self); + visitor.found + } } /// Represents generics which only support [`TypeParam`] separated by commas. struct SimpleGenerics { - lt_token: Token![<], - params: Punctuated, - gt_token: Token![>], + lt_token: Token![<], + params: Punctuated, + gt_token: Token![>], } impl SimpleGenerics { - /// Returns the generics for types declarations etc. - fn type_generics(&self) -> impl Iterator { - self.params.iter().map(|p| &p.ident) - } - - /// Returns the generics for the `impl` block. - fn impl_generics(&self) -> impl Iterator { - self.params.iter() - } + /// Returns the generics for types declarations etc. + fn type_generics(&self) -> impl Iterator { + self.params.iter().map(|p| &p.ident) + } + + /// Returns the generics for the `impl` block. + fn impl_generics(&self) -> impl Iterator { + self.params.iter() + } } impl Parse for SimpleGenerics { - fn parse(input: ParseStream<'_>) -> Result { - Ok(Self { - lt_token: input.parse()?, - params: Punctuated::parse_separated_nonempty(input)?, - gt_token: input.parse()?, - }) - } + fn parse(input: ParseStream<'_>) -> Result { + Ok(Self { + lt_token: input.parse()?, + params: Punctuated::parse_separated_nonempty(input)?, + gt_token: input.parse()?, + }) + } } impl ToTokens for SimpleGenerics { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.lt_token.to_tokens(tokens); - self.params.to_tokens(tokens); - self.gt_token.to_tokens(tokens); - } + fn to_tokens(&self, tokens: &mut TokenStream) { + self.lt_token.to_tokens(tokens); + self.params.to_tokens(tokens); + self.gt_token.to_tokens(tokens); + } } mod storage_types { - syn::custom_keyword!(StorageValue); - syn::custom_keyword!(StorageMap); - syn::custom_keyword!(CountedStorageMap); - syn::custom_keyword!(StorageDoubleMap); - syn::custom_keyword!(StorageNMap); + syn::custom_keyword!(StorageValue); + syn::custom_keyword!(StorageMap); + syn::custom_keyword!(CountedStorageMap); + syn::custom_keyword!(StorageDoubleMap); + syn::custom_keyword!(StorageNMap); } /// The types of prefixes the storage alias macro supports. mod prefix_types { - // Use the verbatim/unmodified input name as the prefix. - syn::custom_keyword!(verbatim); - // The input type is a pallet and its pallet name should be used as the prefix. - syn::custom_keyword!(pallet_name); - // The input type implements `Get<'static str>` and this `str` should be used as the prefix. - syn::custom_keyword!(dynamic); + // Use the verbatim/unmodified input name as the prefix. + syn::custom_keyword!(verbatim); + // The input type is a pallet and its pallet name should be used as the prefix. + syn::custom_keyword!(pallet_name); + // The input type implements `Get<'static str>` and this `str` should be used as the prefix. + syn::custom_keyword!(dynamic); } /// The supported storage types enum StorageType { - Value { - _kw: storage_types::StorageValue, - _lt_token: Token![<], - prefix: Type, - _value_comma: Token![,], - value_ty: Type, - query_type: Option<(Token![,], Type)>, - _trailing_comma: Option, - _gt_token: Token![>], - }, - Map { - _kw: storage_types::StorageMap, - _lt_token: Token![<], - prefix: Type, - _hasher_comma: Token![,], - hasher_ty: Type, - _key_comma: Token![,], - key_ty: Type, - _value_comma: Token![,], - value_ty: Type, - query_type: Option<(Token![,], Type)>, - _trailing_comma: Option, - _gt_token: Token![>], - }, - CountedMap { - _kw: storage_types::CountedStorageMap, - _lt_token: Token![<], - prefix: Type, - _hasher_comma: Token![,], - hasher_ty: Type, - _key_comma: Token![,], - key_ty: Type, - _value_comma: Token![,], - value_ty: Type, - query_type: Option<(Token![,], Type)>, - _trailing_comma: Option, - _gt_token: Token![>], - }, - DoubleMap { - _kw: storage_types::StorageDoubleMap, - _lt_token: Token![<], - prefix: Type, - _hasher1_comma: Token![,], - hasher1_ty: Type, - _key1_comma: Token![,], - key1_ty: Type, - _hasher2_comma: Token![,], - hasher2_ty: Type, - _key2_comma: Token![,], - key2_ty: Type, - _value_comma: Token![,], - value_ty: Type, - query_type: Option<(Token![,], Type)>, - _trailing_comma: Option, - _gt_token: Token![>], - }, - NMap { - _kw: storage_types::StorageNMap, - _lt_token: Token![<], - prefix: Type, - _paren_comma: Token![,], - _paren_token: token::Paren, - key_types: Punctuated, - _value_comma: Token![,], - value_ty: Type, - query_type: Option<(Token![,], Type)>, - _trailing_comma: Option, - _gt_token: Token![>], - }, + Value { + _kw: storage_types::StorageValue, + _lt_token: Token![<], + prefix: Type, + _value_comma: Token![,], + value_ty: Type, + query_type: Option<(Token![,], Type)>, + _trailing_comma: Option, + _gt_token: Token![>], + }, + Map { + _kw: storage_types::StorageMap, + _lt_token: Token![<], + prefix: Type, + _hasher_comma: Token![,], + hasher_ty: Type, + _key_comma: Token![,], + key_ty: Type, + _value_comma: Token![,], + value_ty: Type, + query_type: Option<(Token![,], Type)>, + _trailing_comma: Option, + _gt_token: Token![>], + }, + CountedMap { + _kw: storage_types::CountedStorageMap, + _lt_token: Token![<], + prefix: Type, + _hasher_comma: Token![,], + hasher_ty: Type, + _key_comma: Token![,], + key_ty: Type, + _value_comma: Token![,], + value_ty: Type, + query_type: Option<(Token![,], Type)>, + _trailing_comma: Option, + _gt_token: Token![>], + }, + DoubleMap { + _kw: storage_types::StorageDoubleMap, + _lt_token: Token![<], + prefix: Type, + _hasher1_comma: Token![,], + hasher1_ty: Type, + _key1_comma: Token![,], + key1_ty: Type, + _hasher2_comma: Token![,], + hasher2_ty: Type, + _key2_comma: Token![,], + key2_ty: Type, + _value_comma: Token![,], + value_ty: Type, + query_type: Option<(Token![,], Type)>, + _trailing_comma: Option, + _gt_token: Token![>], + }, + NMap { + _kw: storage_types::StorageNMap, + _lt_token: Token![<], + prefix: Type, + _paren_comma: Token![,], + _paren_token: token::Paren, + key_types: Punctuated, + _value_comma: Token![,], + value_ty: Type, + query_type: Option<(Token![,], Type)>, + _trailing_comma: Option, + _gt_token: Token![>], + }, } impl StorageType { - /// Generate the actual type declaration. - fn generate_type_declaration( - &self, - crate_: &syn::Path, - storage_instance: &StorageInstance, - storage_name: &Ident, - storage_generics: Option<&SimpleGenerics>, - visibility: &Visibility, - attributes: &[Attribute], - ) -> TokenStream { - let storage_instance_generics = &storage_instance.generics; - let storage_instance = &storage_instance.name; - let attributes = attributes.iter(); - let storage_generics = storage_generics.map(|g| { - let generics = g.type_generics(); - - quote!( < #( #generics ),* > ) - }); - - match self { - Self::Value { value_ty, query_type, .. } => { - let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); - - quote! { - #( #attributes )* - #visibility type #storage_name #storage_generics = #crate_::storage::types::StorageValue< - #storage_instance #storage_instance_generics, - #value_ty - #query_type - >; - } - }, - Self::CountedMap { value_ty, query_type, hasher_ty, key_ty, .. } | - Self::Map { value_ty, query_type, hasher_ty, key_ty, .. } => { - let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); - let map_type = Ident::new( - match self { - Self::Map { .. } => "StorageMap", - _ => "CountedStorageMap", - }, - Span::call_site(), - ); - - quote! { - #( #attributes )* - #visibility type #storage_name #storage_generics = #crate_::storage::types::#map_type< - #storage_instance #storage_instance_generics, - #hasher_ty, - #key_ty, - #value_ty - #query_type - >; - } - }, - Self::DoubleMap { - value_ty, - query_type, - hasher1_ty, - key1_ty, - hasher2_ty, - key2_ty, - .. - } => { - let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); - - quote! { - #( #attributes )* - #visibility type #storage_name #storage_generics = #crate_::storage::types::StorageDoubleMap< - #storage_instance #storage_instance_generics, - #hasher1_ty, - #key1_ty, - #hasher2_ty, - #key2_ty, - #value_ty - #query_type - >; - } - }, - Self::NMap { value_ty, query_type, key_types, .. } => { - let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); - let key_types = key_types.iter(); - - quote! { - #( #attributes )* - #visibility type #storage_name #storage_generics = #crate_::storage::types::StorageNMap< - #storage_instance #storage_instance_generics, - ( #( #key_types ),* ), - #value_ty - #query_type - >; - } - }, - } - } - - /// The prefix for this storage type. - fn prefix(&self) -> &Type { - match self { - Self::Value { prefix, .. } | - Self::Map { prefix, .. } | - Self::CountedMap { prefix, .. } | - Self::NMap { prefix, .. } | - Self::DoubleMap { prefix, .. } => prefix, - } - } + /// Generate the actual type declaration. + fn generate_type_declaration( + &self, + crate_: &syn::Path, + storage_instance: &StorageInstance, + storage_name: &Ident, + storage_generics: Option<&SimpleGenerics>, + visibility: &Visibility, + attributes: &[Attribute], + ) -> TokenStream { + let storage_instance_generics = &storage_instance.generics; + let storage_instance = &storage_instance.name; + let attributes = attributes.iter(); + let storage_generics = storage_generics.map(|g| { + let generics = g.type_generics(); + + quote!( < #( #generics ),* > ) + }); + + match self { + Self::Value { + value_ty, + query_type, + .. + } => { + let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); + + quote! { + #( #attributes )* + #visibility type #storage_name #storage_generics = #crate_::storage::types::StorageValue< + #storage_instance #storage_instance_generics, + #value_ty + #query_type + >; + } + } + Self::CountedMap { + value_ty, + query_type, + hasher_ty, + key_ty, + .. + } + | Self::Map { + value_ty, + query_type, + hasher_ty, + key_ty, + .. + } => { + let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); + let map_type = Ident::new( + match self { + Self::Map { .. } => "StorageMap", + _ => "CountedStorageMap", + }, + Span::call_site(), + ); + + quote! { + #( #attributes )* + #visibility type #storage_name #storage_generics = #crate_::storage::types::#map_type< + #storage_instance #storage_instance_generics, + #hasher_ty, + #key_ty, + #value_ty + #query_type + >; + } + } + Self::DoubleMap { + value_ty, + query_type, + hasher1_ty, + key1_ty, + hasher2_ty, + key2_ty, + .. + } => { + let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); + + quote! { + #( #attributes )* + #visibility type #storage_name #storage_generics = #crate_::storage::types::StorageDoubleMap< + #storage_instance #storage_instance_generics, + #hasher1_ty, + #key1_ty, + #hasher2_ty, + #key2_ty, + #value_ty + #query_type + >; + } + } + Self::NMap { + value_ty, + query_type, + key_types, + .. + } => { + let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); + let key_types = key_types.iter(); + + quote! { + #( #attributes )* + #visibility type #storage_name #storage_generics = #crate_::storage::types::StorageNMap< + #storage_instance #storage_instance_generics, + ( #( #key_types ),* ), + #value_ty + #query_type + >; + } + } + } + } + + /// The prefix for this storage type. + fn prefix(&self) -> &Type { + match self { + Self::Value { prefix, .. } + | Self::Map { prefix, .. } + | Self::CountedMap { prefix, .. } + | Self::NMap { prefix, .. } + | Self::DoubleMap { prefix, .. } => prefix, + } + } } impl Parse for StorageType { - fn parse(input: ParseStream<'_>) -> Result { - let lookahead = input.lookahead1(); - - let parse_query_type = |input: ParseStream<'_>| -> Result> { - if input.peek(Token![,]) && !input.peek2(Token![>]) { - Ok(Some((input.parse()?, input.parse()?))) - } else { - Ok(None) - } - }; - - if lookahead.peek(storage_types::StorageValue) { - Ok(Self::Value { - _kw: input.parse()?, - _lt_token: input.parse()?, - prefix: input.parse()?, - _value_comma: input.parse()?, - value_ty: input.parse()?, - query_type: parse_query_type(input)?, - _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, - _gt_token: input.parse()?, - }) - } else if lookahead.peek(storage_types::StorageMap) { - Ok(Self::Map { - _kw: input.parse()?, - _lt_token: input.parse()?, - prefix: input.parse()?, - _hasher_comma: input.parse()?, - hasher_ty: input.parse()?, - _key_comma: input.parse()?, - key_ty: input.parse()?, - _value_comma: input.parse()?, - value_ty: input.parse()?, - query_type: parse_query_type(input)?, - _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, - _gt_token: input.parse()?, - }) - } else if lookahead.peek(storage_types::CountedStorageMap) { - Ok(Self::CountedMap { - _kw: input.parse()?, - _lt_token: input.parse()?, - prefix: input.parse()?, - _hasher_comma: input.parse()?, - hasher_ty: input.parse()?, - _key_comma: input.parse()?, - key_ty: input.parse()?, - _value_comma: input.parse()?, - value_ty: input.parse()?, - query_type: parse_query_type(input)?, - _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, - _gt_token: input.parse()?, - }) - } else if lookahead.peek(storage_types::StorageDoubleMap) { - Ok(Self::DoubleMap { - _kw: input.parse()?, - _lt_token: input.parse()?, - prefix: input.parse()?, - _hasher1_comma: input.parse()?, - hasher1_ty: input.parse()?, - _key1_comma: input.parse()?, - key1_ty: input.parse()?, - _hasher2_comma: input.parse()?, - hasher2_ty: input.parse()?, - _key2_comma: input.parse()?, - key2_ty: input.parse()?, - _value_comma: input.parse()?, - value_ty: input.parse()?, - query_type: parse_query_type(input)?, - _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, - _gt_token: input.parse()?, - }) - } else if lookahead.peek(storage_types::StorageNMap) { - let content; - Ok(Self::NMap { - _kw: input.parse()?, - _lt_token: input.parse()?, - prefix: input.parse()?, - _paren_comma: input.parse()?, - _paren_token: parenthesized!(content in input), - key_types: Punctuated::parse_terminated(&content)?, - _value_comma: input.parse()?, - value_ty: input.parse()?, - query_type: parse_query_type(input)?, - _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, - _gt_token: input.parse()?, - }) - } else { - Err(lookahead.error()) - } - } + fn parse(input: ParseStream<'_>) -> Result { + let lookahead = input.lookahead1(); + + let parse_query_type = |input: ParseStream<'_>| -> Result> { + if input.peek(Token![,]) && !input.peek2(Token![>]) { + Ok(Some((input.parse()?, input.parse()?))) + } else { + Ok(None) + } + }; + + if lookahead.peek(storage_types::StorageValue) { + Ok(Self::Value { + _kw: input.parse()?, + _lt_token: input.parse()?, + prefix: input.parse()?, + _value_comma: input.parse()?, + value_ty: input.parse()?, + query_type: parse_query_type(input)?, + _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, + _gt_token: input.parse()?, + }) + } else if lookahead.peek(storage_types::StorageMap) { + Ok(Self::Map { + _kw: input.parse()?, + _lt_token: input.parse()?, + prefix: input.parse()?, + _hasher_comma: input.parse()?, + hasher_ty: input.parse()?, + _key_comma: input.parse()?, + key_ty: input.parse()?, + _value_comma: input.parse()?, + value_ty: input.parse()?, + query_type: parse_query_type(input)?, + _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, + _gt_token: input.parse()?, + }) + } else if lookahead.peek(storage_types::CountedStorageMap) { + Ok(Self::CountedMap { + _kw: input.parse()?, + _lt_token: input.parse()?, + prefix: input.parse()?, + _hasher_comma: input.parse()?, + hasher_ty: input.parse()?, + _key_comma: input.parse()?, + key_ty: input.parse()?, + _value_comma: input.parse()?, + value_ty: input.parse()?, + query_type: parse_query_type(input)?, + _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, + _gt_token: input.parse()?, + }) + } else if lookahead.peek(storage_types::StorageDoubleMap) { + Ok(Self::DoubleMap { + _kw: input.parse()?, + _lt_token: input.parse()?, + prefix: input.parse()?, + _hasher1_comma: input.parse()?, + hasher1_ty: input.parse()?, + _key1_comma: input.parse()?, + key1_ty: input.parse()?, + _hasher2_comma: input.parse()?, + hasher2_ty: input.parse()?, + _key2_comma: input.parse()?, + key2_ty: input.parse()?, + _value_comma: input.parse()?, + value_ty: input.parse()?, + query_type: parse_query_type(input)?, + _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, + _gt_token: input.parse()?, + }) + } else if lookahead.peek(storage_types::StorageNMap) { + let content; + Ok(Self::NMap { + _kw: input.parse()?, + _lt_token: input.parse()?, + prefix: input.parse()?, + _paren_comma: input.parse()?, + _paren_token: parenthesized!(content in input), + key_types: Punctuated::parse_terminated(&content)?, + _value_comma: input.parse()?, + value_ty: input.parse()?, + query_type: parse_query_type(input)?, + _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, + _gt_token: input.parse()?, + }) + } else { + Err(lookahead.error()) + } + } } /// The input expected by this macro. struct Input { - attributes: Vec, - visibility: Visibility, - _type: Token![type], - storage_name: Ident, - storage_generics: Option, - where_clause: Option, - _equal: Token![=], - storage_type: StorageType, - _semicolon: Token![;], + attributes: Vec, + visibility: Visibility, + _type: Token![type], + storage_name: Ident, + storage_generics: Option, + where_clause: Option, + _equal: Token![=], + storage_type: StorageType, + _semicolon: Token![;], } impl Parse for Input { - fn parse(input: ParseStream<'_>) -> Result { - let attributes = input.call(Attribute::parse_outer)?; - let visibility = input.parse()?; - let _type = input.parse()?; - let storage_name = input.parse()?; - - let lookahead = input.lookahead1(); - let storage_generics = if lookahead.peek(Token![<]) { - Some(input.parse()?) - } else if lookahead.peek(Token![=]) { - None - } else { - return Err(lookahead.error()) - }; - - let lookahead = input.lookahead1(); - let where_clause = if lookahead.peek(Token![where]) { - Some(input.parse()?) - } else if lookahead.peek(Token![=]) { - None - } else { - return Err(lookahead.error()) - }; - - let _equal = input.parse()?; - - let storage_type = input.parse()?; - - let _semicolon = input.parse()?; - - Ok(Self { - attributes, - visibility, - _type, - storage_name, - storage_generics, - _equal, - storage_type, - where_clause, - _semicolon, - }) - } + fn parse(input: ParseStream<'_>) -> Result { + let attributes = input.call(Attribute::parse_outer)?; + let visibility = input.parse()?; + let _type = input.parse()?; + let storage_name = input.parse()?; + + let lookahead = input.lookahead1(); + let storage_generics = if lookahead.peek(Token![<]) { + Some(input.parse()?) + } else if lookahead.peek(Token![=]) { + None + } else { + return Err(lookahead.error()); + }; + + let lookahead = input.lookahead1(); + let where_clause = if lookahead.peek(Token![where]) { + Some(input.parse()?) + } else if lookahead.peek(Token![=]) { + None + } else { + return Err(lookahead.error()); + }; + + let _equal = input.parse()?; + + let storage_type = input.parse()?; + + let _semicolon = input.parse()?; + + Ok(Self { + attributes, + visibility, + _type, + storage_name, + storage_generics, + _equal, + storage_type, + where_clause, + _semicolon, + }) + } } /// Defines which type of prefix the storage alias is using. #[derive(Clone, Copy)] enum PrefixType { - /// An appropriate prefix will be determined automatically. - /// - /// If generics are passed, this is assumed to be a pallet and the pallet name should be used. - /// Otherwise use the verbatim passed name as prefix. - Compatibility, - /// The provided ident/name will be used as the prefix. - Verbatim, - /// The provided type will be used to determine the prefix. This type must - /// implement `PalletInfoAccess` which specifies the proper name. This - /// name is then used as the prefix. - PalletName, - /// Uses the provided type implementing `Get<'static str>` to determine the prefix. - Dynamic, + /// An appropriate prefix will be determined automatically. + /// + /// If generics are passed, this is assumed to be a pallet and the pallet name should be used. + /// Otherwise use the verbatim passed name as prefix. + Compatibility, + /// The provided ident/name will be used as the prefix. + Verbatim, + /// The provided type will be used to determine the prefix. This type must + /// implement `PalletInfoAccess` which specifies the proper name. This + /// name is then used as the prefix. + PalletName, + /// Uses the provided type implementing `Get<'static str>` to determine the prefix. + Dynamic, } /// Implementation of the `storage_alias` attribute macro. pub fn storage_alias(attributes: TokenStream, input: TokenStream) -> Result { - let input = syn::parse2::(input)?; - let crate_ = generate_access_from_frame_or_crate("frame-support")?; - - let prefix_type = if attributes.is_empty() { - PrefixType::Compatibility - } else if syn::parse2::(attributes.clone()).is_ok() { - PrefixType::Verbatim - } else if syn::parse2::(attributes.clone()).is_ok() { - PrefixType::PalletName - } else if syn::parse2::(attributes.clone()).is_ok() { - PrefixType::Dynamic - } else { - return Err(Error::new(attributes.span(), "Unknown attributes")) - }; - - let storage_instance = generate_storage_instance( - &crate_, - &input.storage_name, - input.storage_generics.as_ref(), - input.where_clause.as_ref(), - input.storage_type.prefix(), - &input.visibility, - matches!(input.storage_type, StorageType::CountedMap { .. }), - prefix_type, - )?; - - let definition = input.storage_type.generate_type_declaration( - &crate_, - &storage_instance, - &input.storage_name, - input.storage_generics.as_ref(), - &input.visibility, - &input.attributes, - ); - - let storage_instance_code = storage_instance.code; - - Ok(quote! { - #storage_instance_code - - #definition - }) + let input = syn::parse2::(input)?; + let crate_ = generate_access_from_frame_or_crate("frame-support")?; + + let prefix_type = if attributes.is_empty() { + PrefixType::Compatibility + } else if syn::parse2::(attributes.clone()).is_ok() { + PrefixType::Verbatim + } else if syn::parse2::(attributes.clone()).is_ok() { + PrefixType::PalletName + } else if syn::parse2::(attributes.clone()).is_ok() { + PrefixType::Dynamic + } else { + return Err(Error::new(attributes.span(), "Unknown attributes")); + }; + + let storage_instance = generate_storage_instance( + &crate_, + &input.storage_name, + input.storage_generics.as_ref(), + input.where_clause.as_ref(), + input.storage_type.prefix(), + &input.visibility, + matches!(input.storage_type, StorageType::CountedMap { .. }), + prefix_type, + )?; + + let definition = input.storage_type.generate_type_declaration( + &crate_, + &storage_instance, + &input.storage_name, + input.storage_generics.as_ref(), + &input.visibility, + &input.attributes, + ); + + let storage_instance_code = storage_instance.code; + + Ok(quote! { + #storage_instance_code + + #definition + }) } /// The storage instance to use for the storage alias. struct StorageInstance { - name: Ident, - generics: TokenStream, - code: TokenStream, + name: Ident, + generics: TokenStream, + code: TokenStream, } /// Generate the [`StorageInstance`] for the storage alias. fn generate_storage_instance( - crate_: &syn::Path, - storage_name: &Ident, - storage_generics: Option<&SimpleGenerics>, - storage_where_clause: Option<&WhereClause>, - prefix: &Type, - visibility: &Visibility, - is_counted_map: bool, - prefix_type: PrefixType, + crate_: &syn::Path, + storage_name: &Ident, + storage_generics: Option<&SimpleGenerics>, + storage_where_clause: Option<&WhereClause>, + prefix: &Type, + visibility: &Visibility, + is_counted_map: bool, + prefix_type: PrefixType, ) -> Result { - if let Type::Infer(_) = prefix { - return Err(Error::new(prefix.span(), "`_` is not allowed as prefix by `storage_alias`.")) - } - - let impl_generics_used_by_prefix = storage_generics - .as_ref() - .map(|g| { - g.impl_generics() - .filter(|g| prefix.contains_ident(&g.ident)) - .collect::>() - }) - .unwrap_or_default(); - - let (pallet_prefix, impl_generics, type_generics) = match prefix_type { - PrefixType::Compatibility => - if !impl_generics_used_by_prefix.is_empty() { - let type_generics = impl_generics_used_by_prefix.iter().map(|g| &g.ident); - let impl_generics = impl_generics_used_by_prefix.iter(); - - ( - quote! { - < #prefix as #crate_::traits::PalletInfoAccess>::name() - }, - quote!( #( #impl_generics ),* ), - quote!( #( #type_generics ),* ), - ) - } else if let Some(prefix) = prefix.get_ident() { - let prefix_str = prefix.to_string(); - - (quote!(#prefix_str), quote!(), quote!()) - } else { - return Err(Error::new_spanned( - prefix, - "If there are no generics, the prefix is only allowed to be an identifier.", - )) - }, - PrefixType::Verbatim => { - let prefix_str = match prefix.get_ident() { - Some(p) => p.to_string(), - None => - return Err(Error::new_spanned( - prefix, - "Prefix type `verbatim` requires that the prefix is an ident.", - )), - }; - - (quote!(#prefix_str), quote!(), quote!()) - }, - PrefixType::PalletName => { - let type_generics = impl_generics_used_by_prefix.iter().map(|g| &g.ident); - let impl_generics = impl_generics_used_by_prefix.iter(); - - ( - quote! { - <#prefix as #crate_::traits::PalletInfoAccess>::name() - }, - quote!( #( #impl_generics ),* ), - quote!( #( #type_generics ),* ), - ) - }, - PrefixType::Dynamic => { - let type_generics = impl_generics_used_by_prefix.iter().map(|g| &g.ident); - let impl_generics = impl_generics_used_by_prefix.iter(); - - ( - quote! { - <#prefix as #crate_::traits::Get<_>>::get() - }, - quote!( #( #impl_generics ),* ), - quote!( #( #type_generics ),* ), - ) - }, - }; - - let where_clause = storage_where_clause.map(|w| quote!(#w)).unwrap_or_default(); - - let name_str = format!("{}_Storage_Instance", storage_name); - let name = Ident::new(&name_str, Span::call_site()); - let storage_name_str = storage_name.to_string(); - - let counter_code = is_counted_map.then(|| { - let counter_name = Ident::new(&counter_prefix(&name_str), Span::call_site()); - let counter_storage_name_str = counter_prefix(&storage_name_str); - let storage_prefix_hash = helper::two128_str(&counter_storage_name_str); - - quote! { - #visibility struct #counter_name< #impl_generics >( - ::core::marker::PhantomData<(#type_generics)> - ) #where_clause; - - impl<#impl_generics> #crate_::traits::StorageInstance - for #counter_name< #type_generics > #where_clause - { - fn pallet_prefix() -> &'static str { - #pallet_prefix - } - - const STORAGE_PREFIX: &'static str = #counter_storage_name_str; - fn storage_prefix_hash() -> [u8; 16] { - #storage_prefix_hash - } - } - - impl<#impl_generics> #crate_::storage::types::CountedStorageMapInstance - for #name< #type_generics > #where_clause - { - type CounterPrefix = #counter_name < #type_generics >; - } - } - }); - - let storage_prefix_hash = helper::two128_str(&storage_name_str); - - // Implement `StorageInstance` trait. - let code = quote! { - #[allow(non_camel_case_types)] - #visibility struct #name< #impl_generics >( - ::core::marker::PhantomData<(#type_generics)> - ) #where_clause; - - impl<#impl_generics> #crate_::traits::StorageInstance - for #name< #type_generics > #where_clause - { - fn pallet_prefix() -> &'static str { - #pallet_prefix - } - - const STORAGE_PREFIX: &'static str = #storage_name_str; - fn storage_prefix_hash() -> [u8; 16] { - #storage_prefix_hash - } - } - - #counter_code - }; - - Ok(StorageInstance { name, code, generics: quote!( < #type_generics > ) }) + if let Type::Infer(_) = prefix { + return Err(Error::new( + prefix.span(), + "`_` is not allowed as prefix by `storage_alias`.", + )); + } + + let impl_generics_used_by_prefix = storage_generics + .as_ref() + .map(|g| { + g.impl_generics() + .filter(|g| prefix.contains_ident(&g.ident)) + .collect::>() + }) + .unwrap_or_default(); + + let (pallet_prefix, impl_generics, type_generics) = match prefix_type { + PrefixType::Compatibility => { + if !impl_generics_used_by_prefix.is_empty() { + let type_generics = impl_generics_used_by_prefix.iter().map(|g| &g.ident); + let impl_generics = impl_generics_used_by_prefix.iter(); + + ( + quote! { + < #prefix as #crate_::traits::PalletInfoAccess>::name() + }, + quote!( #( #impl_generics ),* ), + quote!( #( #type_generics ),* ), + ) + } else if let Some(prefix) = prefix.get_ident() { + let prefix_str = prefix.to_string(); + + (quote!(#prefix_str), quote!(), quote!()) + } else { + return Err(Error::new_spanned( + prefix, + "If there are no generics, the prefix is only allowed to be an identifier.", + )); + } + } + PrefixType::Verbatim => { + let prefix_str = match prefix.get_ident() { + Some(p) => p.to_string(), + None => { + return Err(Error::new_spanned( + prefix, + "Prefix type `verbatim` requires that the prefix is an ident.", + )) + } + }; + + (quote!(#prefix_str), quote!(), quote!()) + } + PrefixType::PalletName => { + let type_generics = impl_generics_used_by_prefix.iter().map(|g| &g.ident); + let impl_generics = impl_generics_used_by_prefix.iter(); + + ( + quote! { + <#prefix as #crate_::traits::PalletInfoAccess>::name() + }, + quote!( #( #impl_generics ),* ), + quote!( #( #type_generics ),* ), + ) + } + PrefixType::Dynamic => { + let type_generics = impl_generics_used_by_prefix.iter().map(|g| &g.ident); + let impl_generics = impl_generics_used_by_prefix.iter(); + + ( + quote! { + <#prefix as #crate_::traits::Get<_>>::get() + }, + quote!( #( #impl_generics ),* ), + quote!( #( #type_generics ),* ), + ) + } + }; + + let where_clause = storage_where_clause.map(|w| quote!(#w)).unwrap_or_default(); + + let name_str = format!("{}_Storage_Instance", storage_name); + let name = Ident::new(&name_str, Span::call_site()); + let storage_name_str = storage_name.to_string(); + + let counter_code = is_counted_map.then(|| { + let counter_name = Ident::new(&counter_prefix(&name_str), Span::call_site()); + let counter_storage_name_str = counter_prefix(&storage_name_str); + let storage_prefix_hash = helper::two128_str(&counter_storage_name_str); + + quote! { + #visibility struct #counter_name< #impl_generics >( + ::core::marker::PhantomData<(#type_generics)> + ) #where_clause; + + impl<#impl_generics> #crate_::traits::StorageInstance + for #counter_name< #type_generics > #where_clause + { + fn pallet_prefix() -> &'static str { + #pallet_prefix + } + + const STORAGE_PREFIX: &'static str = #counter_storage_name_str; + fn storage_prefix_hash() -> [u8; 16] { + #storage_prefix_hash + } + } + + impl<#impl_generics> #crate_::storage::types::CountedStorageMapInstance + for #name< #type_generics > #where_clause + { + type CounterPrefix = #counter_name < #type_generics >; + } + } + }); + + let storage_prefix_hash = helper::two128_str(&storage_name_str); + + // Implement `StorageInstance` trait. + let code = quote! { + #[allow(non_camel_case_types)] + #visibility struct #name< #impl_generics >( + ::core::marker::PhantomData<(#type_generics)> + ) #where_clause; + + impl<#impl_generics> #crate_::traits::StorageInstance + for #name< #type_generics > #where_clause + { + fn pallet_prefix() -> &'static str { + #pallet_prefix + } + + const STORAGE_PREFIX: &'static str = #storage_name_str; + fn storage_prefix_hash() -> [u8; 16] { + #storage_prefix_hash + } + } + + #counter_code + }; + + Ok(StorageInstance { + name, + code, + generics: quote!( < #type_generics > ), + }) } diff --git a/support/procedural-fork/src/transactional.rs b/support/procedural-fork/src/transactional.rs index e9d4f84b7..73a841d9b 100644 --- a/support/procedural-fork/src/transactional.rs +++ b/support/procedural-fork/src/transactional.rs @@ -21,40 +21,50 @@ use quote::quote; use syn::{ItemFn, Result}; pub fn transactional(_attr: TokenStream, input: TokenStream) -> Result { - let ItemFn { attrs, vis, sig, block } = syn::parse(input)?; - - let crate_ = generate_access_from_frame_or_crate("frame-support")?; - let output = quote! { - #(#attrs)* - #vis #sig { - use #crate_::storage::{with_transaction, TransactionOutcome}; - with_transaction(|| { - let r = (|| { #block })(); - if r.is_ok() { - TransactionOutcome::Commit(r) - } else { - TransactionOutcome::Rollback(r) - } - }) - } - }; - - Ok(output.into()) + let ItemFn { + attrs, + vis, + sig, + block, + } = syn::parse(input)?; + + let crate_ = generate_access_from_frame_or_crate("frame-support")?; + let output = quote! { + #(#attrs)* + #vis #sig { + use #crate_::storage::{with_transaction, TransactionOutcome}; + with_transaction(|| { + let r = (|| { #block })(); + if r.is_ok() { + TransactionOutcome::Commit(r) + } else { + TransactionOutcome::Rollback(r) + } + }) + } + }; + + Ok(output.into()) } pub fn require_transactional(_attr: TokenStream, input: TokenStream) -> Result { - let ItemFn { attrs, vis, sig, block } = syn::parse(input)?; - - let crate_ = generate_access_from_frame_or_crate("frame-support")?; - let output = quote! { - #(#attrs)* - #vis #sig { - if !#crate_::storage::transactional::is_transactional() { - return Err(#crate_::sp_runtime::TransactionalError::NoLayer.into()); - } - #block - } - }; - - Ok(output.into()) + let ItemFn { + attrs, + vis, + sig, + block, + } = syn::parse(input)?; + + let crate_ = generate_access_from_frame_or_crate("frame-support")?; + let output = quote! { + #(#attrs)* + #vis #sig { + if !#crate_::storage::transactional::is_transactional() { + return Err(#crate_::sp_runtime::TransactionalError::NoLayer.into()); + } + #block + } + }; + + Ok(output.into()) } diff --git a/support/procedural-fork/src/tt_macro.rs b/support/procedural-fork/src/tt_macro.rs index d37127421..3f280013f 100644 --- a/support/procedural-fork/src/tt_macro.rs +++ b/support/procedural-fork/src/tt_macro.rs @@ -22,29 +22,29 @@ use proc_macro2::{Ident, TokenStream}; use quote::format_ident; struct CreateTtReturnMacroDef { - name: Ident, - args: Vec<(Ident, TokenStream)>, + name: Ident, + args: Vec<(Ident, TokenStream)>, } impl syn::parse::Parse for CreateTtReturnMacroDef { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let name = input.parse()?; - let _ = input.parse::()?; + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let name = input.parse()?; + let _ = input.parse::()?; - let mut args = Vec::new(); - while !input.is_empty() { - let mut value; - let key: Ident = input.parse()?; - let _ = input.parse::()?; - let _: syn::token::Bracket = syn::bracketed!(value in input); - let _: syn::token::Brace = syn::braced!(value in value); - let value: TokenStream = value.parse()?; + let mut args = Vec::new(); + while !input.is_empty() { + let mut value; + let key: Ident = input.parse()?; + let _ = input.parse::()?; + let _: syn::token::Bracket = syn::bracketed!(value in input); + let _: syn::token::Brace = syn::braced!(value in value); + let value: TokenStream = value.parse()?; - args.push((key, value)) - } + args.push((key, value)) + } - Ok(Self { name, args }) - } + Ok(Self { name, args }) + } } /// A proc macro that accepts a name and any number of key-value pairs, to be used to create a @@ -74,32 +74,32 @@ impl syn::parse::Parse for CreateTtReturnMacroDef { /// } /// ``` pub fn create_tt_return_macro(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let CreateTtReturnMacroDef { name, args } = - syn::parse_macro_input!(input as CreateTtReturnMacroDef); + let CreateTtReturnMacroDef { name, args } = + syn::parse_macro_input!(input as CreateTtReturnMacroDef); - let (keys, values): (Vec<_>, Vec<_>) = args.into_iter().unzip(); - let count = COUNTER.with(|counter| counter.borrow_mut().inc()); - let unique_name = format_ident!("{}_{}", name, count); + let (keys, values): (Vec<_>, Vec<_>) = args.into_iter().unzip(); + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + let unique_name = format_ident!("{}_{}", name, count); - let decl_macro = quote::quote! { - #[macro_export] - #[doc(hidden)] - macro_rules! #unique_name { - { - $caller:tt - $(your_tt_return = [{ $my_tt_macro:path }])? - } => { - $my_tt_return! { - $caller - #( - #keys = [{ #values }] - )* - } - } - } + let decl_macro = quote::quote! { + #[macro_export] + #[doc(hidden)] + macro_rules! #unique_name { + { + $caller:tt + $(your_tt_return = [{ $my_tt_macro:path }])? + } => { + $my_tt_return! { + $caller + #( + #keys = [{ #values }] + )* + } + } + } - pub use #unique_name as #name; - }; + pub use #unique_name as #name; + }; - decl_macro.into() + decl_macro.into() } From b998f4bffe80f2ab219dd5f585f914ca4c5ae319 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 19 Sep 2024 01:57:44 -0400 Subject: [PATCH 34/53] suppress warnings in test mode --- support/procedural-fork/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/support/procedural-fork/src/lib.rs b/support/procedural-fork/src/lib.rs index 2ac076636..71e8ecba8 100644 --- a/support/procedural-fork/src/lib.rs +++ b/support/procedural-fork/src/lib.rs @@ -11,6 +11,7 @@ //! version/tag name. #![recursion_limit = "512"] #![deny(rustdoc::broken_intra_doc_links)] +#![allow(unused)] extern crate proc_macro; From a1b057c2fd2974e0e269d9237a72891b4bf0a28b Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 19 Sep 2024 02:06:38 -0400 Subject: [PATCH 35/53] cargo clippy --fix --workspace --all-features --- support/procedural-fork/src/benchmark.rs | 28 +++++++++---------- .../construct_runtime/expand/freeze_reason.rs | 2 +- .../construct_runtime/expand/hold_reason.rs | 2 +- .../src/construct_runtime/expand/lock_id.rs | 2 +- .../construct_runtime/expand/slash_reason.rs | 2 +- .../src/construct_runtime/expand/task.rs | 2 +- .../src/construct_runtime/mod.rs | 20 ++++--------- .../src/construct_runtime/parse.rs | 6 ++-- support/procedural-fork/src/derive_impl.rs | 4 +-- support/procedural-fork/src/dynamic_params.rs | 10 ++----- support/procedural-fork/src/lib.rs | 2 +- .../procedural-fork/src/no_bound/default.rs | 2 +- .../src/pallet/expand/config.rs | 2 +- .../src/pallet/expand/documentation.rs | 4 +-- .../src/pallet/expand/storage.rs | 2 +- .../src/pallet/expand/tasks.rs | 2 +- .../src/pallet/expand/warnings.rs | 2 +- support/procedural-fork/src/pallet/mod.rs | 2 +- .../procedural-fork/src/pallet/parse/call.rs | 6 ++-- .../src/pallet/parse/config.rs | 2 +- .../src/pallet/parse/helper.rs | 7 ++--- .../procedural-fork/src/pallet/parse/mod.rs | 12 ++++---- .../src/pallet/parse/storage.rs | 4 +-- .../procedural-fork/src/pallet/parse/tasks.rs | 12 ++++---- support/procedural-fork/src/pallet_error.rs | 4 +-- .../procedural-fork/src/runtime/expand/mod.rs | 19 +++++-------- support/procedural-fork/src/runtime/mod.rs | 2 +- .../procedural-fork/src/runtime/parse/mod.rs | 6 ++-- .../src/runtime/parse/pallet.rs | 7 ++--- 29 files changed, 76 insertions(+), 101 deletions(-) diff --git a/support/procedural-fork/src/benchmark.rs b/support/procedural-fork/src/benchmark.rs index 376200d6e..a47d175af 100644 --- a/support/procedural-fork/src/benchmark.rs +++ b/support/procedural-fork/src/benchmark.rs @@ -166,7 +166,7 @@ impl syn::parse::Parse for PovEstimationMode { let lookahead = input.lookahead1(); if lookahead.peek(keywords::MaxEncodedLen) { let _max_encoded_len: keywords::MaxEncodedLen = input.parse()?; - return Ok(PovEstimationMode::MaxEncodedLen); + Ok(PovEstimationMode::MaxEncodedLen) } else if lookahead.peek(keywords::Measured) { let _measured: keywords::Measured = input.parse()?; return Ok(PovEstimationMode::Measured); @@ -204,7 +204,7 @@ impl syn::parse::Parse for BenchmarkAttrs { let mut extra = false; let mut skip_meta = false; let mut pov_mode = None; - let args = Punctuated::::parse_terminated(&input)?; + let args = Punctuated::::parse_terminated(input)?; for arg in args.into_iter() { match arg { @@ -294,7 +294,7 @@ struct ResultDef { /// Ensures that `ReturnType` is a `Result<(), BenchmarkError>`, if specified fn ensure_valid_return_type(item_fn: &ItemFn) -> Result<()> { if let ReturnType::Type(_, typ) = &item_fn.sig.output { - let non_unit = |span| return Err(Error::new(span, "expected `()`")); + let non_unit = |span| Err(Error::new(span, "expected `()`")); let Type::Path(TypePath { path, qself: _ }) = &**typ else { return Err(Error::new( typ.span(), @@ -328,10 +328,10 @@ fn parse_params(item_fn: &ItemFn) -> Result> { let mut params: Vec = Vec::new(); for arg in &item_fn.sig.inputs { let invalid_param = |span| { - return Err(Error::new( + Err(Error::new( span, "Invalid benchmark function param. A valid example would be `x: Linear<5, 10>`.", - )); + )) }; let FnArg::Typed(arg) = arg else { @@ -344,10 +344,10 @@ fn parse_params(item_fn: &ItemFn) -> Result> { // check param name let var_span = ident.span(); let invalid_param_name = || { - return Err(Error::new( + Err(Error::new( var_span, "Benchmark parameter names must consist of a single lowercase letter (a-z) and no other characters.", - )); + )) }; let name = ident.ident.to_token_stream().to_string(); if name.len() > 1 { @@ -385,10 +385,10 @@ fn parse_params(item_fn: &ItemFn) -> Result> { /// Used in several places where the `#[extrinsic_call]` or `#[body]` annotation is missing fn missing_call(item_fn: &ItemFn) -> Result { - return Err(Error::new( + Err(Error::new( item_fn.block.brace_token.span.join(), "No valid #[extrinsic_call] or #[block] annotation could be found in benchmark function body." - )); + )) } /// Finds the `BenchmarkCallDef` and its index (within the list of stmts for the fn) and @@ -447,7 +447,7 @@ impl BenchmarkDef { pub fn from(item_fn: &ItemFn) -> Result { let params = parse_params(item_fn)?; ensure_valid_return_type(item_fn)?; - let (i, call_def) = parse_call_def(&item_fn)?; + let (i, call_def) = parse_call_def(item_fn)?; let (verify_stmts, last_stmt) = match item_fn.sig.output { ReturnType::Default => @@ -961,11 +961,11 @@ fn expand_benchmark( // set up variables needed during quoting let krate = match generate_access_from_frame_or_crate("frame-benchmarking") { Ok(ident) => ident, - Err(err) => return err.to_compile_error().into(), + Err(err) => return err.to_compile_error(), }; let frame_system = match generate_access_from_frame_or_crate("frame-system") { Ok(path) => path, - Err(err) => return err.to_compile_error().into(), + Err(err) => return err.to_compile_error(), }; let codec = quote!(#krate::__private::codec); let traits = quote!(#krate::__private::traits); @@ -973,7 +973,7 @@ fn expand_benchmark( let verify_stmts = benchmark_def.verify_stmts; let last_stmt = benchmark_def.last_stmt; let test_ident = Ident::new( - format!("test_benchmark_{}", name.to_string()).as_str(), + format!("test_benchmark_{}", name).as_str(), Span::call_site(), ); @@ -1106,7 +1106,7 @@ fn expand_benchmark( sig.generics.where_clause = parse_quote!(where #where_clause); } sig.ident = Ident::new( - format!("_{}", name.to_token_stream().to_string()).as_str(), + format!("_{}", name.to_token_stream()).as_str(), Span::call_site(), ); let mut fn_param_inputs: Vec = diff --git a/support/procedural-fork/src/construct_runtime/expand/freeze_reason.rs b/support/procedural-fork/src/construct_runtime/expand/freeze_reason.rs index 131c919ef..f00269085 100644 --- a/support/procedural-fork/src/construct_runtime/expand/freeze_reason.rs +++ b/support/procedural-fork/src/construct_runtime/expand/freeze_reason.rs @@ -25,7 +25,7 @@ pub fn expand_outer_freeze_reason(pallet_decls: &[Pallet], scrate: &TokenStream) let mut freeze_reason_variants = Vec::new(); let mut freeze_reason_variants_count = Vec::new(); for decl in pallet_decls { - if let Some(_) = decl.find_part("FreezeReason") { + if decl.find_part("FreezeReason").is_some() { let variant_name = &decl.name; let path = &decl.path; let index = decl.index; diff --git a/support/procedural-fork/src/construct_runtime/expand/hold_reason.rs b/support/procedural-fork/src/construct_runtime/expand/hold_reason.rs index 58870a321..5fc2ed1ee 100644 --- a/support/procedural-fork/src/construct_runtime/expand/hold_reason.rs +++ b/support/procedural-fork/src/construct_runtime/expand/hold_reason.rs @@ -25,7 +25,7 @@ pub fn expand_outer_hold_reason(pallet_decls: &[Pallet], scrate: &TokenStream) - let mut hold_reason_variants = Vec::new(); let mut hold_reason_variants_count = Vec::new(); for decl in pallet_decls { - if let Some(_) = decl.find_part("HoldReason") { + if decl.find_part("HoldReason").is_some() { let variant_name = &decl.name; let path = &decl.path; let index = decl.index; diff --git a/support/procedural-fork/src/construct_runtime/expand/lock_id.rs b/support/procedural-fork/src/construct_runtime/expand/lock_id.rs index 67c2fb933..732fb7ac4 100644 --- a/support/procedural-fork/src/construct_runtime/expand/lock_id.rs +++ b/support/procedural-fork/src/construct_runtime/expand/lock_id.rs @@ -24,7 +24,7 @@ pub fn expand_outer_lock_id(pallet_decls: &[Pallet], scrate: &TokenStream) -> To let mut conversion_fns = Vec::new(); let mut lock_id_variants = Vec::new(); for decl in pallet_decls { - if let Some(_) = decl.find_part("LockId") { + if decl.find_part("LockId").is_some() { let variant_name = &decl.name; let path = &decl.path; let index = decl.index; diff --git a/support/procedural-fork/src/construct_runtime/expand/slash_reason.rs b/support/procedural-fork/src/construct_runtime/expand/slash_reason.rs index 0695d8102..d9e9e9320 100644 --- a/support/procedural-fork/src/construct_runtime/expand/slash_reason.rs +++ b/support/procedural-fork/src/construct_runtime/expand/slash_reason.rs @@ -24,7 +24,7 @@ pub fn expand_outer_slash_reason(pallet_decls: &[Pallet], scrate: &TokenStream) let mut conversion_fns = Vec::new(); let mut slash_reason_variants = Vec::new(); for decl in pallet_decls { - if let Some(_) = decl.find_part("SlashReason") { + if decl.find_part("SlashReason").is_some() { let variant_name = &decl.name; let path = &decl.path; let index = decl.index; diff --git a/support/procedural-fork/src/construct_runtime/expand/task.rs b/support/procedural-fork/src/construct_runtime/expand/task.rs index 94a5f52bb..dd8d93c27 100644 --- a/support/procedural-fork/src/construct_runtime/expand/task.rs +++ b/support/procedural-fork/src/construct_runtime/expand/task.rs @@ -69,7 +69,7 @@ pub fn expand_outer_task( let prelude = quote!(#scrate::traits::tasks::__private); - const INCOMPLETE_MATCH_QED: &'static str = + const INCOMPLETE_MATCH_QED: &str = "cannot have an instantiated RuntimeTask without some Task variant in the runtime. QED"; let output = quote! { diff --git a/support/procedural-fork/src/construct_runtime/mod.rs b/support/procedural-fork/src/construct_runtime/mod.rs index de688b3d6..8d0933a51 100644 --- a/support/procedural-fork/src/construct_runtime/mod.rs +++ b/support/procedural-fork/src/construct_runtime/mod.rs @@ -388,18 +388,13 @@ fn construct_runtime_final_expansion( let features = pallets .iter() - .filter_map(|decl| { - (!decl.cfg_pattern.is_empty()).then(|| { - decl.cfg_pattern.iter().flat_map(|attr| { + .filter(|&decl| (!decl.cfg_pattern.is_empty())).flat_map(|decl| decl.cfg_pattern.iter().flat_map(|attr| { attr.predicates().filter_map(|pred| match pred { Predicate::Feature(feat) => Some(feat), Predicate::Test => Some("test"), _ => None, }) - }) - }) - }) - .flatten() + })) .collect::>(); let hidden_crate_name = "construct_runtime"; @@ -439,9 +434,7 @@ fn construct_runtime_final_expansion( let integrity_test = decl_integrity_test(&scrate); let static_assertions = decl_static_assertions(&name, &pallets, &scrate); - let warning = where_section.map_or(None, |where_section| { - Some( - proc_macro_warning::Warning::new_deprecated("WhereSection") + let warning = where_section.map(|where_section| proc_macro_warning::Warning::new_deprecated("WhereSection") .old("use a `where` clause in `construct_runtime`") .new( "use `frame_system::Config` to set the `Block` type and delete this clause. @@ -449,9 +442,7 @@ fn construct_runtime_final_expansion( ) .help_links(&["https://github.com/paritytech/substrate/pull/14437"]) .span(where_section.span) - .build_or_panic(), - ) - }); + .build_or_panic()); let res = quote!( #warning @@ -545,8 +536,7 @@ pub(crate) fn decl_all_pallets<'a>( // Every feature set to the pallet names that should be included by this feature set. let mut features_to_names = features - .iter() - .map(|f| *f) + .iter().copied() .powerset() .map(|feat| (HashSet::from_iter(feat), Vec::new())) .collect::, Vec<_>)>>(); diff --git a/support/procedural-fork/src/construct_runtime/parse.rs b/support/procedural-fork/src/construct_runtime/parse.rs index 173a8dd12..26fbb4dee 100644 --- a/support/procedural-fork/src/construct_runtime/parse.rs +++ b/support/procedural-fork/src/construct_runtime/parse.rs @@ -298,7 +298,7 @@ impl Parse for PalletDeclaration { let pallet_parts = if input.peek(Token![::]) && input.peek3(token::Brace) { let _: Token![::] = input.parse()?; let mut parts = parse_pallet_parts(input)?; - parts.extend(extra_parts.into_iter()); + parts.extend(extra_parts); Some(parts) } else if !input.peek(keyword::exclude_parts) && !input.peek(keyword::use_parts) @@ -740,8 +740,8 @@ fn convert_pallets(pallets: Vec) -> syn::Result { - assert_eq!(path.to_token_stream().to_string(), "dynamic_pallet_params") - } - _ => (), + if let syn::Meta::Path(path) = &attr.meta { + assert_eq!(path.to_token_stream().to_string(), "dynamic_pallet_params") } let runtime_params = &self.runtime_params; @@ -184,8 +181,7 @@ impl VisitMut for MacroInjectArgs { attr.meta = syn::parse2::(quote! { dynamic_pallet_params(#runtime_params, #params_pallet) }) - .unwrap() - .into(); + .unwrap(); } visit_mut::visit_item_mod_mut(self, item); diff --git a/support/procedural-fork/src/lib.rs b/support/procedural-fork/src/lib.rs index 71e8ecba8..570e14727 100644 --- a/support/procedural-fork/src/lib.rs +++ b/support/procedural-fork/src/lib.rs @@ -41,7 +41,7 @@ pub(crate) const NUMBER_OF_INSTANCE: u8 = 16; thread_local! { /// A global counter, can be used to generate a relatively unique identifier. - static COUNTER: RefCell = RefCell::new(Counter(0)); + static COUNTER: RefCell = const { RefCell::new(Counter(0)) }; } /// Counter to generate a relatively unique identifier for macros. This is necessary because diff --git a/support/procedural-fork/src/no_bound/default.rs b/support/procedural-fork/src/no_bound/default.rs index 1c0d90531..6776d84d2 100644 --- a/support/procedural-fork/src/no_bound/default.rs +++ b/support/procedural-fork/src/no_bound/default.rs @@ -136,7 +136,7 @@ pub fn derive_default_no_bound(input: proc_macro::TokenStream) -> proc_macro::To err.extend( additional - .into_iter() + .iter() .map(|variant| syn::Error::new_spanned(variant, "additional default")), ); diff --git a/support/procedural-fork/src/pallet/expand/config.rs b/support/procedural-fork/src/pallet/expand/config.rs index 836c74ae7..55ac72537 100644 --- a/support/procedural-fork/src/pallet/expand/config.rs +++ b/support/procedural-fork/src/pallet/expand/config.rs @@ -51,7 +51,7 @@ Consequently, a runtime that wants to include this pallet must implement this tr // we only emit `DefaultConfig` if there are trait items, so an empty `DefaultConfig` is // impossible consequently. match &config.default_sub_trait { - Some(default_sub_trait) if default_sub_trait.items.len() > 0 => { + Some(default_sub_trait) if !default_sub_trait.items.is_empty() => { let trait_items = &default_sub_trait .items .iter() diff --git a/support/procedural-fork/src/pallet/expand/documentation.rs b/support/procedural-fork/src/pallet/expand/documentation.rs index adc4f7ce9..42891dab6 100644 --- a/support/procedural-fork/src/pallet/expand/documentation.rs +++ b/support/procedural-fork/src/pallet/expand/documentation.rs @@ -20,8 +20,8 @@ use proc_macro2::TokenStream; use quote::ToTokens; use syn::{spanned::Spanned, Attribute, Lit, LitStr}; -const DOC: &'static str = "doc"; -const PALLET_DOC: &'static str = "pallet_doc"; +const DOC: &str = "doc"; +const PALLET_DOC: &str = "pallet_doc"; /// Get the documentation file path from the `pallet_doc` attribute. /// diff --git a/support/procedural-fork/src/pallet/expand/storage.rs b/support/procedural-fork/src/pallet/expand/storage.rs index b77e9846b..32752dc52 100644 --- a/support/procedural-fork/src/pallet/expand/storage.rs +++ b/support/procedural-fork/src/pallet/expand/storage.rs @@ -183,7 +183,7 @@ pub fn process_generics(def: &mut Def) -> syn::Result TokenStream2 { if let Some(tasks_def) = &def.tasks { if def.task_enum.is_none() { def.task_enum = Some(TaskEnumDef::generate( - &tasks_def, + tasks_def, def.type_decl_bounded_generics(tasks_def.item_impl.span()), def.type_use_generics(tasks_def.item_impl.span()), )); diff --git a/support/procedural-fork/src/pallet/expand/warnings.rs b/support/procedural-fork/src/pallet/expand/warnings.rs index 3d71b83af..ece03a13a 100644 --- a/support/procedural-fork/src/pallet/expand/warnings.rs +++ b/support/procedural-fork/src/pallet/expand/warnings.rs @@ -43,7 +43,7 @@ pub(crate) fn weight_witness_warning( .help_link("https://github.com/paritytech/polkadot-sdk/pull/1818"); for (_, arg_ident, _) in method.args.iter() { - if !arg_ident.to_string().starts_with('_') || !contains_ident(w.clone(), &arg_ident) { + if !arg_ident.to_string().starts_with('_') || !contains_ident(w.clone(), arg_ident) { continue; } diff --git a/support/procedural-fork/src/pallet/mod.rs b/support/procedural-fork/src/pallet/mod.rs index d3796662f..5b9bc621b 100644 --- a/support/procedural-fork/src/pallet/mod.rs +++ b/support/procedural-fork/src/pallet/mod.rs @@ -41,7 +41,7 @@ pub fn pallet( ) -> proc_macro::TokenStream { let mut dev_mode = false; if !attr.is_empty() { - if let Ok(_) = syn::parse::(attr.clone()) { + if syn::parse::(attr.clone()).is_ok() { dev_mode = true; } else { let msg = "Invalid pallet macro call: unexpected attribute. Macro call must be \ diff --git a/support/procedural-fork/src/pallet/parse/call.rs b/support/procedural-fork/src/pallet/parse/call.rs index 865c63473..0bcf38a4e 100644 --- a/support/procedural-fork/src/pallet/parse/call.rs +++ b/support/procedural-fork/src/pallet/parse/call.rs @@ -189,7 +189,7 @@ pub fn check_dispatchable_first_arg_type(ty: &syn::Type, is_ref: bool) -> syn::R let result_origin_for = syn::parse2::(ty.to_token_stream()); let result_runtime_origin = syn::parse2::(ty.to_token_stream()); - return match (result_origin_for, result_runtime_origin) { + match (result_origin_for, result_runtime_origin) { (Ok(CheckOriginFor(has_ref)), _) if is_ref == has_ref => Ok(()), (_, Ok(_)) => Ok(()), (_, _) => { @@ -198,9 +198,9 @@ pub fn check_dispatchable_first_arg_type(ty: &syn::Type, is_ref: bool) -> syn::R } else { "Invalid type: expected `OriginFor` or `T::RuntimeOrigin`" }; - return Err(syn::Error::new(ty.span(), msg)); + Err(syn::Error::new(ty.span(), msg)) } - }; + } } impl CallDef { diff --git a/support/procedural-fork/src/pallet/parse/config.rs b/support/procedural-fork/src/pallet/parse/config.rs index cde565245..9ecdbddc3 100644 --- a/support/procedural-fork/src/pallet/parse/config.rs +++ b/support/procedural-fork/src/pallet/parse/config.rs @@ -290,7 +290,7 @@ fn has_expected_system_config(path: syn::Path, frame_system: &syn::Path) -> bool let mut expected_system_config = match ( is_using_frame_crate(&path), - is_using_frame_crate(&frame_system), + is_using_frame_crate(frame_system), ) { (true, false) => // We can't use the path to `frame_system` from `frame` if `frame_system` is not being diff --git a/support/procedural-fork/src/pallet/parse/helper.rs b/support/procedural-fork/src/pallet/parse/helper.rs index f58c8d81c..3d39e0aa0 100644 --- a/support/procedural-fork/src/pallet/parse/helper.rs +++ b/support/procedural-fork/src/pallet/parse/helper.rs @@ -547,10 +547,10 @@ pub fn check_genesis_builder_usage(type_: &syn::Path) -> syn::Result()?; } input.parse::]>()?; - return Ok(Self(Some(instance_usage))); + Ok(Self(Some(instance_usage))) } else { input.parse::()?; - return Ok(Self(None)); + Ok(Self(None)) } } } @@ -652,7 +652,7 @@ pub fn check_pallet_call_return_type(type_: &syn::Type) -> syn::Result<()> { } pub(crate) fn two128_str(s: &str) -> TokenStream { - bytes_to_array(sp_crypto_hashing::twox_128(s.as_bytes()).into_iter()) + bytes_to_array(sp_crypto_hashing::twox_128(s.as_bytes())) } pub(crate) fn bytes_to_array(bytes: impl IntoIterator) -> TokenStream { @@ -661,5 +661,4 @@ pub(crate) fn bytes_to_array(bytes: impl IntoIterator) -> TokenStream quote!( [ #( #bytes ),* ] ) - .into() } diff --git a/support/procedural-fork/src/pallet/parse/mod.rs b/support/procedural-fork/src/pallet/parse/mod.rs index 57c252473..c0f9eca20 100644 --- a/support/procedural-fork/src/pallet/parse/mod.rs +++ b/support/procedural-fork/src/pallet/parse/mod.rs @@ -287,8 +287,7 @@ impl Def { "A `#[pallet::tasks_experimental]` attribute must be attached to your `Task` impl if the \ task enum has been omitted", )); - } else { - } + } } _ => (), } @@ -310,7 +309,7 @@ impl Def { return Ok(()); }; let type_path = type_path.path.segments.iter().collect::>(); - let (Some(seg), None) = (type_path.get(0), type_path.get(1)) else { + let (Some(seg), None) = (type_path.first(), type_path.get(1)) else { return Ok(()); }; let mut result = None; @@ -356,7 +355,7 @@ impl Def { continue; }; let target_path = target_path.path.segments.iter().collect::>(); - let (Some(target_ident), None) = (target_path.get(0), target_path.get(1)) else { + let (Some(target_ident), None) = (target_path.first(), target_path.get(1)) else { continue; }; let matches_task_enum = match task_enum { @@ -420,10 +419,9 @@ impl Def { instances.extend_from_slice(&genesis_config.instances[..]); } if let Some(genesis_build) = &self.genesis_build { - genesis_build + if let Some(i) = genesis_build .instances - .as_ref() - .map(|i| instances.extend_from_slice(&i)); + .as_ref() { instances.extend_from_slice(i) } } if let Some(extra_constants) = &self.extra_constants { instances.extend_from_slice(&extra_constants.instances[..]); diff --git a/support/procedural-fork/src/pallet/parse/storage.rs b/support/procedural-fork/src/pallet/parse/storage.rs index 811832427..dac0782bd 100644 --- a/support/procedural-fork/src/pallet/parse/storage.rs +++ b/support/procedural-fork/src/pallet/parse/storage.rs @@ -955,7 +955,7 @@ impl StorageDef { let msg = format!( "Invalid pallet::storage, unexpected generic args for ResultQuery, \ expected angle-bracketed arguments, found `{}`", - args.to_token_stream().to_string() + args.to_token_stream() ); return Err(syn::Error::new(args.span(), msg)); } @@ -1005,7 +1005,7 @@ impl StorageDef { let msg = format!( "Invalid pallet::storage, unexpected generic argument kind, expected a \ type path to a `PalletError` enum variant, found `{}`", - gen_arg.to_token_stream().to_string(), + gen_arg.to_token_stream(), ); Err(syn::Error::new(gen_arg.span(), msg)) } diff --git a/support/procedural-fork/src/pallet/parse/tasks.rs b/support/procedural-fork/src/pallet/parse/tasks.rs index f1728f824..4d9ad9d0d 100644 --- a/support/procedural-fork/src/pallet/parse/tasks.rs +++ b/support/procedural-fork/src/pallet/parse/tasks.rs @@ -104,7 +104,7 @@ impl syn::parse::Parse for TasksDef { // we require the path on the impl to be a TypePath let enum_path = parse2::(item_impl.self_ty.to_token_stream())?; let segments = enum_path.path.segments.iter().collect::>(); - let (Some(last_seg), None) = (segments.get(0), segments.get(1)) else { + let (Some(last_seg), None) = (segments.first(), segments.get(1)) else { return Err(Error::new( enum_path.span(), "if specified manually, the task enum must be defined locally in this \ @@ -456,7 +456,7 @@ impl TryFrom> for TaskIndexAttr { match value.meta { TaskAttrMeta::TaskIndex(meta) => parse2(quote!(#pound[#pallet #colons #meta])), _ => { - return Err(Error::new( + Err(Error::new( value.span(), format!( "`{:?}` cannot be converted to a `TaskIndexAttr`", @@ -478,7 +478,7 @@ impl TryFrom> for TaskConditionAttr { match value.meta { TaskAttrMeta::TaskCondition(meta) => parse2(quote!(#pound[#pallet #colons #meta])), _ => { - return Err(Error::new( + Err(Error::new( value.span(), format!( "`{:?}` cannot be converted to a `TaskConditionAttr`", @@ -500,7 +500,7 @@ impl TryFrom> for TaskWeightAttr { match value.meta { TaskAttrMeta::TaskWeight(meta) => parse2(quote!(#pound[#pallet #colons #meta])), _ => { - return Err(Error::new( + Err(Error::new( value.span(), format!( "`{:?}` cannot be converted to a `TaskWeightAttr`", @@ -522,7 +522,7 @@ impl TryFrom> for TaskListAttr { match value.meta { TaskAttrMeta::TaskList(meta) => parse2(quote!(#pound[#pallet #colons #meta])), _ => { - return Err(Error::new( + Err(Error::new( value.span(), format!("`{:?}` cannot be converted to a `TaskListAttr`", value.meta), )) @@ -544,7 +544,7 @@ fn extract_pallet_attr(item_enum: &mut ItemEnum) -> Result> .iter() .map(|seg| seg.ident.clone()) .collect::>(); - let (Some(seg1), Some(_), None) = (segs.get(0), segs.get(1), segs.get(2)) else { + let (Some(seg1), Some(_), None) = (segs.first(), segs.get(1), segs.get(2)) else { return true; }; if seg1 != "pallet" { diff --git a/support/procedural-fork/src/pallet_error.rs b/support/procedural-fork/src/pallet_error.rs index bdf8330cd..e78844c63 100644 --- a/support/procedural-fork/src/pallet_error.rs +++ b/support/procedural-fork/src/pallet_error.rs @@ -45,7 +45,7 @@ pub fn derive_pallet_error(input: proc_macro::TokenStream) -> proc_macro::TokenS }) => { let maybe_field_tys = fields .iter() - .map(|f| generate_field_types(f, &frame_support)) + .map(|f| generate_field_types(f, frame_support)) .collect::>>(); let field_tys = match maybe_field_tys { Ok(tys) => tys.into_iter().flatten(), @@ -65,7 +65,7 @@ pub fn derive_pallet_error(input: proc_macro::TokenStream) -> proc_macro::TokenS syn::Data::Enum(syn::DataEnum { variants, .. }) => { let field_tys = variants .iter() - .map(|variant| generate_variant_field_types(variant, &frame_support)) + .map(|variant| generate_variant_field_types(variant, frame_support)) .collect::>>, syn::Error>>(); let field_tys = match field_tys { diff --git a/support/procedural-fork/src/runtime/expand/mod.rs b/support/procedural-fork/src/runtime/expand/mod.rs index c26cbccb7..4ba2b0a74 100644 --- a/support/procedural-fork/src/runtime/expand/mod.rs +++ b/support/procedural-fork/src/runtime/expand/mod.rs @@ -46,7 +46,7 @@ pub fn expand(def: Def, legacy_ordering: bool) -> TokenStream2 { let (check_pallet_number_res, res) = match def.pallets { AllPalletsDeclaration::Implicit(ref decl) => ( check_pallet_number(input.clone(), decl.pallet_count), - construct_runtime_implicit_to_explicit(input.into(), decl.clone(), legacy_ordering), + construct_runtime_implicit_to_explicit(input, decl.clone(), legacy_ordering), ), AllPalletsDeclaration::Explicit(ref decl) => ( check_pallet_number(input, decl.pallets.len()), @@ -76,13 +76,13 @@ pub fn expand(def: Def, legacy_ordering: bool) -> TokenStream2 { res }; - let res = expander::Expander::new("construct_runtime") + + + expander::Expander::new("construct_runtime") .dry(std::env::var("FRAME_EXPAND").is_err()) .verbose(true) .write_to_out_dir(res) - .expect("Does not fail because of IO in OUT_DIR; qed"); - - res.into() + .expect("Does not fail because of IO in OUT_DIR; qed") } fn construct_runtime_implicit_to_explicit( @@ -157,18 +157,13 @@ fn construct_runtime_final_expansion( let features = pallets .iter() - .filter_map(|decl| { - (!decl.cfg_pattern.is_empty()).then(|| { - decl.cfg_pattern.iter().flat_map(|attr| { + .filter(|&decl| (!decl.cfg_pattern.is_empty())).flat_map(|decl| decl.cfg_pattern.iter().flat_map(|attr| { attr.predicates().filter_map(|pred| match pred { Predicate::Feature(feat) => Some(feat), Predicate::Test => Some("test"), _ => None, }) - }) - }) - }) - .flatten() + })) .collect::>(); let hidden_crate_name = "construct_runtime"; diff --git a/support/procedural-fork/src/runtime/mod.rs b/support/procedural-fork/src/runtime/mod.rs index 589acff6c..888a15e11 100644 --- a/support/procedural-fork/src/runtime/mod.rs +++ b/support/procedural-fork/src/runtime/mod.rs @@ -216,7 +216,7 @@ mod keyword { pub fn runtime(attr: TokenStream, tokens: TokenStream) -> TokenStream { let mut legacy_ordering = false; if !attr.is_empty() { - if let Ok(_) = syn::parse::(attr.clone()) { + if syn::parse::(attr.clone()).is_ok() { legacy_ordering = true; } else { let msg = "Invalid runtime macro call: unexpected attribute. Macro call must be \ diff --git a/support/procedural-fork/src/runtime/parse/mod.rs b/support/procedural-fork/src/runtime/parse/mod.rs index 01245187f..c2b33fd76 100644 --- a/support/procedural-fork/src/runtime/parse/mod.rs +++ b/support/procedural-fork/src/runtime/parse/mod.rs @@ -140,7 +140,7 @@ pub struct Def { impl Def { pub fn try_from(mut item: syn::ItemMod) -> syn::Result { - let input: TokenStream2 = item.to_token_stream().into(); + let input: TokenStream2 = item.to_token_stream(); let item_span = item.span(); let items = &mut item .content @@ -207,8 +207,8 @@ impl Def { { let msg = "Two pallets with the same name!"; - let mut err = syn::Error::new(used_pallet, &msg); - err.combine(syn::Error::new(pallet_decl.name.span(), &msg)); + let mut err = syn::Error::new(used_pallet, msg); + err.combine(syn::Error::new(pallet_decl.name.span(), msg)); return Err(err); } diff --git a/support/procedural-fork/src/runtime/parse/pallet.rs b/support/procedural-fork/src/runtime/parse/pallet.rs index 039e2631b..54662bfd7 100644 --- a/support/procedural-fork/src/runtime/parse/pallet.rs +++ b/support/procedural-fork/src/runtime/parse/pallet.rs @@ -77,9 +77,7 @@ impl Pallet { } } - pallet_parts = pallet_parts - .into_iter() - .filter(|part| { + pallet_parts.retain(|part| { if let (true, &PalletPartKeyword::Call(_)) = (disable_call, &part.keyword) { false } else if let (true, &PalletPartKeyword::ValidateUnsigned(_)) = @@ -89,8 +87,7 @@ impl Pallet { } else { true } - }) - .collect(); + }); let cfg_pattern = vec![]; From f8f2b952b642d6bac18b4577bbfa579d00dd704e Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 19 Sep 2024 02:09:31 -0400 Subject: [PATCH 36/53] supporess procedural-fork clippy warnings --- support/procedural-fork/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/support/procedural-fork/src/lib.rs b/support/procedural-fork/src/lib.rs index 570e14727..4680ba09a 100644 --- a/support/procedural-fork/src/lib.rs +++ b/support/procedural-fork/src/lib.rs @@ -10,8 +10,7 @@ //! on an as-needed, ad-hoc basis, and versions will matched the corresponding `polkadot-sdk` //! version/tag name. #![recursion_limit = "512"] -#![deny(rustdoc::broken_intra_doc_links)] -#![allow(unused)] +#![allow(warnings)] extern crate proc_macro; From 4a194d3aa00d76a0d2414bacf92a42a0bd3f35b7 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 19 Sep 2024 02:11:00 -0400 Subject: [PATCH 37/53] allow more in procedural-fork --- support/procedural-fork/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/support/procedural-fork/src/lib.rs b/support/procedural-fork/src/lib.rs index 4680ba09a..ce493859c 100644 --- a/support/procedural-fork/src/lib.rs +++ b/support/procedural-fork/src/lib.rs @@ -11,6 +11,7 @@ //! version/tag name. #![recursion_limit = "512"] #![allow(warnings)] +#![allow(all)] extern crate proc_macro; From 6985939af0ce46ddcac65b6a842a22f2163dd1db Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 19 Sep 2024 02:13:17 -0400 Subject: [PATCH 38/53] tweak --- support/procedural-fork/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support/procedural-fork/src/lib.rs b/support/procedural-fork/src/lib.rs index ce493859c..576f4b421 100644 --- a/support/procedural-fork/src/lib.rs +++ b/support/procedural-fork/src/lib.rs @@ -11,7 +11,7 @@ //! version/tag name. #![recursion_limit = "512"] #![allow(warnings)] -#![allow(all)] +#![allow(clippy)] extern crate proc_macro; From 28b74feb8737765283ef53d03226509ec49f8c8c Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 19 Sep 2024 02:15:42 -0400 Subject: [PATCH 39/53] allow clippy::all in procedural-fork --- support/procedural-fork/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support/procedural-fork/src/lib.rs b/support/procedural-fork/src/lib.rs index 576f4b421..cef3891a0 100644 --- a/support/procedural-fork/src/lib.rs +++ b/support/procedural-fork/src/lib.rs @@ -11,7 +11,7 @@ //! version/tag name. #![recursion_limit = "512"] #![allow(warnings)] -#![allow(clippy)] +#![allow(clippy::all)] extern crate proc_macro; From 2a35d7839c0dec79e6d9b9d8c13a72b8abca943f Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 19 Sep 2024 02:17:58 -0400 Subject: [PATCH 40/53] successfully suppress clippy in procedural-fork --- support/procedural-fork/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/support/procedural-fork/Cargo.toml b/support/procedural-fork/Cargo.toml index 61221ead8..503c81f1f 100644 --- a/support/procedural-fork/Cargo.toml +++ b/support/procedural-fork/Cargo.toml @@ -3,8 +3,8 @@ name = "procedural-fork" version = "1.10.0-rc3" edition = "2021" -[lints] -workspace = true +[lints.clippy] +all = "allow" [dependencies] derive-syn-parse = "0.2" From 3ffcdec175daa5f914910fa35fb82115f3b750ee Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 19 Sep 2024 02:22:39 -0400 Subject: [PATCH 41/53] cargo fmt --all --- .../src/construct_runtime/mod.rs | 38 +-- .../procedural-fork/src/no_bound/default.rs | 242 +++++++++--------- .../procedural-fork/src/pallet/parse/mod.rs | 8 +- .../procedural-fork/src/pallet/parse/tasks.rs | 58 ++--- .../procedural-fork/src/runtime/expand/mod.rs | 19 +- .../src/runtime/parse/pallet.rs | 20 +- 6 files changed, 193 insertions(+), 192 deletions(-) diff --git a/support/procedural-fork/src/construct_runtime/mod.rs b/support/procedural-fork/src/construct_runtime/mod.rs index 8d0933a51..f01ebe0dd 100644 --- a/support/procedural-fork/src/construct_runtime/mod.rs +++ b/support/procedural-fork/src/construct_runtime/mod.rs @@ -388,13 +388,16 @@ fn construct_runtime_final_expansion( let features = pallets .iter() - .filter(|&decl| (!decl.cfg_pattern.is_empty())).flat_map(|decl| decl.cfg_pattern.iter().flat_map(|attr| { - attr.predicates().filter_map(|pred| match pred { - Predicate::Feature(feat) => Some(feat), - Predicate::Test => Some("test"), - _ => None, - }) - })) + .filter(|&decl| (!decl.cfg_pattern.is_empty())) + .flat_map(|decl| { + decl.cfg_pattern.iter().flat_map(|attr| { + attr.predicates().filter_map(|pred| match pred { + Predicate::Feature(feat) => Some(feat), + Predicate::Test => Some("test"), + _ => None, + }) + }) + }) .collect::>(); let hidden_crate_name = "construct_runtime"; @@ -434,15 +437,17 @@ fn construct_runtime_final_expansion( let integrity_test = decl_integrity_test(&scrate); let static_assertions = decl_static_assertions(&name, &pallets, &scrate); - let warning = where_section.map(|where_section| proc_macro_warning::Warning::new_deprecated("WhereSection") - .old("use a `where` clause in `construct_runtime`") - .new( - "use `frame_system::Config` to set the `Block` type and delete this clause. + let warning = where_section.map(|where_section| { + proc_macro_warning::Warning::new_deprecated("WhereSection") + .old("use a `where` clause in `construct_runtime`") + .new( + "use `frame_system::Config` to set the `Block` type and delete this clause. It is planned to be removed in December 2023", - ) - .help_links(&["https://github.com/paritytech/substrate/pull/14437"]) - .span(where_section.span) - .build_or_panic()); + ) + .help_links(&["https://github.com/paritytech/substrate/pull/14437"]) + .span(where_section.span) + .build_or_panic() + }); let res = quote!( #warning @@ -536,7 +541,8 @@ pub(crate) fn decl_all_pallets<'a>( // Every feature set to the pallet names that should be included by this feature set. let mut features_to_names = features - .iter().copied() + .iter() + .copied() .powerset() .map(|feat| (HashSet::from_iter(feat), Vec::new())) .collect::, Vec<_>)>>(); diff --git a/support/procedural-fork/src/no_bound/default.rs b/support/procedural-fork/src/no_bound/default.rs index 6776d84d2..cb054878d 100644 --- a/support/procedural-fork/src/no_bound/default.rs +++ b/support/procedural-fork/src/no_bound/default.rs @@ -27,132 +27,134 @@ pub fn derive_default_no_bound(input: proc_macro::TokenStream) -> proc_macro::To let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - let impl_ = - match input.data { - Data::Struct(struct_) => match struct_.fields { - Fields::Named(named) => { - let fields = named.named.iter().map(|field| &field.ident).map(|ident| { - quote_spanned! {ident.span() => - #ident: ::core::default::Default::default() - } - }); + let impl_ = match input.data { + Data::Struct(struct_) => match struct_.fields { + Fields::Named(named) => { + let fields = named.named.iter().map(|field| &field.ident).map(|ident| { + quote_spanned! {ident.span() => + #ident: ::core::default::Default::default() + } + }); + + quote!(Self { #( #fields, )* }) + } + Fields::Unnamed(unnamed) => { + let fields = unnamed.unnamed.iter().map(|field| { + quote_spanned! {field.span()=> + ::core::default::Default::default() + } + }); + + quote!(Self( #( #fields, )* )) + } + Fields::Unit => { + quote!(Self) + } + }, + Data::Enum(enum_) => { + if enum_.variants.is_empty() { + return syn::Error::new_spanned(name, "cannot derive Default for an empty enum") + .to_compile_error() + .into(); + } - quote!(Self { #( #fields, )* }) - } - Fields::Unnamed(unnamed) => { - let fields = unnamed.unnamed.iter().map(|field| { - quote_spanned! {field.span()=> - ::core::default::Default::default() + // all #[default] attrs with the variant they're on; i.e. a var + let default_variants = enum_ + .variants + .into_iter() + .filter(|variant| { + variant + .attrs + .iter() + .any(|attr| attr.path().is_ident("default")) + }) + .collect::>(); + + match &*default_variants { + [] => return syn::Error::new( + name.clone().span(), + "no default declared, make a variant default by placing `#[default]` above it", + ) + .into_compile_error() + .into(), + // only one variant with the #[default] attribute set + [default_variant] => { + let variant_attrs = default_variant + .attrs + .iter() + .filter(|a| a.path().is_ident("default")) + .collect::>(); + + // check that there is only one #[default] attribute on the variant + if let [first_attr, second_attr, additional_attrs @ ..] = &*variant_attrs { + let mut err = + syn::Error::new(Span::call_site(), "multiple `#[default]` attributes"); + + err.combine(syn::Error::new_spanned( + first_attr, + "`#[default]` used here", + )); + + err.extend([second_attr].into_iter().chain(additional_attrs).map( + |variant| { + syn::Error::new_spanned(variant, "`#[default]` used again here") + }, + )); + + return err.into_compile_error().into(); + } + + let variant_ident = &default_variant.ident; + + let fully_qualified_variant_path = quote!(Self::#variant_ident); + + match &default_variant.fields { + Fields::Named(named) => { + let fields = + named.named.iter().map(|field| &field.ident).map(|ident| { + quote_spanned! {ident.span()=> + #ident: ::core::default::Default::default() + } + }); + + quote!(#fully_qualified_variant_path { #( #fields, )* }) } - }); - - quote!(Self( #( #fields, )* )) - } - Fields::Unit => { - quote!(Self) - } - }, - Data::Enum(enum_) => { - if enum_.variants.is_empty() { - return syn::Error::new_spanned(name, "cannot derive Default for an empty enum") - .to_compile_error() - .into(); + Fields::Unnamed(unnamed) => { + let fields = unnamed.unnamed.iter().map(|field| { + quote_spanned! {field.span()=> + ::core::default::Default::default() + } + }); + + quote!(#fully_qualified_variant_path( #( #fields, )* )) + } + Fields::Unit => fully_qualified_variant_path, + } } + [first, additional @ ..] => { + let mut err = syn::Error::new(Span::call_site(), "multiple declared defaults"); - // all #[default] attrs with the variant they're on; i.e. a var - let default_variants = enum_ - .variants - .into_iter() - .filter(|variant| { - variant - .attrs + err.combine(syn::Error::new_spanned(first, "first default")); + + err.extend( + additional .iter() - .any(|attr| attr.path().is_ident("default")) - }) - .collect::>(); - - match &*default_variants { - [] => return syn::Error::new( - name.clone().span(), - "no default declared, make a variant default by placing `#[default]` above it", - ) - .into_compile_error() - .into(), - // only one variant with the #[default] attribute set - [default_variant] => { - let variant_attrs = default_variant - .attrs - .iter() - .filter(|a| a.path().is_ident("default")) - .collect::>(); - - // check that there is only one #[default] attribute on the variant - if let [first_attr, second_attr, additional_attrs @ ..] = &*variant_attrs { - let mut err = - syn::Error::new(Span::call_site(), "multiple `#[default]` attributes"); - - err.combine(syn::Error::new_spanned(first_attr, "`#[default]` used here")); - - err.extend([second_attr].into_iter().chain(additional_attrs).map( - |variant| { - syn::Error::new_spanned(variant, "`#[default]` used again here") - }, - )); - - return err.into_compile_error().into() - } - - let variant_ident = &default_variant.ident; - - let fully_qualified_variant_path = quote!(Self::#variant_ident); - - match &default_variant.fields { - Fields::Named(named) => { - let fields = - named.named.iter().map(|field| &field.ident).map(|ident| { - quote_spanned! {ident.span()=> - #ident: ::core::default::Default::default() - } - }); - - quote!(#fully_qualified_variant_path { #( #fields, )* }) - }, - Fields::Unnamed(unnamed) => { - let fields = unnamed.unnamed.iter().map(|field| { - quote_spanned! {field.span()=> - ::core::default::Default::default() - } - }); - - quote!(#fully_qualified_variant_path( #( #fields, )* )) - }, - Fields::Unit => fully_qualified_variant_path, - } - }, - [first, additional @ ..] => { - let mut err = syn::Error::new(Span::call_site(), "multiple declared defaults"); - - err.combine(syn::Error::new_spanned(first, "first default")); - - err.extend( - additional - .iter() - .map(|variant| syn::Error::new_spanned(variant, "additional default")), - ); - - return err.into_compile_error().into() - }, - } - } - Data::Union(union_) => { - return syn::Error::new_spanned( - union_.union_token, - "Union type not supported by `derive(DefaultNoBound)`", - ) - .to_compile_error() - .into() + .map(|variant| syn::Error::new_spanned(variant, "additional default")), + ); + + return err.into_compile_error().into(); + } } - }; + } + Data::Union(union_) => { + return syn::Error::new_spanned( + union_.union_token, + "Union type not supported by `derive(DefaultNoBound)`", + ) + .to_compile_error() + .into() + } + }; quote!( const _: () = { diff --git a/support/procedural-fork/src/pallet/parse/mod.rs b/support/procedural-fork/src/pallet/parse/mod.rs index c0f9eca20..53e65fd12 100644 --- a/support/procedural-fork/src/pallet/parse/mod.rs +++ b/support/procedural-fork/src/pallet/parse/mod.rs @@ -287,7 +287,7 @@ impl Def { "A `#[pallet::tasks_experimental]` attribute must be attached to your `Task` impl if the \ task enum has been omitted", )); - } + } } _ => (), } @@ -419,9 +419,9 @@ impl Def { instances.extend_from_slice(&genesis_config.instances[..]); } if let Some(genesis_build) = &self.genesis_build { - if let Some(i) = genesis_build - .instances - .as_ref() { instances.extend_from_slice(i) } + if let Some(i) = genesis_build.instances.as_ref() { + instances.extend_from_slice(i) + } } if let Some(extra_constants) = &self.extra_constants { instances.extend_from_slice(&extra_constants.instances[..]); diff --git a/support/procedural-fork/src/pallet/parse/tasks.rs b/support/procedural-fork/src/pallet/parse/tasks.rs index 4d9ad9d0d..66ee1a7ef 100644 --- a/support/procedural-fork/src/pallet/parse/tasks.rs +++ b/support/procedural-fork/src/pallet/parse/tasks.rs @@ -455,15 +455,13 @@ impl TryFrom> for TaskIndexAttr { let colons = value.colons; match value.meta { TaskAttrMeta::TaskIndex(meta) => parse2(quote!(#pound[#pallet #colons #meta])), - _ => { - Err(Error::new( - value.span(), - format!( - "`{:?}` cannot be converted to a `TaskIndexAttr`", - value.meta - ), - )) - } + _ => Err(Error::new( + value.span(), + format!( + "`{:?}` cannot be converted to a `TaskIndexAttr`", + value.meta + ), + )), } } } @@ -477,15 +475,13 @@ impl TryFrom> for TaskConditionAttr { let colons = value.colons; match value.meta { TaskAttrMeta::TaskCondition(meta) => parse2(quote!(#pound[#pallet #colons #meta])), - _ => { - Err(Error::new( - value.span(), - format!( - "`{:?}` cannot be converted to a `TaskConditionAttr`", - value.meta - ), - )) - } + _ => Err(Error::new( + value.span(), + format!( + "`{:?}` cannot be converted to a `TaskConditionAttr`", + value.meta + ), + )), } } } @@ -499,15 +495,13 @@ impl TryFrom> for TaskWeightAttr { let colons = value.colons; match value.meta { TaskAttrMeta::TaskWeight(meta) => parse2(quote!(#pound[#pallet #colons #meta])), - _ => { - Err(Error::new( - value.span(), - format!( - "`{:?}` cannot be converted to a `TaskWeightAttr`", - value.meta - ), - )) - } + _ => Err(Error::new( + value.span(), + format!( + "`{:?}` cannot be converted to a `TaskWeightAttr`", + value.meta + ), + )), } } } @@ -521,12 +515,10 @@ impl TryFrom> for TaskListAttr { let colons = value.colons; match value.meta { TaskAttrMeta::TaskList(meta) => parse2(quote!(#pound[#pallet #colons #meta])), - _ => { - Err(Error::new( - value.span(), - format!("`{:?}` cannot be converted to a `TaskListAttr`", value.meta), - )) - } + _ => Err(Error::new( + value.span(), + format!("`{:?}` cannot be converted to a `TaskListAttr`", value.meta), + )), } } } diff --git a/support/procedural-fork/src/runtime/expand/mod.rs b/support/procedural-fork/src/runtime/expand/mod.rs index 4ba2b0a74..38d40964b 100644 --- a/support/procedural-fork/src/runtime/expand/mod.rs +++ b/support/procedural-fork/src/runtime/expand/mod.rs @@ -76,8 +76,6 @@ pub fn expand(def: Def, legacy_ordering: bool) -> TokenStream2 { res }; - - expander::Expander::new("construct_runtime") .dry(std::env::var("FRAME_EXPAND").is_err()) .verbose(true) @@ -157,13 +155,16 @@ fn construct_runtime_final_expansion( let features = pallets .iter() - .filter(|&decl| (!decl.cfg_pattern.is_empty())).flat_map(|decl| decl.cfg_pattern.iter().flat_map(|attr| { - attr.predicates().filter_map(|pred| match pred { - Predicate::Feature(feat) => Some(feat), - Predicate::Test => Some("test"), - _ => None, - }) - })) + .filter(|&decl| (!decl.cfg_pattern.is_empty())) + .flat_map(|decl| { + decl.cfg_pattern.iter().flat_map(|attr| { + attr.predicates().filter_map(|pred| match pred { + Predicate::Feature(feat) => Some(feat), + Predicate::Test => Some("test"), + _ => None, + }) + }) + }) .collect::>(); let hidden_crate_name = "construct_runtime"; diff --git a/support/procedural-fork/src/runtime/parse/pallet.rs b/support/procedural-fork/src/runtime/parse/pallet.rs index 54662bfd7..976aba764 100644 --- a/support/procedural-fork/src/runtime/parse/pallet.rs +++ b/support/procedural-fork/src/runtime/parse/pallet.rs @@ -78,16 +78,16 @@ impl Pallet { } pallet_parts.retain(|part| { - if let (true, &PalletPartKeyword::Call(_)) = (disable_call, &part.keyword) { - false - } else if let (true, &PalletPartKeyword::ValidateUnsigned(_)) = - (disable_unsigned, &part.keyword) - { - false - } else { - true - } - }); + if let (true, &PalletPartKeyword::Call(_)) = (disable_call, &part.keyword) { + false + } else if let (true, &PalletPartKeyword::ValidateUnsigned(_)) = + (disable_unsigned, &part.keyword) + { + false + } else { + true + } + }); let cfg_pattern = vec![]; From 47db25a6ac236b43f9ecf85dd51287c0821a97df Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 19 Sep 2024 02:33:37 -0400 Subject: [PATCH 42/53] allow unwrap in pallet index tests --- support/linting/src/pallet_index.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/support/linting/src/pallet_index.rs b/support/linting/src/pallet_index.rs index b74e5a62c..8ed3627d5 100644 --- a/support/linting/src/pallet_index.rs +++ b/support/linting/src/pallet_index.rs @@ -106,6 +106,7 @@ impl ConstructRuntimeVisitor { } #[cfg(test)] +#[allow(clippy::unwrap_used)] mod tests { use super::*; use quote::quote; From 22ba77c818d37e6c4338f4d2caada7f74be6a540 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 19 Sep 2024 02:44:00 -0400 Subject: [PATCH 43/53] fix lint check in CI --- .github/workflows/check-rust.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/check-rust.yml b/.github/workflows/check-rust.yml index b088744cb..95901979d 100644 --- a/.github/workflows/check-rust.yml +++ b/.github/workflows/check-rust.yml @@ -157,9 +157,9 @@ jobs: - name: check lints run: | - set -o pipefail # Ensure the pipeline fails if any command in the pipeline fails - cargo check 2>&1 | sed -r "s/\x1B\[[0-9;]*[mK]//g" | tee /dev/tty | grep -q "^warning:" && \ - (echo "Build emitted the following warnings:" >&2 && exit 1) || echo "No warnings found." + set -o pipefail + cargo check 2>&1 | sed -r "s/\x1B\[[0-9;]*[mK]//g" | tee /dev/tty | grep "warning:" && exit 1 + echo "No warnings found." cargo-clippy-all-features: name: cargo clippy --all-features From 3ea6cf6a570fc8f0db80e4308cbcc86c0d7c231d Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 19 Sep 2024 02:46:54 -0400 Subject: [PATCH 44/53] fix lint check again --- .github/workflows/check-rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-rust.yml b/.github/workflows/check-rust.yml index 95901979d..d1796364b 100644 --- a/.github/workflows/check-rust.yml +++ b/.github/workflows/check-rust.yml @@ -158,7 +158,7 @@ jobs: - name: check lints run: | set -o pipefail - cargo check 2>&1 | sed -r "s/\x1B\[[0-9;]*[mK]//g" | tee /dev/tty | grep "warning:" && exit 1 + cargo check 2>&1 | sed -r "s/\x1B\[[0-9;]*[mK]//g" | grep "warning:" && exit 1 echo "No warnings found." cargo-clippy-all-features: From bbab17a07b7fa19a7b90c30fc90379ce1bf6a934 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 19 Sep 2024 02:50:05 -0400 Subject: [PATCH 45/53] lint check confirmed working in CI, fixing canary! --- pallets/subtensor/tests/mock.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/tests/mock.rs b/pallets/subtensor/tests/mock.rs index 9555833de..6f3b44383 100644 --- a/pallets/subtensor/tests/mock.rs +++ b/pallets/subtensor/tests/mock.rs @@ -25,7 +25,7 @@ frame_support::construct_runtime!( pub enum Test { System: frame_system::{Pallet, Call, Config, Storage, Event} = 1, - Balances: pallet_balances::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Config, Storage, Event} = 2, Triumvirate: pallet_collective::::{Pallet, Call, Storage, Origin, Event, Config} = 3, TriumvirateMembers: pallet_membership::::{Pallet, Call, Storage, Event, Config} = 4, Senate: pallet_collective::::{Pallet, Call, Storage, Origin, Event, Config} = 5, From b4b71756cb6ef5c8e45f5f847fae611de5324978 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 19 Sep 2024 09:56:17 -0400 Subject: [PATCH 46/53] undo clippy changes to procedural-fork since we have silenced --- support/procedural-fork/src/benchmark.rs | 2121 ++++++++--------- .../src/construct_runtime/expand/call.rs | 393 ++- .../expand/composite_helper.rs | 132 +- .../src/construct_runtime/expand/config.rs | 208 +- .../construct_runtime/expand/freeze_reason.rs | 90 +- .../construct_runtime/expand/hold_reason.rs | 90 +- .../src/construct_runtime/expand/inherent.rs | 459 ++-- .../src/construct_runtime/expand/lock_id.rs | 72 +- .../src/construct_runtime/expand/metadata.rs | 399 ++-- .../src/construct_runtime/expand/origin.rs | 846 ++++--- .../construct_runtime/expand/outer_enums.rs | 379 ++- .../construct_runtime/expand/slash_reason.rs | 72 +- .../src/construct_runtime/expand/task.rs | 212 +- .../src/construct_runtime/expand/unsigned.rs | 113 +- .../src/construct_runtime/mod.rs | 1006 ++++---- .../src/construct_runtime/parse.rs | 1257 +++++----- support/procedural-fork/src/crate_version.rs | 36 +- support/procedural-fork/src/derive_impl.rs | 386 ++- .../procedural-fork/src/dummy_part_checker.rs | 98 +- support/procedural-fork/src/dynamic_params.rs | 418 ++-- support/procedural-fork/src/key_prefix.rs | 142 +- .../procedural-fork/src/match_and_insert.rs | 244 +- support/procedural-fork/src/no_bound/clone.rs | 162 +- support/procedural-fork/src/no_bound/debug.rs | 186 +- .../procedural-fork/src/no_bound/default.rs | 283 ++- support/procedural-fork/src/no_bound/ord.rs | 96 +- .../src/no_bound/partial_eq.rs | 214 +- .../src/no_bound/partial_ord.rs | 119 +- .../procedural-fork/src/pallet/expand/call.rs | 874 ++++--- .../src/pallet/expand/composite.rs | 20 +- .../src/pallet/expand/config.rs | 120 +- .../src/pallet/expand/constants.rs | 172 +- .../src/pallet/expand/doc_only.rs | 152 +- .../src/pallet/expand/documentation.rs | 193 +- .../src/pallet/expand/error.rs | 298 ++- .../src/pallet/expand/event.rs | 303 ++- .../src/pallet/expand/genesis_build.rs | 50 +- .../src/pallet/expand/genesis_config.rs | 239 +- .../src/pallet/expand/hooks.rs | 588 +++-- .../src/pallet/expand/inherent.rs | 59 +- .../src/pallet/expand/instances.rs | 32 +- .../procedural-fork/src/pallet/expand/mod.rs | 124 +- .../src/pallet/expand/origin.rs | 59 +- .../src/pallet/expand/pallet_struct.rs | 524 ++-- .../src/pallet/expand/storage.rs | 1415 ++++++----- .../src/pallet/expand/tasks.rs | 308 ++- .../src/pallet/expand/tt_default_parts.rs | 394 ++- .../src/pallet/expand/type_value.rs | 90 +- .../src/pallet/expand/validate_unsigned.rs | 60 +- .../src/pallet/expand/warnings.rs | 111 +- support/procedural-fork/src/pallet/mod.rs | 38 +- .../procedural-fork/src/pallet/parse/call.rs | 791 +++--- .../src/pallet/parse/composite.rs | 330 ++- .../src/pallet/parse/config.rs | 983 ++++---- .../procedural-fork/src/pallet/parse/error.rs | 148 +- .../procedural-fork/src/pallet/parse/event.rs | 206 +- .../src/pallet/parse/extra_constants.rs | 240 +- .../src/pallet/parse/genesis_build.rs | 69 +- .../src/pallet/parse/genesis_config.rs | 81 +- .../src/pallet/parse/helper.rs | 932 ++++---- .../procedural-fork/src/pallet/parse/hooks.rs | 104 +- .../src/pallet/parse/inherent.rs | 68 +- .../procedural-fork/src/pallet/parse/mod.rs | 1158 +++++---- .../src/pallet/parse/origin.rs | 80 +- .../src/pallet/parse/pallet_struct.rs | 220 +- .../src/pallet/parse/storage.rs | 1741 +++++++------- .../procedural-fork/src/pallet/parse/tasks.rs | 1481 ++++++------ .../src/pallet/parse/tests/mod.rs | 146 +- .../src/pallet/parse/tests/tasks.rs | 372 +-- .../src/pallet/parse/type_value.rs | 176 +- .../src/pallet/parse/validate_unsigned.rs | 64 +- support/procedural-fork/src/pallet_error.rs | 307 ++- .../procedural-fork/src/runtime/expand/mod.rs | 570 +++-- support/procedural-fork/src/runtime/mod.rs | 32 +- .../src/runtime/parse/helper.rs | 29 +- .../procedural-fork/src/runtime/parse/mod.rs | 345 ++- .../src/runtime/parse/pallet.rs | 143 +- .../src/runtime/parse/pallet_decl.rs | 77 +- .../src/runtime/parse/runtime_struct.rs | 25 +- .../src/runtime/parse/runtime_types.rs | 90 +- support/procedural-fork/src/storage_alias.rs | 1211 +++++----- support/procedural-fork/src/transactional.rs | 76 +- support/procedural-fork/src/tt_macro.rs | 82 +- 83 files changed, 13983 insertions(+), 14850 deletions(-) diff --git a/support/procedural-fork/src/benchmark.rs b/support/procedural-fork/src/benchmark.rs index a47d175af..0a62c3f92 100644 --- a/support/procedural-fork/src/benchmark.rs +++ b/support/procedural-fork/src/benchmark.rs @@ -23,380 +23,343 @@ use proc_macro::TokenStream; use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; use quote::{quote, ToTokens}; use syn::{ - parse::{Nothing, ParseStream}, - parse_quote, - punctuated::Punctuated, - spanned::Spanned, - token::{Comma, Gt, Lt, PathSep}, - Attribute, Error, Expr, ExprBlock, ExprCall, ExprPath, FnArg, Item, ItemFn, ItemMod, Pat, Path, - PathArguments, PathSegment, Result, ReturnType, Signature, Stmt, Token, Type, TypePath, - Visibility, WhereClause, + parse::{Nothing, ParseStream}, + parse_quote, + punctuated::Punctuated, + spanned::Spanned, + token::{Comma, Gt, Lt, PathSep}, + Attribute, Error, Expr, ExprBlock, ExprCall, ExprPath, FnArg, Item, ItemFn, ItemMod, Pat, Path, + PathArguments, PathSegment, Result, ReturnType, Signature, Stmt, Token, Type, TypePath, + Visibility, WhereClause, }; mod keywords { - use syn::custom_keyword; - - custom_keyword!(benchmark); - custom_keyword!(benchmarks); - custom_keyword!(block); - custom_keyword!(extra); - custom_keyword!(pov_mode); - custom_keyword!(extrinsic_call); - custom_keyword!(skip_meta); - custom_keyword!(BenchmarkError); - custom_keyword!(Result); - custom_keyword!(MaxEncodedLen); - custom_keyword!(Measured); - custom_keyword!(Ignored); - - pub const BENCHMARK_TOKEN: &str = stringify!(benchmark); - pub const BENCHMARKS_TOKEN: &str = stringify!(benchmarks); + use syn::custom_keyword; + + custom_keyword!(benchmark); + custom_keyword!(benchmarks); + custom_keyword!(block); + custom_keyword!(extra); + custom_keyword!(pov_mode); + custom_keyword!(extrinsic_call); + custom_keyword!(skip_meta); + custom_keyword!(BenchmarkError); + custom_keyword!(Result); + custom_keyword!(MaxEncodedLen); + custom_keyword!(Measured); + custom_keyword!(Ignored); + + pub const BENCHMARK_TOKEN: &str = stringify!(benchmark); + pub const BENCHMARKS_TOKEN: &str = stringify!(benchmarks); } /// This represents the raw parsed data for a param definition such as `x: Linear<10, 20>`. #[derive(Clone)] struct ParamDef { - name: String, - _typ: Type, - start: syn::GenericArgument, - end: syn::GenericArgument, + name: String, + _typ: Type, + start: syn::GenericArgument, + end: syn::GenericArgument, } /// Allows easy parsing of the `<10, 20>` component of `x: Linear<10, 20>`. #[derive(Parse)] struct RangeArgs { - _lt_token: Lt, - start: syn::GenericArgument, - _comma: Comma, - end: syn::GenericArgument, - _trailing_comma: Option, - _gt_token: Gt, + _lt_token: Lt, + start: syn::GenericArgument, + _comma: Comma, + end: syn::GenericArgument, + _trailing_comma: Option, + _gt_token: Gt, } #[derive(Clone, Debug)] struct BenchmarkAttrs { - skip_meta: bool, - extra: bool, - pov_mode: Option, + skip_meta: bool, + extra: bool, + pov_mode: Option, } /// Represents a single benchmark option enum BenchmarkAttr { - Extra, - SkipMeta, - /// How the PoV should be measured. - PoV(PovModeAttr), + Extra, + SkipMeta, + /// How the PoV should be measured. + PoV(PovModeAttr), } impl syn::parse::Parse for PovModeAttr { - fn parse(input: ParseStream) -> Result { - let _pov: keywords::pov_mode = input.parse()?; - let _eq: Token![=] = input.parse()?; - let root = PovEstimationMode::parse(input)?; - - let mut maybe_content = None; - let _ = || -> Result<()> { - let content; - syn::braced!(content in input); - maybe_content = Some(content); - Ok(()) - }(); - - let per_key = match maybe_content { - Some(content) => { - let per_key = Punctuated::::parse_terminated(&content)?; - per_key.into_iter().collect() - } - None => Vec::new(), - }; - - Ok(Self { root, per_key }) - } + fn parse(input: ParseStream) -> Result { + let _pov: keywords::pov_mode = input.parse()?; + let _eq: Token![=] = input.parse()?; + let root = PovEstimationMode::parse(input)?; + + let mut maybe_content = None; + let _ = || -> Result<()> { + let content; + syn::braced!(content in input); + maybe_content = Some(content); + Ok(()) + }(); + + let per_key = match maybe_content { + Some(content) => { + let per_key = Punctuated::::parse_terminated(&content)?; + per_key.into_iter().collect() + }, + None => Vec::new(), + }; + + Ok(Self { root, per_key }) + } } impl syn::parse::Parse for BenchmarkAttr { - fn parse(input: ParseStream) -> Result { - let lookahead = input.lookahead1(); - if lookahead.peek(keywords::extra) { - let _extra: keywords::extra = input.parse()?; - Ok(BenchmarkAttr::Extra) - } else if lookahead.peek(keywords::skip_meta) { - let _skip_meta: keywords::skip_meta = input.parse()?; - Ok(BenchmarkAttr::SkipMeta) - } else if lookahead.peek(keywords::pov_mode) { - PovModeAttr::parse(input).map(BenchmarkAttr::PoV) - } else { - Err(lookahead.error()) - } - } + fn parse(input: ParseStream) -> Result { + let lookahead = input.lookahead1(); + if lookahead.peek(keywords::extra) { + let _extra: keywords::extra = input.parse()?; + Ok(BenchmarkAttr::Extra) + } else if lookahead.peek(keywords::skip_meta) { + let _skip_meta: keywords::skip_meta = input.parse()?; + Ok(BenchmarkAttr::SkipMeta) + } else if lookahead.peek(keywords::pov_mode) { + PovModeAttr::parse(input).map(BenchmarkAttr::PoV) + } else { + Err(lookahead.error()) + } + } } /// A `#[pov_mode = .. { .. }]` attribute. #[derive(Debug, Clone)] struct PovModeAttr { - /// The root mode for this benchmarks. - root: PovEstimationMode, - /// The pov-mode for a specific key. This overwrites `root` for this key. - per_key: Vec, + /// The root mode for this benchmarks. + root: PovEstimationMode, + /// The pov-mode for a specific key. This overwrites `root` for this key. + per_key: Vec, } /// A single key-value pair inside the `{}` of a `#[pov_mode = .. { .. }]` attribute. #[derive(Debug, Clone, derive_syn_parse::Parse)] struct PovModeKeyAttr { - /// A specific storage key for which to set the PoV mode. - key: Path, - _underscore: Token![:], - /// The PoV mode for this key. - mode: PovEstimationMode, + /// A specific storage key for which to set the PoV mode. + key: Path, + _underscore: Token![:], + /// The PoV mode for this key. + mode: PovEstimationMode, } /// How the PoV should be estimated. #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum PovEstimationMode { - /// Use the maximal encoded length as provided by [`codec::MaxEncodedLen`]. - MaxEncodedLen, - /// Measure the accessed value size in the pallet benchmarking and add some trie overhead. - Measured, - /// Do not estimate the PoV size for this storage item or benchmark. - Ignored, + /// Use the maximal encoded length as provided by [`codec::MaxEncodedLen`]. + MaxEncodedLen, + /// Measure the accessed value size in the pallet benchmarking and add some trie overhead. + Measured, + /// Do not estimate the PoV size for this storage item or benchmark. + Ignored, } impl syn::parse::Parse for PovEstimationMode { - fn parse(input: ParseStream) -> Result { - let lookahead = input.lookahead1(); - if lookahead.peek(keywords::MaxEncodedLen) { - let _max_encoded_len: keywords::MaxEncodedLen = input.parse()?; - Ok(PovEstimationMode::MaxEncodedLen) - } else if lookahead.peek(keywords::Measured) { - let _measured: keywords::Measured = input.parse()?; - return Ok(PovEstimationMode::Measured); - } else if lookahead.peek(keywords::Ignored) { - let _ignored: keywords::Ignored = input.parse()?; - return Ok(PovEstimationMode::Ignored); - } else { - return Err(lookahead.error()); - } - } + fn parse(input: ParseStream) -> Result { + let lookahead = input.lookahead1(); + if lookahead.peek(keywords::MaxEncodedLen) { + let _max_encoded_len: keywords::MaxEncodedLen = input.parse()?; + return Ok(PovEstimationMode::MaxEncodedLen) + } else if lookahead.peek(keywords::Measured) { + let _measured: keywords::Measured = input.parse()?; + return Ok(PovEstimationMode::Measured) + } else if lookahead.peek(keywords::Ignored) { + let _ignored: keywords::Ignored = input.parse()?; + return Ok(PovEstimationMode::Ignored) + } else { + return Err(lookahead.error()) + } + } } impl ToString for PovEstimationMode { - fn to_string(&self) -> String { - match self { - PovEstimationMode::MaxEncodedLen => "MaxEncodedLen".into(), - PovEstimationMode::Measured => "Measured".into(), - PovEstimationMode::Ignored => "Ignored".into(), - } - } + fn to_string(&self) -> String { + match self { + PovEstimationMode::MaxEncodedLen => "MaxEncodedLen".into(), + PovEstimationMode::Measured => "Measured".into(), + PovEstimationMode::Ignored => "Ignored".into(), + } + } } impl quote::ToTokens for PovEstimationMode { - fn to_tokens(&self, tokens: &mut TokenStream2) { - match self { - PovEstimationMode::MaxEncodedLen => tokens.extend(quote!(MaxEncodedLen)), - PovEstimationMode::Measured => tokens.extend(quote!(Measured)), - PovEstimationMode::Ignored => tokens.extend(quote!(Ignored)), - } - } + fn to_tokens(&self, tokens: &mut TokenStream2) { + match self { + PovEstimationMode::MaxEncodedLen => tokens.extend(quote!(MaxEncodedLen)), + PovEstimationMode::Measured => tokens.extend(quote!(Measured)), + PovEstimationMode::Ignored => tokens.extend(quote!(Ignored)), + } + } } impl syn::parse::Parse for BenchmarkAttrs { - fn parse(input: ParseStream) -> syn::Result { - let mut extra = false; - let mut skip_meta = false; - let mut pov_mode = None; - let args = Punctuated::::parse_terminated(input)?; - - for arg in args.into_iter() { - match arg { - BenchmarkAttr::Extra => { - if extra { - return Err(input.error("`extra` can only be specified once")); - } - extra = true; - } - BenchmarkAttr::SkipMeta => { - if skip_meta { - return Err(input.error("`skip_meta` can only be specified once")); - } - skip_meta = true; - } - BenchmarkAttr::PoV(mode) => { - if pov_mode.is_some() { - return Err(input.error("`pov_mode` can only be specified once")); - } - pov_mode = Some(mode); - } - } - } - Ok(BenchmarkAttrs { - extra, - skip_meta, - pov_mode, - }) - } + fn parse(input: ParseStream) -> syn::Result { + let mut extra = false; + let mut skip_meta = false; + let mut pov_mode = None; + let args = Punctuated::::parse_terminated(&input)?; + + for arg in args.into_iter() { + match arg { + BenchmarkAttr::Extra => { + if extra { + return Err(input.error("`extra` can only be specified once")) + } + extra = true; + }, + BenchmarkAttr::SkipMeta => { + if skip_meta { + return Err(input.error("`skip_meta` can only be specified once")) + } + skip_meta = true; + }, + BenchmarkAttr::PoV(mode) => { + if pov_mode.is_some() { + return Err(input.error("`pov_mode` can only be specified once")) + } + pov_mode = Some(mode); + }, + } + } + Ok(BenchmarkAttrs { extra, skip_meta, pov_mode }) + } } /// Represents the parsed extrinsic call for a benchmark #[derive(Clone)] enum BenchmarkCallDef { - ExtrinsicCall { - origin: Expr, - expr_call: ExprCall, - attr_span: Span, - }, // #[extrinsic_call] - Block { - block: ExprBlock, - attr_span: Span, - }, // #[block] + ExtrinsicCall { origin: Expr, expr_call: ExprCall, attr_span: Span }, // #[extrinsic_call] + Block { block: ExprBlock, attr_span: Span }, // #[block] } impl BenchmarkCallDef { - /// Returns the `span()` for attribute - fn attr_span(&self) -> Span { - match self { - BenchmarkCallDef::ExtrinsicCall { - origin: _, - expr_call: _, - attr_span, - } => *attr_span, - BenchmarkCallDef::Block { - block: _, - attr_span, - } => *attr_span, - } - } + /// Returns the `span()` for attribute + fn attr_span(&self) -> Span { + match self { + BenchmarkCallDef::ExtrinsicCall { origin: _, expr_call: _, attr_span } => *attr_span, + BenchmarkCallDef::Block { block: _, attr_span } => *attr_span, + } + } } /// Represents a parsed `#[benchmark]` or `#[instance_benchmark]` item. #[derive(Clone)] struct BenchmarkDef { - params: Vec, - setup_stmts: Vec, - call_def: BenchmarkCallDef, - verify_stmts: Vec, - last_stmt: Option, - fn_sig: Signature, - fn_vis: Visibility, - fn_attrs: Vec, + params: Vec, + setup_stmts: Vec, + call_def: BenchmarkCallDef, + verify_stmts: Vec, + last_stmt: Option, + fn_sig: Signature, + fn_vis: Visibility, + fn_attrs: Vec, } /// used to parse something compatible with `Result` #[derive(Parse)] struct ResultDef { - _result_kw: keywords::Result, - _lt: Token![<], - unit: Type, - _comma: Comma, - e_type: TypePath, - _gt: Token![>], + _result_kw: keywords::Result, + _lt: Token![<], + unit: Type, + _comma: Comma, + e_type: TypePath, + _gt: Token![>], } /// Ensures that `ReturnType` is a `Result<(), BenchmarkError>`, if specified fn ensure_valid_return_type(item_fn: &ItemFn) -> Result<()> { - if let ReturnType::Type(_, typ) = &item_fn.sig.output { - let non_unit = |span| Err(Error::new(span, "expected `()`")); - let Type::Path(TypePath { path, qself: _ }) = &**typ else { - return Err(Error::new( + if let ReturnType::Type(_, typ) = &item_fn.sig.output { + let non_unit = |span| return Err(Error::new(span, "expected `()`")); + let Type::Path(TypePath { path, qself: _ }) = &**typ else { + return Err(Error::new( typ.span(), "Only `Result<(), BenchmarkError>` or a blank return type is allowed on benchmark function definitions", - )); - }; - let seg = path - .segments - .last() - .expect("to be parsed as a TypePath, it must have at least one segment; qed"); - let res: ResultDef = syn::parse2(seg.to_token_stream())?; - // ensure T in Result is () - let Type::Tuple(tup) = res.unit else { - return non_unit(res.unit.span()); - }; - if !tup.elems.is_empty() { - return non_unit(tup.span()); - } - let TypePath { path, qself: _ } = res.e_type; - let seg = path - .segments - .last() - .expect("to be parsed as a TypePath, it must have at least one segment; qed"); - syn::parse2::(seg.to_token_stream())?; - } - Ok(()) + )) + }; + let seg = path + .segments + .last() + .expect("to be parsed as a TypePath, it must have at least one segment; qed"); + let res: ResultDef = syn::parse2(seg.to_token_stream())?; + // ensure T in Result is () + let Type::Tuple(tup) = res.unit else { return non_unit(res.unit.span()) }; + if !tup.elems.is_empty() { + return non_unit(tup.span()) + } + let TypePath { path, qself: _ } = res.e_type; + let seg = path + .segments + .last() + .expect("to be parsed as a TypePath, it must have at least one segment; qed"); + syn::parse2::(seg.to_token_stream())?; + } + Ok(()) } /// Parses params such as `x: Linear<0, 1>` fn parse_params(item_fn: &ItemFn) -> Result> { - let mut params: Vec = Vec::new(); - for arg in &item_fn.sig.inputs { - let invalid_param = |span| { - Err(Error::new( - span, - "Invalid benchmark function param. A valid example would be `x: Linear<5, 10>`.", - )) - }; - - let FnArg::Typed(arg) = arg else { - return invalid_param(arg.span()); - }; - let Pat::Ident(ident) = &*arg.pat else { - return invalid_param(arg.span()); - }; - - // check param name - let var_span = ident.span(); - let invalid_param_name = || { - Err(Error::new( + let mut params: Vec = Vec::new(); + for arg in &item_fn.sig.inputs { + let invalid_param = |span| { + return Err(Error::new( + span, + "Invalid benchmark function param. A valid example would be `x: Linear<5, 10>`.", + )) + }; + + let FnArg::Typed(arg) = arg else { return invalid_param(arg.span()) }; + let Pat::Ident(ident) = &*arg.pat else { return invalid_param(arg.span()) }; + + // check param name + let var_span = ident.span(); + let invalid_param_name = || { + return Err(Error::new( var_span, "Benchmark parameter names must consist of a single lowercase letter (a-z) and no other characters.", - )) - }; - let name = ident.ident.to_token_stream().to_string(); - if name.len() > 1 { - return invalid_param_name(); - }; - let Some(name_char) = name.chars().next() else { - return invalid_param_name(); - }; - if !name_char.is_alphabetic() || !name_char.is_lowercase() { - return invalid_param_name(); - } - - // parse type - let typ = &*arg.ty; - let Type::Path(tpath) = typ else { - return invalid_param(typ.span()); - }; - let Some(segment) = tpath.path.segments.last() else { - return invalid_param(typ.span()); - }; - let args = segment.arguments.to_token_stream().into(); - let Ok(args) = syn::parse::(args) else { - return invalid_param(typ.span()); - }; - - params.push(ParamDef { - name, - _typ: typ.clone(), - start: args.start, - end: args.end, - }); - } - Ok(params) + )); + }; + let name = ident.ident.to_token_stream().to_string(); + if name.len() > 1 { + return invalid_param_name() + }; + let Some(name_char) = name.chars().next() else { return invalid_param_name() }; + if !name_char.is_alphabetic() || !name_char.is_lowercase() { + return invalid_param_name() + } + + // parse type + let typ = &*arg.ty; + let Type::Path(tpath) = typ else { return invalid_param(typ.span()) }; + let Some(segment) = tpath.path.segments.last() else { return invalid_param(typ.span()) }; + let args = segment.arguments.to_token_stream().into(); + let Ok(args) = syn::parse::(args) else { return invalid_param(typ.span()) }; + + params.push(ParamDef { name, _typ: typ.clone(), start: args.start, end: args.end }); + } + Ok(params) } /// Used in several places where the `#[extrinsic_call]` or `#[body]` annotation is missing fn missing_call(item_fn: &ItemFn) -> Result { - Err(Error::new( + return Err(Error::new( item_fn.block.brace_token.span.join(), "No valid #[extrinsic_call] or #[block] annotation could be found in benchmark function body." - )) + )); } /// Finds the `BenchmarkCallDef` and its index (within the list of stmts for the fn) and /// returns them. Also handles parsing errors for invalid / extra call defs. AKA this is /// general handling for `#[extrinsic_call]` and `#[block]` fn parse_call_def(item_fn: &ItemFn) -> Result<(usize, BenchmarkCallDef)> { - // #[extrinsic_call] / #[block] handling - let call_defs = item_fn.block.stmts.iter().enumerate().filter_map(|(i, child)| { + // #[extrinsic_call] / #[block] handling + let call_defs = item_fn.block.stmts.iter().enumerate().filter_map(|(i, child)| { if let Stmt::Expr(Expr::Call(expr_call), _semi) = child { // #[extrinsic_call] case expr_call.attrs.iter().enumerate().find_map(|(k, attr)| { @@ -430,850 +393,810 @@ fn parse_call_def(item_fn: &ItemFn) -> Result<(usize, BenchmarkCallDef)> { None } }).collect::>>()?; - Ok(match &call_defs[..] { - [(i, call_def)] => (*i, call_def.clone()), // = 1 - [] => return missing_call(item_fn), - _ => { - return Err(Error::new( - call_defs[1].1.attr_span(), - "Only one #[extrinsic_call] or #[block] attribute is allowed per benchmark.", - )) - } - }) + Ok(match &call_defs[..] { + [(i, call_def)] => (*i, call_def.clone()), // = 1 + [] => return missing_call(item_fn), + _ => + return Err(Error::new( + call_defs[1].1.attr_span(), + "Only one #[extrinsic_call] or #[block] attribute is allowed per benchmark.", + )), + }) } impl BenchmarkDef { - /// Constructs a [`BenchmarkDef`] by traversing an existing [`ItemFn`] node. - pub fn from(item_fn: &ItemFn) -> Result { - let params = parse_params(item_fn)?; - ensure_valid_return_type(item_fn)?; - let (i, call_def) = parse_call_def(item_fn)?; - - let (verify_stmts, last_stmt) = match item_fn.sig.output { - ReturnType::Default => - // no return type, last_stmt should be None - { - ( - Vec::from(&item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len()]), - None, - ) - } - ReturnType::Type(_, _) => { - // defined return type, last_stmt should be Result<(), BenchmarkError> - // compatible and should not be included in verify_stmts - if i + 1 >= item_fn.block.stmts.len() { - return Err(Error::new( - item_fn.block.span(), - "Benchmark `#[block]` or `#[extrinsic_call]` item cannot be the \ + /// Constructs a [`BenchmarkDef`] by traversing an existing [`ItemFn`] node. + pub fn from(item_fn: &ItemFn) -> Result { + let params = parse_params(item_fn)?; + ensure_valid_return_type(item_fn)?; + let (i, call_def) = parse_call_def(&item_fn)?; + + let (verify_stmts, last_stmt) = match item_fn.sig.output { + ReturnType::Default => + // no return type, last_stmt should be None + (Vec::from(&item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len()]), None), + ReturnType::Type(_, _) => { + // defined return type, last_stmt should be Result<(), BenchmarkError> + // compatible and should not be included in verify_stmts + if i + 1 >= item_fn.block.stmts.len() { + return Err(Error::new( + item_fn.block.span(), + "Benchmark `#[block]` or `#[extrinsic_call]` item cannot be the \ last statement of your benchmark function definition if you have \ defined a return type. You should return something compatible \ with Result<(), BenchmarkError> (i.e. `Ok(())`) as the last statement \ or change your signature to a blank return type.", - )); - } - let Some(stmt) = item_fn.block.stmts.last() else { - return missing_call(item_fn); - }; - ( - Vec::from(&item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len() - 1]), - Some(stmt.clone()), - ) - } - }; - - Ok(BenchmarkDef { - params, - setup_stmts: Vec::from(&item_fn.block.stmts[0..i]), - call_def, - verify_stmts, - last_stmt, - fn_sig: item_fn.sig.clone(), - fn_vis: item_fn.vis.clone(), - fn_attrs: item_fn.attrs.clone(), - }) - } + )) + } + let Some(stmt) = item_fn.block.stmts.last() else { return missing_call(item_fn) }; + ( + Vec::from(&item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len() - 1]), + Some(stmt.clone()), + ) + }, + }; + + Ok(BenchmarkDef { + params, + setup_stmts: Vec::from(&item_fn.block.stmts[0..i]), + call_def, + verify_stmts, + last_stmt, + fn_sig: item_fn.sig.clone(), + fn_vis: item_fn.vis.clone(), + fn_attrs: item_fn.attrs.clone(), + }) + } } /// Parses and expands a `#[benchmarks]` or `#[instance_benchmarks]` invocation pub fn benchmarks( - attrs: TokenStream, - tokens: TokenStream, - instance: bool, + attrs: TokenStream, + tokens: TokenStream, + instance: bool, ) -> syn::Result { - let krate = generate_access_from_frame_or_crate("frame-benchmarking")?; - // gather module info - let module: ItemMod = syn::parse(tokens)?; - let mod_span = module.span(); - let where_clause = match syn::parse::(attrs.clone()) { - Ok(_) => quote!(), - Err(_) => syn::parse::(attrs)? - .predicates - .to_token_stream(), - }; - let mod_vis = module.vis; - let mod_name = module.ident; - - // consume #[benchmarks] attribute by excluding it from mod_attrs - let mod_attrs: Vec<&Attribute> = module - .attrs - .iter() - .filter(|attr| !attr.path().is_ident(keywords::BENCHMARKS_TOKEN)) - .collect(); - - let mut benchmark_names: Vec = Vec::new(); - let mut extra_benchmark_names: Vec = Vec::new(); - let mut skip_meta_benchmark_names: Vec = Vec::new(); - // Map benchmarks to PoV modes. - let mut pov_modes = Vec::new(); - - let (_brace, mut content) = module - .content - .ok_or(syn::Error::new(mod_span, "Module cannot be empty!"))?; - - // find all function defs marked with #[benchmark] - let benchmark_fn_metas = content.iter_mut().filter_map(|stmt| { - // parse as a function def first - let Item::Fn(func) = stmt else { return None }; - - // find #[benchmark] attribute on function def - let benchmark_attr = func - .attrs - .iter() - .find(|attr| attr.path().is_ident(keywords::BENCHMARK_TOKEN))?; - - Some((benchmark_attr.clone(), func.clone(), stmt)) - }); - - // parse individual benchmark defs and args - for (benchmark_attr, func, stmt) in benchmark_fn_metas { - // parse benchmark def - let benchmark_def = BenchmarkDef::from(&func)?; - - // record benchmark name - let name = &func.sig.ident; - benchmark_names.push(name.clone()); - - // Check if we need to parse any args - if benchmark_attr.meta.require_path_only().is_err() { - // parse any args provided to #[benchmark] - let benchmark_attrs: BenchmarkAttrs = benchmark_attr.parse_args()?; - - // record name sets - if benchmark_attrs.extra { - extra_benchmark_names.push(name.clone()); - } else if benchmark_attrs.skip_meta { - skip_meta_benchmark_names.push(name.clone()); - } - - if let Some(mode) = benchmark_attrs.pov_mode { - let mut modes = Vec::new(); - // We cannot expand strings here since it is no-std, but syn does not expand bytes. - let name = name.to_string(); - let m = mode.root.to_string(); - modes.push(quote!(("ALL".as_bytes().to_vec(), #m.as_bytes().to_vec()))); - - for attr in mode.per_key.iter() { - // syn always puts spaces in quoted paths: - let key = attr - .key - .clone() - .into_token_stream() - .to_string() - .replace(" ", ""); - let mode = attr.mode.to_string(); - modes.push(quote!((#key.as_bytes().to_vec(), #mode.as_bytes().to_vec()))); - } - - pov_modes.push( - quote!((#name.as_bytes().to_vec(), #krate::__private::vec![#(#modes),*])), - ); - } - } - - // expand benchmark - let expanded = expand_benchmark(benchmark_def, name, instance, where_clause.clone()); - - // replace original function def with expanded code - *stmt = Item::Verbatim(expanded); - } - - // generics - let type_use_generics = match instance { - false => quote!(T), - true => quote!(T, I), - }; - let type_impl_generics = match instance { - false => quote!(T: Config), - true => quote!(T: Config, I: 'static), - }; - - let frame_system = generate_access_from_frame_or_crate("frame-system")?; - - // benchmark name variables - let benchmark_names_str: Vec = benchmark_names.iter().map(|n| n.to_string()).collect(); - let extra_benchmark_names_str: Vec = extra_benchmark_names - .iter() - .map(|n| n.to_string()) - .collect(); - let skip_meta_benchmark_names_str: Vec = skip_meta_benchmark_names - .iter() - .map(|n| n.to_string()) - .collect(); - let mut selected_benchmark_mappings: Vec = Vec::new(); - let mut benchmarks_by_name_mappings: Vec = Vec::new(); - let test_idents: Vec = benchmark_names_str - .iter() - .map(|n| Ident::new(format!("test_benchmark_{}", n).as_str(), Span::call_site())) - .collect(); - for i in 0..benchmark_names.len() { - let name_ident = &benchmark_names[i]; - let name_str = &benchmark_names_str[i]; - let test_ident = &test_idents[i]; - selected_benchmark_mappings.push(quote!(#name_str => SelectedBenchmark::#name_ident)); - benchmarks_by_name_mappings.push(quote!(#name_str => Self::#test_ident())) - } - - let impl_test_function = content - .iter_mut() - .find_map(|item| { - let Item::Macro(item_macro) = item else { - return None; - }; - - if !item_macro - .mac - .path - .segments - .iter() - .any(|s| s.ident == "impl_benchmark_test_suite") - { - return None; - } - - let tokens = item_macro.mac.tokens.clone(); - *item = Item::Verbatim(quote! {}); - - Some(quote! { - impl_test_function!( - (#( {} #benchmark_names )*) - (#( #extra_benchmark_names )*) - (#( #skip_meta_benchmark_names )*) - #tokens - ); - }) - }) - .unwrap_or(quote! {}); - - // emit final quoted tokens - let res = quote! { - #(#mod_attrs) - * - #mod_vis mod #mod_name { - #(#content) - * - - #[allow(non_camel_case_types)] - enum SelectedBenchmark { - #(#benchmark_names), - * - } - - impl<#type_impl_generics> #krate::BenchmarkingSetup<#type_use_generics> for SelectedBenchmark where #where_clause { - fn components(&self) -> #krate::__private::Vec<(#krate::BenchmarkParameter, u32, u32)> { - match self { - #( - Self::#benchmark_names => { - <#benchmark_names as #krate::BenchmarkingSetup<#type_use_generics>>::components(&#benchmark_names) - } - ) - * - } - } - - fn instance( - &self, - components: &[(#krate::BenchmarkParameter, u32)], - verify: bool, - ) -> Result< - #krate::__private::Box Result<(), #krate::BenchmarkError>>, - #krate::BenchmarkError, - > { - match self { - #( - Self::#benchmark_names => { - <#benchmark_names as #krate::BenchmarkingSetup< - #type_use_generics - >>::instance(&#benchmark_names, components, verify) - } - ) - * - } - } - } - #[cfg(any(feature = "runtime-benchmarks", test))] - impl<#type_impl_generics> #krate::Benchmarking for Pallet<#type_use_generics> - where T: #frame_system::Config, #where_clause - { - fn benchmarks( - extra: bool, - ) -> #krate::__private::Vec<#krate::BenchmarkMetadata> { - let mut all_names = #krate::__private::vec![ - #(#benchmark_names_str), - * - ]; - if !extra { - let extra = [ - #(#extra_benchmark_names_str), - * - ]; - all_names.retain(|x| !extra.contains(x)); - } - let pov_modes: - #krate::__private::Vec<( - #krate::__private::Vec, - #krate::__private::Vec<( - #krate::__private::Vec, - #krate::__private::Vec - )>, - )> = #krate::__private::vec![ - #( #pov_modes ),* - ]; - all_names.into_iter().map(|benchmark| { - let selected_benchmark = match benchmark { - #(#selected_benchmark_mappings), - *, - _ => panic!("all benchmarks should be selectable") - }; - let components = >::components(&selected_benchmark); - let name = benchmark.as_bytes().to_vec(); - let modes = pov_modes.iter().find(|p| p.0 == name).map(|p| p.1.clone()); - - #krate::BenchmarkMetadata { - name: benchmark.as_bytes().to_vec(), - components, - pov_modes: modes.unwrap_or_default(), - } - }).collect::<#krate::__private::Vec<_>>() - } - - fn run_benchmark( - extrinsic: &[u8], - c: &[(#krate::BenchmarkParameter, u32)], - whitelist: &[#krate::__private::TrackedStorageKey], - verify: bool, - internal_repeats: u32, - ) -> Result<#krate::__private::Vec<#krate::BenchmarkResult>, #krate::BenchmarkError> { - let extrinsic = #krate::__private::str::from_utf8(extrinsic).map_err(|_| "`extrinsic` is not a valid utf-8 string!")?; - let selected_benchmark = match extrinsic { - #(#selected_benchmark_mappings), - *, - _ => return Err("Could not find extrinsic.".into()), - }; - let mut whitelist = whitelist.to_vec(); - let whitelisted_caller_key = <#frame_system::Account< - T, - > as #krate::__private::storage::StorageMap<_, _,>>::hashed_key_for( - #krate::whitelisted_caller::() - ); - whitelist.push(whitelisted_caller_key.into()); - let transactional_layer_key = #krate::__private::TrackedStorageKey::new( - #krate::__private::storage::transactional::TRANSACTION_LEVEL_KEY.into(), - ); - whitelist.push(transactional_layer_key); - // Whitelist the `:extrinsic_index`. - let extrinsic_index = #krate::__private::TrackedStorageKey::new( - #krate::__private::well_known_keys::EXTRINSIC_INDEX.into() - ); - whitelist.push(extrinsic_index); - // Whitelist the `:intrablock_entropy`. - let intrablock_entropy = #krate::__private::TrackedStorageKey::new( - #krate::__private::well_known_keys::INTRABLOCK_ENTROPY.into() - ); - whitelist.push(intrablock_entropy); - - #krate::benchmarking::set_whitelist(whitelist.clone()); - let mut results: #krate::__private::Vec<#krate::BenchmarkResult> = #krate::__private::Vec::new(); - - // Always do at least one internal repeat... - for _ in 0 .. internal_repeats.max(1) { - // Always reset the state after the benchmark. - #krate::__private::defer!(#krate::benchmarking::wipe_db()); - - // Set up the externalities environment for the setup we want to - // benchmark. - let closure_to_benchmark = < - SelectedBenchmark as #krate::BenchmarkingSetup<#type_use_generics> - >::instance(&selected_benchmark, c, verify)?; - - // Set the block number to at least 1 so events are deposited. - if #krate::__private::Zero::is_zero(&#frame_system::Pallet::::block_number()) { - #frame_system::Pallet::::set_block_number(1u32.into()); - } - - // Commit the externalities to the database, flushing the DB cache. - // This will enable worst case scenario for reading from the database. - #krate::benchmarking::commit_db(); - - // Access all whitelisted keys to get them into the proof recorder since the - // recorder does now have a whitelist. - for key in &whitelist { - #krate::__private::storage::unhashed::get_raw(&key.key); - } - - // Reset the read/write counter so we don't count operations in the setup process. - #krate::benchmarking::reset_read_write_count(); - - // Time the extrinsic logic. - #krate::__private::log::trace!( - target: "benchmark", - "Start Benchmark: {} ({:?})", - extrinsic, - c - ); - - let start_pov = #krate::benchmarking::proof_size(); - let start_extrinsic = #krate::benchmarking::current_time(); - - closure_to_benchmark()?; - - let finish_extrinsic = #krate::benchmarking::current_time(); - let end_pov = #krate::benchmarking::proof_size(); - - // Calculate the diff caused by the benchmark. - let elapsed_extrinsic = finish_extrinsic.saturating_sub(start_extrinsic); - let diff_pov = match (start_pov, end_pov) { - (Some(start), Some(end)) => end.saturating_sub(start), - _ => Default::default(), - }; - - // Commit the changes to get proper write count - #krate::benchmarking::commit_db(); - #krate::__private::log::trace!( - target: "benchmark", - "End Benchmark: {} ns", elapsed_extrinsic - ); - let read_write_count = #krate::benchmarking::read_write_count(); - #krate::__private::log::trace!( - target: "benchmark", - "Read/Write Count {:?}", read_write_count - ); - - // Time the storage root recalculation. - let start_storage_root = #krate::benchmarking::current_time(); - #krate::__private::storage_root(#krate::__private::StateVersion::V1); - let finish_storage_root = #krate::benchmarking::current_time(); - let elapsed_storage_root = finish_storage_root - start_storage_root; - - let skip_meta = [ #(#skip_meta_benchmark_names_str),* ]; - let read_and_written_keys = if skip_meta.contains(&extrinsic) { - #krate::__private::vec![(b"Skipped Metadata".to_vec(), 0, 0, false)] - } else { - #krate::benchmarking::get_read_and_written_keys() - }; - - results.push(#krate::BenchmarkResult { - components: c.to_vec(), - extrinsic_time: elapsed_extrinsic, - storage_root_time: elapsed_storage_root, - reads: read_write_count.0, - repeat_reads: read_write_count.1, - writes: read_write_count.2, - repeat_writes: read_write_count.3, - proof_size: diff_pov, - keys: read_and_written_keys, - }); - } - - return Ok(results); - } - } - - #[cfg(test)] - impl<#type_impl_generics> Pallet<#type_use_generics> where T: #frame_system::Config, #where_clause { - /// Test a particular benchmark by name. - /// - /// This isn't called `test_benchmark_by_name` just in case some end-user eventually - /// writes a benchmark, itself called `by_name`; the function would be shadowed in - /// that case. - /// - /// This is generally intended to be used by child test modules such as those created - /// by the `impl_benchmark_test_suite` macro. However, it is not an error if a pallet - /// author chooses not to implement benchmarks. - #[allow(unused)] - fn test_bench_by_name(name: &[u8]) -> Result<(), #krate::BenchmarkError> { - let name = #krate::__private::str::from_utf8(name) - .map_err(|_| -> #krate::BenchmarkError { "`name` is not a valid utf8 string!".into() })?; - match name { - #(#benchmarks_by_name_mappings), - *, - _ => Err("Could not find test for requested benchmark.".into()), - } - } - } - - #impl_test_function - } - #mod_vis use #mod_name::*; - }; - Ok(res.into()) + let krate = generate_access_from_frame_or_crate("frame-benchmarking")?; + // gather module info + let module: ItemMod = syn::parse(tokens)?; + let mod_span = module.span(); + let where_clause = match syn::parse::(attrs.clone()) { + Ok(_) => quote!(), + Err(_) => syn::parse::(attrs)?.predicates.to_token_stream(), + }; + let mod_vis = module.vis; + let mod_name = module.ident; + + // consume #[benchmarks] attribute by excluding it from mod_attrs + let mod_attrs: Vec<&Attribute> = module + .attrs + .iter() + .filter(|attr| !attr.path().is_ident(keywords::BENCHMARKS_TOKEN)) + .collect(); + + let mut benchmark_names: Vec = Vec::new(); + let mut extra_benchmark_names: Vec = Vec::new(); + let mut skip_meta_benchmark_names: Vec = Vec::new(); + // Map benchmarks to PoV modes. + let mut pov_modes = Vec::new(); + + let (_brace, mut content) = + module.content.ok_or(syn::Error::new(mod_span, "Module cannot be empty!"))?; + + // find all function defs marked with #[benchmark] + let benchmark_fn_metas = content.iter_mut().filter_map(|stmt| { + // parse as a function def first + let Item::Fn(func) = stmt else { return None }; + + // find #[benchmark] attribute on function def + let benchmark_attr = + func.attrs.iter().find(|attr| attr.path().is_ident(keywords::BENCHMARK_TOKEN))?; + + Some((benchmark_attr.clone(), func.clone(), stmt)) + }); + + // parse individual benchmark defs and args + for (benchmark_attr, func, stmt) in benchmark_fn_metas { + // parse benchmark def + let benchmark_def = BenchmarkDef::from(&func)?; + + // record benchmark name + let name = &func.sig.ident; + benchmark_names.push(name.clone()); + + // Check if we need to parse any args + if benchmark_attr.meta.require_path_only().is_err() { + // parse any args provided to #[benchmark] + let benchmark_attrs: BenchmarkAttrs = benchmark_attr.parse_args()?; + + // record name sets + if benchmark_attrs.extra { + extra_benchmark_names.push(name.clone()); + } else if benchmark_attrs.skip_meta { + skip_meta_benchmark_names.push(name.clone()); + } + + if let Some(mode) = benchmark_attrs.pov_mode { + let mut modes = Vec::new(); + // We cannot expand strings here since it is no-std, but syn does not expand bytes. + let name = name.to_string(); + let m = mode.root.to_string(); + modes.push(quote!(("ALL".as_bytes().to_vec(), #m.as_bytes().to_vec()))); + + for attr in mode.per_key.iter() { + // syn always puts spaces in quoted paths: + let key = attr.key.clone().into_token_stream().to_string().replace(" ", ""); + let mode = attr.mode.to_string(); + modes.push(quote!((#key.as_bytes().to_vec(), #mode.as_bytes().to_vec()))); + } + + pov_modes.push( + quote!((#name.as_bytes().to_vec(), #krate::__private::vec![#(#modes),*])), + ); + } + } + + // expand benchmark + let expanded = expand_benchmark(benchmark_def, name, instance, where_clause.clone()); + + // replace original function def with expanded code + *stmt = Item::Verbatim(expanded); + } + + // generics + let type_use_generics = match instance { + false => quote!(T), + true => quote!(T, I), + }; + let type_impl_generics = match instance { + false => quote!(T: Config), + true => quote!(T: Config, I: 'static), + }; + + let frame_system = generate_access_from_frame_or_crate("frame-system")?; + + // benchmark name variables + let benchmark_names_str: Vec = benchmark_names.iter().map(|n| n.to_string()).collect(); + let extra_benchmark_names_str: Vec = + extra_benchmark_names.iter().map(|n| n.to_string()).collect(); + let skip_meta_benchmark_names_str: Vec = + skip_meta_benchmark_names.iter().map(|n| n.to_string()).collect(); + let mut selected_benchmark_mappings: Vec = Vec::new(); + let mut benchmarks_by_name_mappings: Vec = Vec::new(); + let test_idents: Vec = benchmark_names_str + .iter() + .map(|n| Ident::new(format!("test_benchmark_{}", n).as_str(), Span::call_site())) + .collect(); + for i in 0..benchmark_names.len() { + let name_ident = &benchmark_names[i]; + let name_str = &benchmark_names_str[i]; + let test_ident = &test_idents[i]; + selected_benchmark_mappings.push(quote!(#name_str => SelectedBenchmark::#name_ident)); + benchmarks_by_name_mappings.push(quote!(#name_str => Self::#test_ident())) + } + + let impl_test_function = content + .iter_mut() + .find_map(|item| { + let Item::Macro(item_macro) = item else { + return None; + }; + + if !item_macro + .mac + .path + .segments + .iter() + .any(|s| s.ident == "impl_benchmark_test_suite") + { + return None; + } + + let tokens = item_macro.mac.tokens.clone(); + *item = Item::Verbatim(quote! {}); + + Some(quote! { + impl_test_function!( + (#( {} #benchmark_names )*) + (#( #extra_benchmark_names )*) + (#( #skip_meta_benchmark_names )*) + #tokens + ); + }) + }) + .unwrap_or(quote! {}); + + // emit final quoted tokens + let res = quote! { + #(#mod_attrs) + * + #mod_vis mod #mod_name { + #(#content) + * + + #[allow(non_camel_case_types)] + enum SelectedBenchmark { + #(#benchmark_names), + * + } + + impl<#type_impl_generics> #krate::BenchmarkingSetup<#type_use_generics> for SelectedBenchmark where #where_clause { + fn components(&self) -> #krate::__private::Vec<(#krate::BenchmarkParameter, u32, u32)> { + match self { + #( + Self::#benchmark_names => { + <#benchmark_names as #krate::BenchmarkingSetup<#type_use_generics>>::components(&#benchmark_names) + } + ) + * + } + } + + fn instance( + &self, + components: &[(#krate::BenchmarkParameter, u32)], + verify: bool, + ) -> Result< + #krate::__private::Box Result<(), #krate::BenchmarkError>>, + #krate::BenchmarkError, + > { + match self { + #( + Self::#benchmark_names => { + <#benchmark_names as #krate::BenchmarkingSetup< + #type_use_generics + >>::instance(&#benchmark_names, components, verify) + } + ) + * + } + } + } + #[cfg(any(feature = "runtime-benchmarks", test))] + impl<#type_impl_generics> #krate::Benchmarking for Pallet<#type_use_generics> + where T: #frame_system::Config, #where_clause + { + fn benchmarks( + extra: bool, + ) -> #krate::__private::Vec<#krate::BenchmarkMetadata> { + let mut all_names = #krate::__private::vec![ + #(#benchmark_names_str), + * + ]; + if !extra { + let extra = [ + #(#extra_benchmark_names_str), + * + ]; + all_names.retain(|x| !extra.contains(x)); + } + let pov_modes: + #krate::__private::Vec<( + #krate::__private::Vec, + #krate::__private::Vec<( + #krate::__private::Vec, + #krate::__private::Vec + )>, + )> = #krate::__private::vec![ + #( #pov_modes ),* + ]; + all_names.into_iter().map(|benchmark| { + let selected_benchmark = match benchmark { + #(#selected_benchmark_mappings), + *, + _ => panic!("all benchmarks should be selectable") + }; + let components = >::components(&selected_benchmark); + let name = benchmark.as_bytes().to_vec(); + let modes = pov_modes.iter().find(|p| p.0 == name).map(|p| p.1.clone()); + + #krate::BenchmarkMetadata { + name: benchmark.as_bytes().to_vec(), + components, + pov_modes: modes.unwrap_or_default(), + } + }).collect::<#krate::__private::Vec<_>>() + } + + fn run_benchmark( + extrinsic: &[u8], + c: &[(#krate::BenchmarkParameter, u32)], + whitelist: &[#krate::__private::TrackedStorageKey], + verify: bool, + internal_repeats: u32, + ) -> Result<#krate::__private::Vec<#krate::BenchmarkResult>, #krate::BenchmarkError> { + let extrinsic = #krate::__private::str::from_utf8(extrinsic).map_err(|_| "`extrinsic` is not a valid utf-8 string!")?; + let selected_benchmark = match extrinsic { + #(#selected_benchmark_mappings), + *, + _ => return Err("Could not find extrinsic.".into()), + }; + let mut whitelist = whitelist.to_vec(); + let whitelisted_caller_key = <#frame_system::Account< + T, + > as #krate::__private::storage::StorageMap<_, _,>>::hashed_key_for( + #krate::whitelisted_caller::() + ); + whitelist.push(whitelisted_caller_key.into()); + let transactional_layer_key = #krate::__private::TrackedStorageKey::new( + #krate::__private::storage::transactional::TRANSACTION_LEVEL_KEY.into(), + ); + whitelist.push(transactional_layer_key); + // Whitelist the `:extrinsic_index`. + let extrinsic_index = #krate::__private::TrackedStorageKey::new( + #krate::__private::well_known_keys::EXTRINSIC_INDEX.into() + ); + whitelist.push(extrinsic_index); + // Whitelist the `:intrablock_entropy`. + let intrablock_entropy = #krate::__private::TrackedStorageKey::new( + #krate::__private::well_known_keys::INTRABLOCK_ENTROPY.into() + ); + whitelist.push(intrablock_entropy); + + #krate::benchmarking::set_whitelist(whitelist.clone()); + let mut results: #krate::__private::Vec<#krate::BenchmarkResult> = #krate::__private::Vec::new(); + + // Always do at least one internal repeat... + for _ in 0 .. internal_repeats.max(1) { + // Always reset the state after the benchmark. + #krate::__private::defer!(#krate::benchmarking::wipe_db()); + + // Set up the externalities environment for the setup we want to + // benchmark. + let closure_to_benchmark = < + SelectedBenchmark as #krate::BenchmarkingSetup<#type_use_generics> + >::instance(&selected_benchmark, c, verify)?; + + // Set the block number to at least 1 so events are deposited. + if #krate::__private::Zero::is_zero(&#frame_system::Pallet::::block_number()) { + #frame_system::Pallet::::set_block_number(1u32.into()); + } + + // Commit the externalities to the database, flushing the DB cache. + // This will enable worst case scenario for reading from the database. + #krate::benchmarking::commit_db(); + + // Access all whitelisted keys to get them into the proof recorder since the + // recorder does now have a whitelist. + for key in &whitelist { + #krate::__private::storage::unhashed::get_raw(&key.key); + } + + // Reset the read/write counter so we don't count operations in the setup process. + #krate::benchmarking::reset_read_write_count(); + + // Time the extrinsic logic. + #krate::__private::log::trace!( + target: "benchmark", + "Start Benchmark: {} ({:?})", + extrinsic, + c + ); + + let start_pov = #krate::benchmarking::proof_size(); + let start_extrinsic = #krate::benchmarking::current_time(); + + closure_to_benchmark()?; + + let finish_extrinsic = #krate::benchmarking::current_time(); + let end_pov = #krate::benchmarking::proof_size(); + + // Calculate the diff caused by the benchmark. + let elapsed_extrinsic = finish_extrinsic.saturating_sub(start_extrinsic); + let diff_pov = match (start_pov, end_pov) { + (Some(start), Some(end)) => end.saturating_sub(start), + _ => Default::default(), + }; + + // Commit the changes to get proper write count + #krate::benchmarking::commit_db(); + #krate::__private::log::trace!( + target: "benchmark", + "End Benchmark: {} ns", elapsed_extrinsic + ); + let read_write_count = #krate::benchmarking::read_write_count(); + #krate::__private::log::trace!( + target: "benchmark", + "Read/Write Count {:?}", read_write_count + ); + + // Time the storage root recalculation. + let start_storage_root = #krate::benchmarking::current_time(); + #krate::__private::storage_root(#krate::__private::StateVersion::V1); + let finish_storage_root = #krate::benchmarking::current_time(); + let elapsed_storage_root = finish_storage_root - start_storage_root; + + let skip_meta = [ #(#skip_meta_benchmark_names_str),* ]; + let read_and_written_keys = if skip_meta.contains(&extrinsic) { + #krate::__private::vec![(b"Skipped Metadata".to_vec(), 0, 0, false)] + } else { + #krate::benchmarking::get_read_and_written_keys() + }; + + results.push(#krate::BenchmarkResult { + components: c.to_vec(), + extrinsic_time: elapsed_extrinsic, + storage_root_time: elapsed_storage_root, + reads: read_write_count.0, + repeat_reads: read_write_count.1, + writes: read_write_count.2, + repeat_writes: read_write_count.3, + proof_size: diff_pov, + keys: read_and_written_keys, + }); + } + + return Ok(results); + } + } + + #[cfg(test)] + impl<#type_impl_generics> Pallet<#type_use_generics> where T: #frame_system::Config, #where_clause { + /// Test a particular benchmark by name. + /// + /// This isn't called `test_benchmark_by_name` just in case some end-user eventually + /// writes a benchmark, itself called `by_name`; the function would be shadowed in + /// that case. + /// + /// This is generally intended to be used by child test modules such as those created + /// by the `impl_benchmark_test_suite` macro. However, it is not an error if a pallet + /// author chooses not to implement benchmarks. + #[allow(unused)] + fn test_bench_by_name(name: &[u8]) -> Result<(), #krate::BenchmarkError> { + let name = #krate::__private::str::from_utf8(name) + .map_err(|_| -> #krate::BenchmarkError { "`name` is not a valid utf8 string!".into() })?; + match name { + #(#benchmarks_by_name_mappings), + *, + _ => Err("Could not find test for requested benchmark.".into()), + } + } + } + + #impl_test_function + } + #mod_vis use #mod_name::*; + }; + Ok(res.into()) } /// Prepares a [`Vec`] to be interpolated by [`quote!`] by creating easily-iterable /// arrays formatted in such a way that they can be interpolated directly. struct UnrolledParams { - param_ranges: Vec, - param_names: Vec, + param_ranges: Vec, + param_names: Vec, } impl UnrolledParams { - /// Constructs an [`UnrolledParams`] from a [`Vec`] - fn from(params: &Vec) -> UnrolledParams { - let param_ranges: Vec = params - .iter() - .map(|p| { - let name = Ident::new(&p.name, Span::call_site()); - let start = &p.start; - let end = &p.end; - quote!(#name, #start, #end) - }) - .collect(); - let param_names: Vec = params - .iter() - .map(|p| { - let name = Ident::new(&p.name, Span::call_site()); - quote!(#name) - }) - .collect(); - UnrolledParams { - param_ranges, - param_names, - } - } + /// Constructs an [`UnrolledParams`] from a [`Vec`] + fn from(params: &Vec) -> UnrolledParams { + let param_ranges: Vec = params + .iter() + .map(|p| { + let name = Ident::new(&p.name, Span::call_site()); + let start = &p.start; + let end = &p.end; + quote!(#name, #start, #end) + }) + .collect(); + let param_names: Vec = params + .iter() + .map(|p| { + let name = Ident::new(&p.name, Span::call_site()); + quote!(#name) + }) + .collect(); + UnrolledParams { param_ranges, param_names } + } } /// Performs expansion of an already-parsed [`BenchmarkDef`]. fn expand_benchmark( - benchmark_def: BenchmarkDef, - name: &Ident, - is_instance: bool, - where_clause: TokenStream2, + benchmark_def: BenchmarkDef, + name: &Ident, + is_instance: bool, + where_clause: TokenStream2, ) -> TokenStream2 { - // set up variables needed during quoting - let krate = match generate_access_from_frame_or_crate("frame-benchmarking") { - Ok(ident) => ident, - Err(err) => return err.to_compile_error(), - }; - let frame_system = match generate_access_from_frame_or_crate("frame-system") { - Ok(path) => path, - Err(err) => return err.to_compile_error(), - }; - let codec = quote!(#krate::__private::codec); - let traits = quote!(#krate::__private::traits); - let setup_stmts = benchmark_def.setup_stmts; - let verify_stmts = benchmark_def.verify_stmts; - let last_stmt = benchmark_def.last_stmt; - let test_ident = Ident::new( - format!("test_benchmark_{}", name).as_str(), - Span::call_site(), - ); - - // unroll params (prepare for quoting) - let unrolled = UnrolledParams::from(&benchmark_def.params); - let param_names = unrolled.param_names; - let param_ranges = unrolled.param_ranges; - - let type_use_generics = match is_instance { - false => quote!(T), - true => quote!(T, I), - }; - - let type_impl_generics = match is_instance { - false => quote!(T: Config), - true => quote!(T: Config, I: 'static), - }; - - // used in the benchmarking impls - let (pre_call, post_call, fn_call_body) = match &benchmark_def.call_def { - BenchmarkCallDef::ExtrinsicCall { - origin, - expr_call, - attr_span: _, - } => { - let mut expr_call = expr_call.clone(); - - // remove first arg from expr_call - let mut final_args = Punctuated::::new(); - let args: Vec<&Expr> = expr_call.args.iter().collect(); - for arg in &args[1..] { - final_args.push((*(*arg)).clone()); - } - expr_call.args = final_args; - - let origin = match origin { - Expr::Cast(t) => { - let ty = t.ty.clone(); - quote! { - <::RuntimeOrigin as From<#ty>>::from(#origin); - } - } - _ => quote! { - #origin.into(); - }, - }; - - // determine call name (handles `_` and normal call syntax) - let expr_span = expr_call.span(); - let call_err = || { - syn::Error::new(expr_span, "Extrinsic call must be a function call or `_`") - .to_compile_error() - }; - let call_name = match *expr_call.func { - Expr::Path(expr_path) => { - // normal function call - let Some(segment) = expr_path.path.segments.last() else { - return call_err(); - }; - segment.ident.to_string() - } - Expr::Infer(_) => { - // `_` style - // replace `_` with fn name - name.to_string() - } - _ => return call_err(), - }; - - // modify extrinsic call to be prefixed with "new_call_variant" - let call_name = format!("new_call_variant_{}", call_name); - let mut punct: Punctuated = Punctuated::new(); - punct.push(PathSegment { - arguments: PathArguments::None, - ident: Ident::new(call_name.as_str(), Span::call_site()), - }); - *expr_call.func = Expr::Path(ExprPath { - attrs: vec![], - qself: None, - path: Path { - leading_colon: None, - segments: punct, - }, - }); - let pre_call = quote! { - let __call = Call::<#type_use_generics>::#expr_call; - let __benchmarked_call_encoded = #codec::Encode::encode(&__call); - }; - let post_call = quote! { - let __call_decoded = as #codec::Decode> - ::decode(&mut &__benchmarked_call_encoded[..]) - .expect("call is encoded above, encoding must be correct"); - let __origin = #origin; - as #traits::UnfilteredDispatchable>::dispatch_bypass_filter( - __call_decoded, - __origin, - ) - }; - ( - // (pre_call, post_call, fn_call_body): - pre_call.clone(), - quote!(#post_call?;), - quote! { - #pre_call - #post_call.unwrap(); - }, - ) - } - BenchmarkCallDef::Block { - block, - attr_span: _, - } => (quote!(), quote!(#block), quote!(#block)), - }; - - let vis = benchmark_def.fn_vis; - - // remove #[benchmark] attribute - let fn_attrs = benchmark_def - .fn_attrs - .iter() - .filter(|attr| !attr.path().is_ident(keywords::BENCHMARK_TOKEN)); - - // modify signature generics, ident, and inputs, e.g: - // before: `fn bench(u: Linear<1, 100>) -> Result<(), BenchmarkError>` - // after: `fn _bench , I: 'static>(u: u32, verify: bool) -> Result<(), - // BenchmarkError>` - let mut sig = benchmark_def.fn_sig; - sig.generics = parse_quote!(<#type_impl_generics>); - if !where_clause.is_empty() { - sig.generics.where_clause = parse_quote!(where #where_clause); - } - sig.ident = Ident::new( - format!("_{}", name.to_token_stream()).as_str(), - Span::call_site(), - ); - let mut fn_param_inputs: Vec = - param_names.iter().map(|name| quote!(#name: u32)).collect(); - fn_param_inputs.push(quote!(verify: bool)); - sig.inputs = parse_quote!(#(#fn_param_inputs),*); - - // used in instance() impl - let impl_last_stmt = match &last_stmt { - Some(stmt) => quote!(#stmt), - None => quote!(Ok(())), - }; - let fn_attrs_clone = fn_attrs.clone(); - - let fn_def = quote! { - #( - #fn_attrs_clone - )* - #vis #sig { - #( - #setup_stmts - )* - #fn_call_body - if verify { - #( - #verify_stmts - )* - } - #last_stmt - } - }; - - // generate final quoted tokens - let res = quote! { - // benchmark function definition - #fn_def - - #[allow(non_camel_case_types)] - #( - #fn_attrs - )* - struct #name; - - #[allow(unused_variables)] - impl<#type_impl_generics> #krate::BenchmarkingSetup<#type_use_generics> - for #name where #where_clause { - fn components(&self) -> #krate::__private::Vec<(#krate::BenchmarkParameter, u32, u32)> { - #krate::__private::vec! [ - #( - (#krate::BenchmarkParameter::#param_ranges) - ),* - ] - } - - fn instance( - &self, - components: &[(#krate::BenchmarkParameter, u32)], - verify: bool - ) -> Result<#krate::__private::Box Result<(), #krate::BenchmarkError>>, #krate::BenchmarkError> { - #( - // prepare instance #param_names - let #param_names = components.iter() - .find(|&c| c.0 == #krate::BenchmarkParameter::#param_names) - .ok_or("Could not find component during benchmark preparation.")? - .1; - )* - - // benchmark setup code - #( - #setup_stmts - )* - #pre_call - Ok(#krate::__private::Box::new(move || -> Result<(), #krate::BenchmarkError> { - #post_call - if verify { - #( - #verify_stmts - )* - } - #impl_last_stmt - })) - } - } - - #[cfg(test)] - impl<#type_impl_generics> Pallet<#type_use_generics> where T: #frame_system::Config, #where_clause { - #[allow(unused)] - fn #test_ident() -> Result<(), #krate::BenchmarkError> { - let selected_benchmark = SelectedBenchmark::#name; - let components = < - SelectedBenchmark as #krate::BenchmarkingSetup - >::components(&selected_benchmark); - let execute_benchmark = | - c: #krate::__private::Vec<(#krate::BenchmarkParameter, u32)> - | -> Result<(), #krate::BenchmarkError> { - // Always reset the state after the benchmark. - #krate::__private::defer!(#krate::benchmarking::wipe_db()); - - // Set up the benchmark, return execution + verification function. - let closure_to_verify = < - SelectedBenchmark as #krate::BenchmarkingSetup - >::instance(&selected_benchmark, &c, true)?; - - // Set the block number to at least 1 so events are deposited. - if #krate::__private::Zero::is_zero(&#frame_system::Pallet::::block_number()) { - #frame_system::Pallet::::set_block_number(1u32.into()); - } - - // Run execution + verification - closure_to_verify() - }; - - if components.is_empty() { - execute_benchmark(Default::default())?; - } else { - let num_values: u32 = if let Ok(ev) = std::env::var("VALUES_PER_COMPONENT") { - ev.parse().map_err(|_| { - #krate::BenchmarkError::Stop( - "Could not parse env var `VALUES_PER_COMPONENT` as u32." - ) - })? - } else { - 6 - }; - - if num_values < 2 { - return Err("`VALUES_PER_COMPONENT` must be at least 2".into()); - } - - for (name, low, high) in components.clone().into_iter() { - // Test the lowest, highest (if its different from the lowest) - // and up to num_values-2 more equidistant values in between. - // For 0..10 and num_values=6 this would mean: [0, 2, 4, 6, 8, 10] - if high < low { - return Err("The start of a `ParamRange` must be less than or equal to the end".into()); - } - - let mut values = #krate::__private::vec![low]; - let diff = (high - low).min(num_values - 1); - let slope = (high - low) as f32 / diff as f32; - - for i in 1..=diff { - let value = ((low as f32 + slope * i as f32) as u32) - .clamp(low, high); - values.push(value); - } - - for component_value in values { - // Select the max value for all the other components. - let c: #krate::__private::Vec<(#krate::BenchmarkParameter, u32)> = components - .iter() - .map(|(n, _, h)| - if *n == name { - (*n, component_value) - } else { - (*n, *h) - } - ) - .collect(); - - execute_benchmark(c)?; - } - } - } - return Ok(()); - } - } - }; - res + // set up variables needed during quoting + let krate = match generate_access_from_frame_or_crate("frame-benchmarking") { + Ok(ident) => ident, + Err(err) => return err.to_compile_error().into(), + }; + let frame_system = match generate_access_from_frame_or_crate("frame-system") { + Ok(path) => path, + Err(err) => return err.to_compile_error().into(), + }; + let codec = quote!(#krate::__private::codec); + let traits = quote!(#krate::__private::traits); + let setup_stmts = benchmark_def.setup_stmts; + let verify_stmts = benchmark_def.verify_stmts; + let last_stmt = benchmark_def.last_stmt; + let test_ident = + Ident::new(format!("test_benchmark_{}", name.to_string()).as_str(), Span::call_site()); + + // unroll params (prepare for quoting) + let unrolled = UnrolledParams::from(&benchmark_def.params); + let param_names = unrolled.param_names; + let param_ranges = unrolled.param_ranges; + + let type_use_generics = match is_instance { + false => quote!(T), + true => quote!(T, I), + }; + + let type_impl_generics = match is_instance { + false => quote!(T: Config), + true => quote!(T: Config, I: 'static), + }; + + // used in the benchmarking impls + let (pre_call, post_call, fn_call_body) = match &benchmark_def.call_def { + BenchmarkCallDef::ExtrinsicCall { origin, expr_call, attr_span: _ } => { + let mut expr_call = expr_call.clone(); + + // remove first arg from expr_call + let mut final_args = Punctuated::::new(); + let args: Vec<&Expr> = expr_call.args.iter().collect(); + for arg in &args[1..] { + final_args.push((*(*arg)).clone()); + } + expr_call.args = final_args; + + let origin = match origin { + Expr::Cast(t) => { + let ty = t.ty.clone(); + quote! { + <::RuntimeOrigin as From<#ty>>::from(#origin); + } + }, + _ => quote! { + #origin.into(); + }, + }; + + // determine call name (handles `_` and normal call syntax) + let expr_span = expr_call.span(); + let call_err = || { + syn::Error::new(expr_span, "Extrinsic call must be a function call or `_`") + .to_compile_error() + }; + let call_name = match *expr_call.func { + Expr::Path(expr_path) => { + // normal function call + let Some(segment) = expr_path.path.segments.last() else { return call_err() }; + segment.ident.to_string() + }, + Expr::Infer(_) => { + // `_` style + // replace `_` with fn name + name.to_string() + }, + _ => return call_err(), + }; + + // modify extrinsic call to be prefixed with "new_call_variant" + let call_name = format!("new_call_variant_{}", call_name); + let mut punct: Punctuated = Punctuated::new(); + punct.push(PathSegment { + arguments: PathArguments::None, + ident: Ident::new(call_name.as_str(), Span::call_site()), + }); + *expr_call.func = Expr::Path(ExprPath { + attrs: vec![], + qself: None, + path: Path { leading_colon: None, segments: punct }, + }); + let pre_call = quote! { + let __call = Call::<#type_use_generics>::#expr_call; + let __benchmarked_call_encoded = #codec::Encode::encode(&__call); + }; + let post_call = quote! { + let __call_decoded = as #codec::Decode> + ::decode(&mut &__benchmarked_call_encoded[..]) + .expect("call is encoded above, encoding must be correct"); + let __origin = #origin; + as #traits::UnfilteredDispatchable>::dispatch_bypass_filter( + __call_decoded, + __origin, + ) + }; + ( + // (pre_call, post_call, fn_call_body): + pre_call.clone(), + quote!(#post_call?;), + quote! { + #pre_call + #post_call.unwrap(); + }, + ) + }, + BenchmarkCallDef::Block { block, attr_span: _ } => + (quote!(), quote!(#block), quote!(#block)), + }; + + let vis = benchmark_def.fn_vis; + + // remove #[benchmark] attribute + let fn_attrs = benchmark_def + .fn_attrs + .iter() + .filter(|attr| !attr.path().is_ident(keywords::BENCHMARK_TOKEN)); + + // modify signature generics, ident, and inputs, e.g: + // before: `fn bench(u: Linear<1, 100>) -> Result<(), BenchmarkError>` + // after: `fn _bench , I: 'static>(u: u32, verify: bool) -> Result<(), + // BenchmarkError>` + let mut sig = benchmark_def.fn_sig; + sig.generics = parse_quote!(<#type_impl_generics>); + if !where_clause.is_empty() { + sig.generics.where_clause = parse_quote!(where #where_clause); + } + sig.ident = + Ident::new(format!("_{}", name.to_token_stream().to_string()).as_str(), Span::call_site()); + let mut fn_param_inputs: Vec = + param_names.iter().map(|name| quote!(#name: u32)).collect(); + fn_param_inputs.push(quote!(verify: bool)); + sig.inputs = parse_quote!(#(#fn_param_inputs),*); + + // used in instance() impl + let impl_last_stmt = match &last_stmt { + Some(stmt) => quote!(#stmt), + None => quote!(Ok(())), + }; + let fn_attrs_clone = fn_attrs.clone(); + + let fn_def = quote! { + #( + #fn_attrs_clone + )* + #vis #sig { + #( + #setup_stmts + )* + #fn_call_body + if verify { + #( + #verify_stmts + )* + } + #last_stmt + } + }; + + // generate final quoted tokens + let res = quote! { + // benchmark function definition + #fn_def + + #[allow(non_camel_case_types)] + #( + #fn_attrs + )* + struct #name; + + #[allow(unused_variables)] + impl<#type_impl_generics> #krate::BenchmarkingSetup<#type_use_generics> + for #name where #where_clause { + fn components(&self) -> #krate::__private::Vec<(#krate::BenchmarkParameter, u32, u32)> { + #krate::__private::vec! [ + #( + (#krate::BenchmarkParameter::#param_ranges) + ),* + ] + } + + fn instance( + &self, + components: &[(#krate::BenchmarkParameter, u32)], + verify: bool + ) -> Result<#krate::__private::Box Result<(), #krate::BenchmarkError>>, #krate::BenchmarkError> { + #( + // prepare instance #param_names + let #param_names = components.iter() + .find(|&c| c.0 == #krate::BenchmarkParameter::#param_names) + .ok_or("Could not find component during benchmark preparation.")? + .1; + )* + + // benchmark setup code + #( + #setup_stmts + )* + #pre_call + Ok(#krate::__private::Box::new(move || -> Result<(), #krate::BenchmarkError> { + #post_call + if verify { + #( + #verify_stmts + )* + } + #impl_last_stmt + })) + } + } + + #[cfg(test)] + impl<#type_impl_generics> Pallet<#type_use_generics> where T: #frame_system::Config, #where_clause { + #[allow(unused)] + fn #test_ident() -> Result<(), #krate::BenchmarkError> { + let selected_benchmark = SelectedBenchmark::#name; + let components = < + SelectedBenchmark as #krate::BenchmarkingSetup + >::components(&selected_benchmark); + let execute_benchmark = | + c: #krate::__private::Vec<(#krate::BenchmarkParameter, u32)> + | -> Result<(), #krate::BenchmarkError> { + // Always reset the state after the benchmark. + #krate::__private::defer!(#krate::benchmarking::wipe_db()); + + // Set up the benchmark, return execution + verification function. + let closure_to_verify = < + SelectedBenchmark as #krate::BenchmarkingSetup + >::instance(&selected_benchmark, &c, true)?; + + // Set the block number to at least 1 so events are deposited. + if #krate::__private::Zero::is_zero(&#frame_system::Pallet::::block_number()) { + #frame_system::Pallet::::set_block_number(1u32.into()); + } + + // Run execution + verification + closure_to_verify() + }; + + if components.is_empty() { + execute_benchmark(Default::default())?; + } else { + let num_values: u32 = if let Ok(ev) = std::env::var("VALUES_PER_COMPONENT") { + ev.parse().map_err(|_| { + #krate::BenchmarkError::Stop( + "Could not parse env var `VALUES_PER_COMPONENT` as u32." + ) + })? + } else { + 6 + }; + + if num_values < 2 { + return Err("`VALUES_PER_COMPONENT` must be at least 2".into()); + } + + for (name, low, high) in components.clone().into_iter() { + // Test the lowest, highest (if its different from the lowest) + // and up to num_values-2 more equidistant values in between. + // For 0..10 and num_values=6 this would mean: [0, 2, 4, 6, 8, 10] + if high < low { + return Err("The start of a `ParamRange` must be less than or equal to the end".into()); + } + + let mut values = #krate::__private::vec![low]; + let diff = (high - low).min(num_values - 1); + let slope = (high - low) as f32 / diff as f32; + + for i in 1..=diff { + let value = ((low as f32 + slope * i as f32) as u32) + .clamp(low, high); + values.push(value); + } + + for component_value in values { + // Select the max value for all the other components. + let c: #krate::__private::Vec<(#krate::BenchmarkParameter, u32)> = components + .iter() + .map(|(n, _, h)| + if *n == name { + (*n, component_value) + } else { + (*n, *h) + } + ) + .collect(); + + execute_benchmark(c)?; + } + } + } + return Ok(()); + } + } + }; + res } diff --git a/support/procedural-fork/src/construct_runtime/expand/call.rs b/support/procedural-fork/src/construct_runtime/expand/call.rs index 7e8c2e856..b0041ccc0 100644 --- a/support/procedural-fork/src/construct_runtime/expand/call.rs +++ b/support/procedural-fork/src/construct_runtime/expand/call.rs @@ -22,205 +22,202 @@ use std::str::FromStr; use syn::Ident; pub fn expand_outer_dispatch( - runtime: &Ident, - system_pallet: &Pallet, - pallet_decls: &[Pallet], - scrate: &TokenStream, + runtime: &Ident, + system_pallet: &Pallet, + pallet_decls: &[Pallet], + scrate: &TokenStream, ) -> TokenStream { - let mut variant_defs = TokenStream::new(); - let mut variant_patterns = Vec::new(); - let mut query_call_part_macros = Vec::new(); - let mut pallet_names = Vec::new(); - let mut pallet_attrs = Vec::new(); - let system_path = &system_pallet.path; - - let pallets_with_call = pallet_decls.iter().filter(|decl| decl.exists_part("Call")); - - for pallet_declaration in pallets_with_call { - let name = &pallet_declaration.name; - let path = &pallet_declaration.path; - let index = pallet_declaration.index; - let attr = - pallet_declaration - .cfg_pattern - .iter() - .fold(TokenStream::new(), |acc, pattern| { - let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) - .expect("was successfully parsed before; qed"); - quote! { - #acc - #attr - } - }); - - variant_defs.extend(quote! { - #attr - #[codec(index = #index)] - #name( #scrate::dispatch::CallableCallFor<#name, #runtime> ), - }); - variant_patterns.push(quote!(RuntimeCall::#name(call))); - pallet_names.push(name); - pallet_attrs.push(attr); - query_call_part_macros.push(quote! { - #path::__substrate_call_check::is_call_part_defined!(#name); - }); - } - - quote! { - #( #query_call_part_macros )* - - #[derive( - Clone, PartialEq, Eq, - #scrate::__private::codec::Encode, - #scrate::__private::codec::Decode, - #scrate::__private::scale_info::TypeInfo, - #scrate::__private::RuntimeDebug, - )] - pub enum RuntimeCall { - #variant_defs - } - #[cfg(test)] - impl RuntimeCall { - /// Return a list of the module names together with their size in memory. - pub const fn sizes() -> &'static [( &'static str, usize )] { - use #scrate::dispatch::Callable; - use core::mem::size_of; - &[#( - #pallet_attrs - ( - stringify!(#pallet_names), - size_of::< <#pallet_names as Callable<#runtime>>::RuntimeCall >(), - ), - )*] - } - - /// Panics with diagnostic information if the size is greater than the given `limit`. - pub fn assert_size_under(limit: usize) { - let size = core::mem::size_of::(); - let call_oversize = size > limit; - if call_oversize { - println!("Size of `Call` is {} bytes (provided limit is {} bytes)", size, limit); - let mut sizes = Self::sizes().to_vec(); - sizes.sort_by_key(|x| -(x.1 as isize)); - for (i, &(name, size)) in sizes.iter().enumerate().take(5) { - println!("Offender #{}: {} at {} bytes", i + 1, name, size); - } - if let Some((_, next_size)) = sizes.get(5) { - println!("{} others of size {} bytes or less", sizes.len() - 5, next_size); - } - panic!( - "Size of `Call` is more than limit; use `Box` on complex parameter types to reduce the + let mut variant_defs = TokenStream::new(); + let mut variant_patterns = Vec::new(); + let mut query_call_part_macros = Vec::new(); + let mut pallet_names = Vec::new(); + let mut pallet_attrs = Vec::new(); + let system_path = &system_pallet.path; + + let pallets_with_call = pallet_decls.iter().filter(|decl| decl.exists_part("Call")); + + for pallet_declaration in pallets_with_call { + let name = &pallet_declaration.name; + let path = &pallet_declaration.path; + let index = pallet_declaration.index; + let attr = + pallet_declaration.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); + + variant_defs.extend(quote! { + #attr + #[codec(index = #index)] + #name( #scrate::dispatch::CallableCallFor<#name, #runtime> ), + }); + variant_patterns.push(quote!(RuntimeCall::#name(call))); + pallet_names.push(name); + pallet_attrs.push(attr); + query_call_part_macros.push(quote! { + #path::__substrate_call_check::is_call_part_defined!(#name); + }); + } + + quote! { + #( #query_call_part_macros )* + + #[derive( + Clone, PartialEq, Eq, + #scrate::__private::codec::Encode, + #scrate::__private::codec::Decode, + #scrate::__private::scale_info::TypeInfo, + #scrate::__private::RuntimeDebug, + )] + pub enum RuntimeCall { + #variant_defs + } + #[cfg(test)] + impl RuntimeCall { + /// Return a list of the module names together with their size in memory. + pub const fn sizes() -> &'static [( &'static str, usize )] { + use #scrate::dispatch::Callable; + use core::mem::size_of; + &[#( + #pallet_attrs + ( + stringify!(#pallet_names), + size_of::< <#pallet_names as Callable<#runtime>>::RuntimeCall >(), + ), + )*] + } + + /// Panics with diagnostic information if the size is greater than the given `limit`. + pub fn assert_size_under(limit: usize) { + let size = core::mem::size_of::(); + let call_oversize = size > limit; + if call_oversize { + println!("Size of `Call` is {} bytes (provided limit is {} bytes)", size, limit); + let mut sizes = Self::sizes().to_vec(); + sizes.sort_by_key(|x| -(x.1 as isize)); + for (i, &(name, size)) in sizes.iter().enumerate().take(5) { + println!("Offender #{}: {} at {} bytes", i + 1, name, size); + } + if let Some((_, next_size)) = sizes.get(5) { + println!("{} others of size {} bytes or less", sizes.len() - 5, next_size); + } + panic!( + "Size of `Call` is more than limit; use `Box` on complex parameter types to reduce the size of `Call`. If the limit is too strong, maybe consider providing a higher limit." - ); - } - } - } - impl #scrate::dispatch::GetDispatchInfo for RuntimeCall { - fn get_dispatch_info(&self) -> #scrate::dispatch::DispatchInfo { - match self { - #( - #pallet_attrs - #variant_patterns => call.get_dispatch_info(), - )* - } - } - } - - impl #scrate::dispatch::CheckIfFeeless for RuntimeCall { - type Origin = #system_path::pallet_prelude::OriginFor<#runtime>; - fn is_feeless(&self, origin: &Self::Origin) -> bool { - match self { - #( - #pallet_attrs - #variant_patterns => call.is_feeless(origin), - )* - } - } - } - - impl #scrate::traits::GetCallMetadata for RuntimeCall { - fn get_call_metadata(&self) -> #scrate::traits::CallMetadata { - use #scrate::traits::GetCallName; - match self { - #( - #pallet_attrs - #variant_patterns => { - let function_name = call.get_call_name(); - let pallet_name = stringify!(#pallet_names); - #scrate::traits::CallMetadata { function_name, pallet_name } - } - )* - } - } - - fn get_module_names() -> &'static [&'static str] { - &[#( - #pallet_attrs - stringify!(#pallet_names), - )*] - } - - fn get_call_names(module: &str) -> &'static [&'static str] { - use #scrate::{dispatch::Callable, traits::GetCallName}; - match module { - #( - #pallet_attrs - stringify!(#pallet_names) => - <<#pallet_names as Callable<#runtime>>::RuntimeCall - as GetCallName>::get_call_names(), - )* - _ => unreachable!(), - } - } - } - impl #scrate::__private::Dispatchable for RuntimeCall { - type RuntimeOrigin = RuntimeOrigin; - type Config = RuntimeCall; - type Info = #scrate::dispatch::DispatchInfo; - type PostInfo = #scrate::dispatch::PostDispatchInfo; - fn dispatch(self, origin: RuntimeOrigin) -> #scrate::dispatch::DispatchResultWithPostInfo { - if !::filter_call(&origin, &self) { - return ::core::result::Result::Err( - #system_path::Error::<#runtime>::CallFiltered.into() - ); - } - - #scrate::traits::UnfilteredDispatchable::dispatch_bypass_filter(self, origin) - } - } - impl #scrate::traits::UnfilteredDispatchable for RuntimeCall { - type RuntimeOrigin = RuntimeOrigin; - fn dispatch_bypass_filter(self, origin: RuntimeOrigin) -> #scrate::dispatch::DispatchResultWithPostInfo { - match self { - #( - #pallet_attrs - #variant_patterns => - #scrate::traits::UnfilteredDispatchable::dispatch_bypass_filter(call, origin), - )* - } - } - } - - #( - #pallet_attrs - impl #scrate::traits::IsSubType<#scrate::dispatch::CallableCallFor<#pallet_names, #runtime>> for RuntimeCall { - #[allow(unreachable_patterns)] - fn is_sub_type(&self) -> Option<&#scrate::dispatch::CallableCallFor<#pallet_names, #runtime>> { - match self { - #variant_patterns => Some(call), - // May be unreachable - _ => None, - } - } - } - - #pallet_attrs - impl From<#scrate::dispatch::CallableCallFor<#pallet_names, #runtime>> for RuntimeCall { - fn from(call: #scrate::dispatch::CallableCallFor<#pallet_names, #runtime>) -> Self { - #variant_patterns - } - } - )* - } + ); + } + } + } + impl #scrate::dispatch::GetDispatchInfo for RuntimeCall { + fn get_dispatch_info(&self) -> #scrate::dispatch::DispatchInfo { + match self { + #( + #pallet_attrs + #variant_patterns => call.get_dispatch_info(), + )* + } + } + } + + impl #scrate::dispatch::CheckIfFeeless for RuntimeCall { + type Origin = #system_path::pallet_prelude::OriginFor<#runtime>; + fn is_feeless(&self, origin: &Self::Origin) -> bool { + match self { + #( + #pallet_attrs + #variant_patterns => call.is_feeless(origin), + )* + } + } + } + + impl #scrate::traits::GetCallMetadata for RuntimeCall { + fn get_call_metadata(&self) -> #scrate::traits::CallMetadata { + use #scrate::traits::GetCallName; + match self { + #( + #pallet_attrs + #variant_patterns => { + let function_name = call.get_call_name(); + let pallet_name = stringify!(#pallet_names); + #scrate::traits::CallMetadata { function_name, pallet_name } + } + )* + } + } + + fn get_module_names() -> &'static [&'static str] { + &[#( + #pallet_attrs + stringify!(#pallet_names), + )*] + } + + fn get_call_names(module: &str) -> &'static [&'static str] { + use #scrate::{dispatch::Callable, traits::GetCallName}; + match module { + #( + #pallet_attrs + stringify!(#pallet_names) => + <<#pallet_names as Callable<#runtime>>::RuntimeCall + as GetCallName>::get_call_names(), + )* + _ => unreachable!(), + } + } + } + impl #scrate::__private::Dispatchable for RuntimeCall { + type RuntimeOrigin = RuntimeOrigin; + type Config = RuntimeCall; + type Info = #scrate::dispatch::DispatchInfo; + type PostInfo = #scrate::dispatch::PostDispatchInfo; + fn dispatch(self, origin: RuntimeOrigin) -> #scrate::dispatch::DispatchResultWithPostInfo { + if !::filter_call(&origin, &self) { + return ::core::result::Result::Err( + #system_path::Error::<#runtime>::CallFiltered.into() + ); + } + + #scrate::traits::UnfilteredDispatchable::dispatch_bypass_filter(self, origin) + } + } + impl #scrate::traits::UnfilteredDispatchable for RuntimeCall { + type RuntimeOrigin = RuntimeOrigin; + fn dispatch_bypass_filter(self, origin: RuntimeOrigin) -> #scrate::dispatch::DispatchResultWithPostInfo { + match self { + #( + #pallet_attrs + #variant_patterns => + #scrate::traits::UnfilteredDispatchable::dispatch_bypass_filter(call, origin), + )* + } + } + } + + #( + #pallet_attrs + impl #scrate::traits::IsSubType<#scrate::dispatch::CallableCallFor<#pallet_names, #runtime>> for RuntimeCall { + #[allow(unreachable_patterns)] + fn is_sub_type(&self) -> Option<&#scrate::dispatch::CallableCallFor<#pallet_names, #runtime>> { + match self { + #variant_patterns => Some(call), + // May be unreachable + _ => None, + } + } + } + + #pallet_attrs + impl From<#scrate::dispatch::CallableCallFor<#pallet_names, #runtime>> for RuntimeCall { + fn from(call: #scrate::dispatch::CallableCallFor<#pallet_names, #runtime>) -> Self { + #variant_patterns + } + } + )* + } } diff --git a/support/procedural-fork/src/construct_runtime/expand/composite_helper.rs b/support/procedural-fork/src/construct_runtime/expand/composite_helper.rs index be6b2f085..101a476fb 100644 --- a/support/procedural-fork/src/construct_runtime/expand/composite_helper.rs +++ b/support/procedural-fork/src/construct_runtime/expand/composite_helper.rs @@ -20,82 +20,82 @@ use proc_macro2::{Ident, TokenStream}; use quote::quote; pub(crate) fn expand_conversion_fn( - composite_name: &str, - path: &PalletPath, - instance: Option<&Ident>, - variant_name: &Ident, + composite_name: &str, + path: &PalletPath, + instance: Option<&Ident>, + variant_name: &Ident, ) -> TokenStream { - let composite_name = quote::format_ident!("{}", composite_name); - let runtime_composite_name = quote::format_ident!("Runtime{}", composite_name); + let composite_name = quote::format_ident!("{}", composite_name); + let runtime_composite_name = quote::format_ident!("Runtime{}", composite_name); - if let Some(inst) = instance { - quote! { - impl From<#path::#composite_name<#path::#inst>> for #runtime_composite_name { - fn from(hr: #path::#composite_name<#path::#inst>) -> Self { - #runtime_composite_name::#variant_name(hr) - } - } - } - } else { - quote! { - impl From<#path::#composite_name> for #runtime_composite_name { - fn from(hr: #path::#composite_name) -> Self { - #runtime_composite_name::#variant_name(hr) - } - } - } - } + if let Some(inst) = instance { + quote! { + impl From<#path::#composite_name<#path::#inst>> for #runtime_composite_name { + fn from(hr: #path::#composite_name<#path::#inst>) -> Self { + #runtime_composite_name::#variant_name(hr) + } + } + } + } else { + quote! { + impl From<#path::#composite_name> for #runtime_composite_name { + fn from(hr: #path::#composite_name) -> Self { + #runtime_composite_name::#variant_name(hr) + } + } + } + } } pub(crate) fn expand_variant( - composite_name: &str, - index: u8, - path: &PalletPath, - instance: Option<&Ident>, - variant_name: &Ident, + composite_name: &str, + index: u8, + path: &PalletPath, + instance: Option<&Ident>, + variant_name: &Ident, ) -> TokenStream { - let composite_name = quote::format_ident!("{}", composite_name); + let composite_name = quote::format_ident!("{}", composite_name); - if let Some(inst) = instance { - quote! { - #[codec(index = #index)] - #variant_name(#path::#composite_name<#path::#inst>), - } - } else { - quote! { - #[codec(index = #index)] - #variant_name(#path::#composite_name), - } - } + if let Some(inst) = instance { + quote! { + #[codec(index = #index)] + #variant_name(#path::#composite_name<#path::#inst>), + } + } else { + quote! { + #[codec(index = #index)] + #variant_name(#path::#composite_name), + } + } } pub(crate) fn expand_variant_count( - composite_name: &str, - path: &PalletPath, - instance: Option<&Ident>, + composite_name: &str, + path: &PalletPath, + instance: Option<&Ident>, ) -> TokenStream { - let composite_name = quote::format_ident!("{}", composite_name); + let composite_name = quote::format_ident!("{}", composite_name); - if let Some(inst) = instance { - quote! { - #path::#composite_name::<#path::#inst>::VARIANT_COUNT - } - } else { - // Wrapped `<`..`>` means: use default type parameter for enum. - // - // This is used for pallets without instance support or pallets with instance support when - // we don't specify instance: - // - // ``` - // pub struct Pallet{..} - // - // #[pallet::composite_enum] - // pub enum HoldReason {..} - // - // Pallet1: pallet_x, // <- default type parameter - // ``` - quote! { - <#path::#composite_name>::VARIANT_COUNT - } - } + if let Some(inst) = instance { + quote! { + #path::#composite_name::<#path::#inst>::VARIANT_COUNT + } + } else { + // Wrapped `<`..`>` means: use default type parameter for enum. + // + // This is used for pallets without instance support or pallets with instance support when + // we don't specify instance: + // + // ``` + // pub struct Pallet{..} + // + // #[pallet::composite_enum] + // pub enum HoldReason {..} + // + // Pallet1: pallet_x, // <- default type parameter + // ``` + quote! { + <#path::#composite_name>::VARIANT_COUNT + } + } } diff --git a/support/procedural-fork/src/construct_runtime/expand/config.rs b/support/procedural-fork/src/construct_runtime/expand/config.rs index ff715e584..dbbe6ba6e 100644 --- a/support/procedural-fork/src/construct_runtime/expand/config.rs +++ b/support/procedural-fork/src/construct_runtime/expand/config.rs @@ -23,135 +23,125 @@ use std::str::FromStr; use syn::Ident; pub fn expand_outer_config( - runtime: &Ident, - pallet_decls: &[Pallet], - scrate: &TokenStream, + runtime: &Ident, + pallet_decls: &[Pallet], + scrate: &TokenStream, ) -> TokenStream { - let mut types = TokenStream::new(); - let mut fields = TokenStream::new(); - let mut genesis_build_calls = TokenStream::new(); - let mut query_genesis_config_part_macros = Vec::new(); + let mut types = TokenStream::new(); + let mut fields = TokenStream::new(); + let mut genesis_build_calls = TokenStream::new(); + let mut query_genesis_config_part_macros = Vec::new(); - for decl in pallet_decls { - if let Some(pallet_entry) = decl.find_part("Config") { - let path = &decl.path; - let pallet_name = &decl.name; - let path_str = path.into_token_stream().to_string(); - let config = format_ident!("{}Config", pallet_name); - let field_name = - &Ident::new(&pallet_name.to_string().to_snake_case(), decl.name.span()); - let part_is_generic = !pallet_entry.generics.params.is_empty(); - let attr = &decl - .cfg_pattern - .iter() - .fold(TokenStream::new(), |acc, pattern| { - let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) - .expect("was successfully parsed before; qed"); - quote! { - #acc - #attr - } - }); + for decl in pallet_decls { + if let Some(pallet_entry) = decl.find_part("Config") { + let path = &decl.path; + let pallet_name = &decl.name; + let path_str = path.into_token_stream().to_string(); + let config = format_ident!("{}Config", pallet_name); + let field_name = + &Ident::new(&pallet_name.to_string().to_snake_case(), decl.name.span()); + let part_is_generic = !pallet_entry.generics.params.is_empty(); + let attr = &decl.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); - types.extend(expand_config_types( - attr, - runtime, - decl, - &config, - part_is_generic, - )); - fields.extend(quote!(#attr pub #field_name: #config,)); - genesis_build_calls.extend(expand_config_build_storage_call( - scrate, &config, attr, field_name, - )); - query_genesis_config_part_macros.push(quote! { + types.extend(expand_config_types(attr, runtime, decl, &config, part_is_generic)); + fields.extend(quote!(#attr pub #field_name: #config,)); + genesis_build_calls + .extend(expand_config_build_storage_call(scrate, &config, attr, field_name)); + query_genesis_config_part_macros.push(quote! { #path::__substrate_genesis_config_check::is_genesis_config_defined!(#pallet_name); #[cfg(feature = "std")] #path::__substrate_genesis_config_check::is_std_enabled_for_genesis!(#pallet_name, #path_str); }); - } - } + } + } - quote! { - #( #query_genesis_config_part_macros )* + quote! { + #( #query_genesis_config_part_macros )* - #types + #types - use #scrate::__private::serde as __genesis_config_serde_import__; - #[derive(#scrate::__private::serde::Serialize, #scrate::__private::serde::Deserialize, Default)] - #[serde(rename_all = "camelCase")] - #[serde(deny_unknown_fields)] - #[serde(crate = "__genesis_config_serde_import__")] - pub struct RuntimeGenesisConfig { - #fields - } + use #scrate::__private::serde as __genesis_config_serde_import__; + #[derive(#scrate::__private::serde::Serialize, #scrate::__private::serde::Deserialize, Default)] + #[serde(rename_all = "camelCase")] + #[serde(deny_unknown_fields)] + #[serde(crate = "__genesis_config_serde_import__")] + pub struct RuntimeGenesisConfig { + #fields + } - #[cfg(any(feature = "std", test))] - impl #scrate::sp_runtime::BuildStorage for RuntimeGenesisConfig { - fn assimilate_storage( - &self, - storage: &mut #scrate::sp_runtime::Storage, - ) -> std::result::Result<(), String> { - #scrate::__private::BasicExternalities::execute_with_storage(storage, || { - ::build(&self); - Ok(()) - }) - } - } + #[cfg(any(feature = "std", test))] + impl #scrate::sp_runtime::BuildStorage for RuntimeGenesisConfig { + fn assimilate_storage( + &self, + storage: &mut #scrate::sp_runtime::Storage, + ) -> std::result::Result<(), String> { + #scrate::__private::BasicExternalities::execute_with_storage(storage, || { + ::build(&self); + Ok(()) + }) + } + } - impl #scrate::traits::BuildGenesisConfig for RuntimeGenesisConfig { - fn build(&self) { - #genesis_build_calls - ::on_genesis(); - } - } + impl #scrate::traits::BuildGenesisConfig for RuntimeGenesisConfig { + fn build(&self) { + #genesis_build_calls + ::on_genesis(); + } + } - /// Test the `Default` derive impl of the `RuntimeGenesisConfig`. - #[cfg(test)] - #[test] - fn test_genesis_config_builds() { - #scrate::__private::sp_io::TestExternalities::default().execute_with(|| { - ::build( - &RuntimeGenesisConfig::default() - ); - }); - } - } + /// Test the `Default` derive impl of the `RuntimeGenesisConfig`. + #[cfg(test)] + #[test] + fn test_genesis_config_builds() { + #scrate::__private::sp_io::TestExternalities::default().execute_with(|| { + ::build( + &RuntimeGenesisConfig::default() + ); + }); + } + } } fn expand_config_types( - attr: &TokenStream, - runtime: &Ident, - decl: &Pallet, - config: &Ident, - part_is_generic: bool, + attr: &TokenStream, + runtime: &Ident, + decl: &Pallet, + config: &Ident, + part_is_generic: bool, ) -> TokenStream { - let path = &decl.path; + let path = &decl.path; - match (decl.instance.as_ref(), part_is_generic) { - (Some(inst), true) => quote! { - #attr - pub type #config = #path::GenesisConfig<#runtime, #path::#inst>; - }, - (None, true) => quote! { - #attr - pub type #config = #path::GenesisConfig<#runtime>; - }, - (_, false) => quote! { - #attr - pub type #config = #path::GenesisConfig; - }, - } + match (decl.instance.as_ref(), part_is_generic) { + (Some(inst), true) => quote! { + #attr + pub type #config = #path::GenesisConfig<#runtime, #path::#inst>; + }, + (None, true) => quote! { + #attr + pub type #config = #path::GenesisConfig<#runtime>; + }, + (_, false) => quote! { + #attr + pub type #config = #path::GenesisConfig; + }, + } } fn expand_config_build_storage_call( - scrate: &TokenStream, - pallet_genesis_config: &Ident, - attr: &TokenStream, - field_name: &Ident, + scrate: &TokenStream, + pallet_genesis_config: &Ident, + attr: &TokenStream, + field_name: &Ident, ) -> TokenStream { - quote! { - #attr - <#pallet_genesis_config as #scrate::traits::BuildGenesisConfig>::build(&self.#field_name); - } + quote! { + #attr + <#pallet_genesis_config as #scrate::traits::BuildGenesisConfig>::build(&self.#field_name); + } } diff --git a/support/procedural-fork/src/construct_runtime/expand/freeze_reason.rs b/support/procedural-fork/src/construct_runtime/expand/freeze_reason.rs index f00269085..f12f99526 100644 --- a/support/procedural-fork/src/construct_runtime/expand/freeze_reason.rs +++ b/support/procedural-fork/src/construct_runtime/expand/freeze_reason.rs @@ -21,55 +21,55 @@ use proc_macro2::TokenStream; use quote::quote; pub fn expand_outer_freeze_reason(pallet_decls: &[Pallet], scrate: &TokenStream) -> TokenStream { - let mut conversion_fns = Vec::new(); - let mut freeze_reason_variants = Vec::new(); - let mut freeze_reason_variants_count = Vec::new(); - for decl in pallet_decls { - if decl.find_part("FreezeReason").is_some() { - let variant_name = &decl.name; - let path = &decl.path; - let index = decl.index; - let instance = decl.instance.as_ref(); + let mut conversion_fns = Vec::new(); + let mut freeze_reason_variants = Vec::new(); + let mut freeze_reason_variants_count = Vec::new(); + for decl in pallet_decls { + if let Some(_) = decl.find_part("FreezeReason") { + let variant_name = &decl.name; + let path = &decl.path; + let index = decl.index; + let instance = decl.instance.as_ref(); - conversion_fns.push(composite_helper::expand_conversion_fn( - "FreezeReason", - path, - instance, - variant_name, - )); + conversion_fns.push(composite_helper::expand_conversion_fn( + "FreezeReason", + path, + instance, + variant_name, + )); - freeze_reason_variants.push(composite_helper::expand_variant( - "FreezeReason", - index, - path, - instance, - variant_name, - )); + freeze_reason_variants.push(composite_helper::expand_variant( + "FreezeReason", + index, + path, + instance, + variant_name, + )); - freeze_reason_variants_count.push(composite_helper::expand_variant_count( - "FreezeReason", - path, - instance, - )); - } - } + freeze_reason_variants_count.push(composite_helper::expand_variant_count( + "FreezeReason", + path, + instance, + )); + } + } - quote! { - /// A reason for placing a freeze on funds. - #[derive( - Copy, Clone, Eq, PartialEq, - #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, - #scrate::__private::scale_info::TypeInfo, - #scrate::__private::RuntimeDebug, - )] - pub enum RuntimeFreezeReason { - #( #freeze_reason_variants )* - } + quote! { + /// A reason for placing a freeze on funds. + #[derive( + Copy, Clone, Eq, PartialEq, + #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, + #scrate::__private::scale_info::TypeInfo, + #scrate::__private::RuntimeDebug, + )] + pub enum RuntimeFreezeReason { + #( #freeze_reason_variants )* + } - impl #scrate::traits::VariantCount for RuntimeFreezeReason { - const VARIANT_COUNT: u32 = 0 #( + #freeze_reason_variants_count )*; - } + impl #scrate::traits::VariantCount for RuntimeFreezeReason { + const VARIANT_COUNT: u32 = 0 #( + #freeze_reason_variants_count )*; + } - #( #conversion_fns )* - } + #( #conversion_fns )* + } } diff --git a/support/procedural-fork/src/construct_runtime/expand/hold_reason.rs b/support/procedural-fork/src/construct_runtime/expand/hold_reason.rs index 5fc2ed1ee..cdab92712 100644 --- a/support/procedural-fork/src/construct_runtime/expand/hold_reason.rs +++ b/support/procedural-fork/src/construct_runtime/expand/hold_reason.rs @@ -21,55 +21,55 @@ use proc_macro2::TokenStream; use quote::quote; pub fn expand_outer_hold_reason(pallet_decls: &[Pallet], scrate: &TokenStream) -> TokenStream { - let mut conversion_fns = Vec::new(); - let mut hold_reason_variants = Vec::new(); - let mut hold_reason_variants_count = Vec::new(); - for decl in pallet_decls { - if decl.find_part("HoldReason").is_some() { - let variant_name = &decl.name; - let path = &decl.path; - let index = decl.index; - let instance = decl.instance.as_ref(); + let mut conversion_fns = Vec::new(); + let mut hold_reason_variants = Vec::new(); + let mut hold_reason_variants_count = Vec::new(); + for decl in pallet_decls { + if let Some(_) = decl.find_part("HoldReason") { + let variant_name = &decl.name; + let path = &decl.path; + let index = decl.index; + let instance = decl.instance.as_ref(); - conversion_fns.push(composite_helper::expand_conversion_fn( - "HoldReason", - path, - instance, - variant_name, - )); + conversion_fns.push(composite_helper::expand_conversion_fn( + "HoldReason", + path, + instance, + variant_name, + )); - hold_reason_variants.push(composite_helper::expand_variant( - "HoldReason", - index, - path, - instance, - variant_name, - )); + hold_reason_variants.push(composite_helper::expand_variant( + "HoldReason", + index, + path, + instance, + variant_name, + )); - hold_reason_variants_count.push(composite_helper::expand_variant_count( - "HoldReason", - path, - instance, - )); - } - } + hold_reason_variants_count.push(composite_helper::expand_variant_count( + "HoldReason", + path, + instance, + )); + } + } - quote! { - /// A reason for placing a hold on funds. - #[derive( - Copy, Clone, Eq, PartialEq, - #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, - #scrate::__private::scale_info::TypeInfo, - #scrate::__private::RuntimeDebug, - )] - pub enum RuntimeHoldReason { - #( #hold_reason_variants )* - } + quote! { + /// A reason for placing a hold on funds. + #[derive( + Copy, Clone, Eq, PartialEq, + #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, + #scrate::__private::scale_info::TypeInfo, + #scrate::__private::RuntimeDebug, + )] + pub enum RuntimeHoldReason { + #( #hold_reason_variants )* + } - impl #scrate::traits::VariantCount for RuntimeHoldReason { - const VARIANT_COUNT: u32 = 0 #( + #hold_reason_variants_count )*; - } + impl #scrate::traits::VariantCount for RuntimeHoldReason { + const VARIANT_COUNT: u32 = 0 #( + #hold_reason_variants_count )*; + } - #( #conversion_fns )* - } + #( #conversion_fns )* + } } diff --git a/support/procedural-fork/src/construct_runtime/expand/inherent.rs b/support/procedural-fork/src/construct_runtime/expand/inherent.rs index b58d540fe..da483fa6c 100644 --- a/support/procedural-fork/src/construct_runtime/expand/inherent.rs +++ b/support/procedural-fork/src/construct_runtime/expand/inherent.rs @@ -22,236 +22,233 @@ use std::str::FromStr; use syn::Ident; pub fn expand_outer_inherent( - runtime: &Ident, - block: &TokenStream, - unchecked_extrinsic: &TokenStream, - pallet_decls: &[Pallet], - scrate: &TokenStream, + runtime: &Ident, + block: &TokenStream, + unchecked_extrinsic: &TokenStream, + pallet_decls: &[Pallet], + scrate: &TokenStream, ) -> TokenStream { - let mut pallet_names = Vec::new(); - let mut pallet_attrs = Vec::new(); - let mut query_inherent_part_macros = Vec::new(); - - for pallet_decl in pallet_decls { - if pallet_decl.exists_part("Inherent") { - let name = &pallet_decl.name; - let path = &pallet_decl.path; - let attr = pallet_decl - .cfg_pattern - .iter() - .fold(TokenStream::new(), |acc, pattern| { - let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) - .expect("was successfully parsed before; qed"); - quote! { - #acc - #attr - } - }); - - pallet_names.push(name); - pallet_attrs.push(attr); - query_inherent_part_macros.push(quote! { - #path::__substrate_inherent_check::is_inherent_part_defined!(#name); - }); - } - } - - quote! { - #( #query_inherent_part_macros )* - - trait InherentDataExt { - fn create_extrinsics(&self) -> - #scrate::__private::sp_std::vec::Vec<<#block as #scrate::sp_runtime::traits::Block>::Extrinsic>; - fn check_extrinsics(&self, block: &#block) -> #scrate::inherent::CheckInherentsResult; - } - - impl InherentDataExt for #scrate::inherent::InherentData { - fn create_extrinsics(&self) -> - #scrate::__private::sp_std::vec::Vec<<#block as #scrate::sp_runtime::traits::Block>::Extrinsic> - { - use #scrate::inherent::ProvideInherent; - - let mut inherents = #scrate::__private::sp_std::vec::Vec::new(); - - #( - #pallet_attrs - if let Some(inherent) = #pallet_names::create_inherent(self) { - let inherent = <#unchecked_extrinsic as #scrate::sp_runtime::traits::Extrinsic>::new( - inherent.into(), - None, - ).expect("Runtime UncheckedExtrinsic is not Opaque, so it has to return \ - `Some`; qed"); - - inherents.push(inherent); - } - )* - - inherents - } - - fn check_extrinsics(&self, block: &#block) -> #scrate::inherent::CheckInherentsResult { - use #scrate::inherent::{ProvideInherent, IsFatalError}; - use #scrate::traits::{IsSubType, ExtrinsicCall}; - use #scrate::sp_runtime::traits::Block as _; - use #scrate::__private::{sp_inherents::Error, log}; - - let mut result = #scrate::inherent::CheckInherentsResult::new(); - - // This handle assume we abort on the first fatal error. - fn handle_put_error_result(res: Result<(), Error>) { - const LOG_TARGET: &str = "runtime::inherent"; - match res { - Ok(()) => (), - Err(Error::InherentDataExists(id)) => - log::debug!( - target: LOG_TARGET, - "Some error already reported for inherent {:?}, new non fatal \ - error is ignored", - id - ), - Err(Error::FatalErrorReported) => - log::error!( - target: LOG_TARGET, - "Fatal error already reported, unexpected considering there is \ - only one fatal error", - ), - Err(_) => - log::error!( - target: LOG_TARGET, - "Unexpected error from `put_error` operation", - ), - } - } - - for xt in block.extrinsics() { - // Inherents are before any other extrinsics. - // And signed extrinsics are not inherents. - if #scrate::sp_runtime::traits::Extrinsic::is_signed(xt).unwrap_or(false) { - break - } - - let mut is_inherent = false; - - #( - #pallet_attrs - { - let call = <#unchecked_extrinsic as ExtrinsicCall>::call(xt); - if let Some(call) = IsSubType::<_>::is_sub_type(call) { - if #pallet_names::is_inherent(call) { - is_inherent = true; - if let Err(e) = #pallet_names::check_inherent(call, self) { - handle_put_error_result(result.put_error( - #pallet_names::INHERENT_IDENTIFIER, &e - )); - if e.is_fatal_error() { - return result; - } - } - } - } - } - )* - - // Inherents are before any other extrinsics. - // No module marked it as inherent thus it is not. - if !is_inherent { - break - } - } - - #( - #pallet_attrs - match #pallet_names::is_inherent_required(self) { - Ok(Some(e)) => { - let found = block.extrinsics().iter().any(|xt| { - let is_signed = #scrate::sp_runtime::traits::Extrinsic::is_signed(xt) - .unwrap_or(false); - - if !is_signed { - let call = < - #unchecked_extrinsic as ExtrinsicCall - >::call(xt); - if let Some(call) = IsSubType::<_>::is_sub_type(call) { - #pallet_names::is_inherent(&call) - } else { - false - } - } else { - // Signed extrinsics are not inherents. - false - } - }); - - if !found { - handle_put_error_result(result.put_error( - #pallet_names::INHERENT_IDENTIFIER, &e - )); - if e.is_fatal_error() { - return result; - } - } - }, - Ok(None) => (), - Err(e) => { - handle_put_error_result(result.put_error( - #pallet_names::INHERENT_IDENTIFIER, &e - )); - if e.is_fatal_error() { - return result; - } - }, - } - )* - - result - } - } - - impl #scrate::traits::IsInherent<<#block as #scrate::sp_runtime::traits::Block>::Extrinsic> for #runtime { - fn is_inherent(ext: &<#block as #scrate::sp_runtime::traits::Block>::Extrinsic) -> bool { - use #scrate::inherent::ProvideInherent; - use #scrate::traits::{IsSubType, ExtrinsicCall}; - - if #scrate::sp_runtime::traits::Extrinsic::is_signed(ext).unwrap_or(false) { - // Signed extrinsics are never inherents. - return false - } - - #( - #pallet_attrs - { - let call = <#unchecked_extrinsic as ExtrinsicCall>::call(ext); - if let Some(call) = IsSubType::<_>::is_sub_type(call) { - if <#pallet_names as ProvideInherent>::is_inherent(&call) { - return true; - } - } - } - )* - false - } - } - - impl #scrate::traits::EnsureInherentsAreFirst<#block> for #runtime { - fn ensure_inherents_are_first(block: &#block) -> Result { - use #scrate::inherent::ProvideInherent; - use #scrate::traits::{IsSubType, ExtrinsicCall}; - use #scrate::sp_runtime::traits::Block as _; - - let mut num_inherents = 0u32; - - for (i, xt) in block.extrinsics().iter().enumerate() { - if >::is_inherent(xt) { - if num_inherents != i as u32 { - return Err(i as u32); - } - - num_inherents += 1; // Safe since we are in an `enumerate` loop. - } - } - - Ok(num_inherents) - } - } - } + let mut pallet_names = Vec::new(); + let mut pallet_attrs = Vec::new(); + let mut query_inherent_part_macros = Vec::new(); + + for pallet_decl in pallet_decls { + if pallet_decl.exists_part("Inherent") { + let name = &pallet_decl.name; + let path = &pallet_decl.path; + let attr = pallet_decl.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); + + pallet_names.push(name); + pallet_attrs.push(attr); + query_inherent_part_macros.push(quote! { + #path::__substrate_inherent_check::is_inherent_part_defined!(#name); + }); + } + } + + quote! { + #( #query_inherent_part_macros )* + + trait InherentDataExt { + fn create_extrinsics(&self) -> + #scrate::__private::sp_std::vec::Vec<<#block as #scrate::sp_runtime::traits::Block>::Extrinsic>; + fn check_extrinsics(&self, block: &#block) -> #scrate::inherent::CheckInherentsResult; + } + + impl InherentDataExt for #scrate::inherent::InherentData { + fn create_extrinsics(&self) -> + #scrate::__private::sp_std::vec::Vec<<#block as #scrate::sp_runtime::traits::Block>::Extrinsic> + { + use #scrate::inherent::ProvideInherent; + + let mut inherents = #scrate::__private::sp_std::vec::Vec::new(); + + #( + #pallet_attrs + if let Some(inherent) = #pallet_names::create_inherent(self) { + let inherent = <#unchecked_extrinsic as #scrate::sp_runtime::traits::Extrinsic>::new( + inherent.into(), + None, + ).expect("Runtime UncheckedExtrinsic is not Opaque, so it has to return \ + `Some`; qed"); + + inherents.push(inherent); + } + )* + + inherents + } + + fn check_extrinsics(&self, block: &#block) -> #scrate::inherent::CheckInherentsResult { + use #scrate::inherent::{ProvideInherent, IsFatalError}; + use #scrate::traits::{IsSubType, ExtrinsicCall}; + use #scrate::sp_runtime::traits::Block as _; + use #scrate::__private::{sp_inherents::Error, log}; + + let mut result = #scrate::inherent::CheckInherentsResult::new(); + + // This handle assume we abort on the first fatal error. + fn handle_put_error_result(res: Result<(), Error>) { + const LOG_TARGET: &str = "runtime::inherent"; + match res { + Ok(()) => (), + Err(Error::InherentDataExists(id)) => + log::debug!( + target: LOG_TARGET, + "Some error already reported for inherent {:?}, new non fatal \ + error is ignored", + id + ), + Err(Error::FatalErrorReported) => + log::error!( + target: LOG_TARGET, + "Fatal error already reported, unexpected considering there is \ + only one fatal error", + ), + Err(_) => + log::error!( + target: LOG_TARGET, + "Unexpected error from `put_error` operation", + ), + } + } + + for xt in block.extrinsics() { + // Inherents are before any other extrinsics. + // And signed extrinsics are not inherents. + if #scrate::sp_runtime::traits::Extrinsic::is_signed(xt).unwrap_or(false) { + break + } + + let mut is_inherent = false; + + #( + #pallet_attrs + { + let call = <#unchecked_extrinsic as ExtrinsicCall>::call(xt); + if let Some(call) = IsSubType::<_>::is_sub_type(call) { + if #pallet_names::is_inherent(call) { + is_inherent = true; + if let Err(e) = #pallet_names::check_inherent(call, self) { + handle_put_error_result(result.put_error( + #pallet_names::INHERENT_IDENTIFIER, &e + )); + if e.is_fatal_error() { + return result; + } + } + } + } + } + )* + + // Inherents are before any other extrinsics. + // No module marked it as inherent thus it is not. + if !is_inherent { + break + } + } + + #( + #pallet_attrs + match #pallet_names::is_inherent_required(self) { + Ok(Some(e)) => { + let found = block.extrinsics().iter().any(|xt| { + let is_signed = #scrate::sp_runtime::traits::Extrinsic::is_signed(xt) + .unwrap_or(false); + + if !is_signed { + let call = < + #unchecked_extrinsic as ExtrinsicCall + >::call(xt); + if let Some(call) = IsSubType::<_>::is_sub_type(call) { + #pallet_names::is_inherent(&call) + } else { + false + } + } else { + // Signed extrinsics are not inherents. + false + } + }); + + if !found { + handle_put_error_result(result.put_error( + #pallet_names::INHERENT_IDENTIFIER, &e + )); + if e.is_fatal_error() { + return result; + } + } + }, + Ok(None) => (), + Err(e) => { + handle_put_error_result(result.put_error( + #pallet_names::INHERENT_IDENTIFIER, &e + )); + if e.is_fatal_error() { + return result; + } + }, + } + )* + + result + } + } + + impl #scrate::traits::IsInherent<<#block as #scrate::sp_runtime::traits::Block>::Extrinsic> for #runtime { + fn is_inherent(ext: &<#block as #scrate::sp_runtime::traits::Block>::Extrinsic) -> bool { + use #scrate::inherent::ProvideInherent; + use #scrate::traits::{IsSubType, ExtrinsicCall}; + + if #scrate::sp_runtime::traits::Extrinsic::is_signed(ext).unwrap_or(false) { + // Signed extrinsics are never inherents. + return false + } + + #( + #pallet_attrs + { + let call = <#unchecked_extrinsic as ExtrinsicCall>::call(ext); + if let Some(call) = IsSubType::<_>::is_sub_type(call) { + if <#pallet_names as ProvideInherent>::is_inherent(&call) { + return true; + } + } + } + )* + false + } + } + + impl #scrate::traits::EnsureInherentsAreFirst<#block> for #runtime { + fn ensure_inherents_are_first(block: &#block) -> Result { + use #scrate::inherent::ProvideInherent; + use #scrate::traits::{IsSubType, ExtrinsicCall}; + use #scrate::sp_runtime::traits::Block as _; + + let mut num_inherents = 0u32; + + for (i, xt) in block.extrinsics().iter().enumerate() { + if >::is_inherent(xt) { + if num_inherents != i as u32 { + return Err(i as u32); + } + + num_inherents += 1; // Safe since we are in an `enumerate` loop. + } + } + + Ok(num_inherents) + } + } + } } diff --git a/support/procedural-fork/src/construct_runtime/expand/lock_id.rs b/support/procedural-fork/src/construct_runtime/expand/lock_id.rs index 732fb7ac4..e67c0da00 100644 --- a/support/procedural-fork/src/construct_runtime/expand/lock_id.rs +++ b/support/procedural-fork/src/construct_runtime/expand/lock_id.rs @@ -21,44 +21,44 @@ use proc_macro2::TokenStream; use quote::quote; pub fn expand_outer_lock_id(pallet_decls: &[Pallet], scrate: &TokenStream) -> TokenStream { - let mut conversion_fns = Vec::new(); - let mut lock_id_variants = Vec::new(); - for decl in pallet_decls { - if decl.find_part("LockId").is_some() { - let variant_name = &decl.name; - let path = &decl.path; - let index = decl.index; - let instance = decl.instance.as_ref(); + let mut conversion_fns = Vec::new(); + let mut lock_id_variants = Vec::new(); + for decl in pallet_decls { + if let Some(_) = decl.find_part("LockId") { + let variant_name = &decl.name; + let path = &decl.path; + let index = decl.index; + let instance = decl.instance.as_ref(); - conversion_fns.push(composite_helper::expand_conversion_fn( - "LockId", - path, - instance, - variant_name, - )); + conversion_fns.push(composite_helper::expand_conversion_fn( + "LockId", + path, + instance, + variant_name, + )); - lock_id_variants.push(composite_helper::expand_variant( - "LockId", - index, - path, - instance, - variant_name, - )); - } - } + lock_id_variants.push(composite_helper::expand_variant( + "LockId", + index, + path, + instance, + variant_name, + )); + } + } - quote! { - /// An identifier for each lock placed on funds. - #[derive( - Copy, Clone, Eq, PartialEq, - #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, - #scrate::__private::scale_info::TypeInfo, - #scrate::__private::RuntimeDebug, - )] - pub enum RuntimeLockId { - #( #lock_id_variants )* - } + quote! { + /// An identifier for each lock placed on funds. + #[derive( + Copy, Clone, Eq, PartialEq, + #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, + #scrate::__private::scale_info::TypeInfo, + #scrate::__private::RuntimeDebug, + )] + pub enum RuntimeLockId { + #( #lock_id_variants )* + } - #( #conversion_fns )* - } + #( #conversion_fns )* + } } diff --git a/support/procedural-fork/src/construct_runtime/expand/metadata.rs b/support/procedural-fork/src/construct_runtime/expand/metadata.rs index f98c719ca..0e76f9a92 100644 --- a/support/procedural-fork/src/construct_runtime/expand/metadata.rs +++ b/support/procedural-fork/src/construct_runtime/expand/metadata.rs @@ -22,240 +22,237 @@ use std::str::FromStr; use syn::Ident; pub fn expand_runtime_metadata( - runtime: &Ident, - pallet_declarations: &[Pallet], - scrate: &TokenStream, - extrinsic: &TokenStream, - system_path: &PalletPath, + runtime: &Ident, + pallet_declarations: &[Pallet], + scrate: &TokenStream, + extrinsic: &TokenStream, + system_path: &PalletPath, ) -> TokenStream { - let pallets = pallet_declarations - .iter() - .filter_map(|pallet_declaration| { - pallet_declaration.find_part("Pallet").map(|_| { - let filtered_names: Vec<_> = pallet_declaration - .pallet_parts() - .iter() - .filter(|part| part.name() != "Pallet") - .map(|part| part.name()) - .collect(); - (pallet_declaration, filtered_names) - }) - }) - .map(|(decl, filtered_names)| { - let name = &decl.name; - let index = &decl.index; - let storage = expand_pallet_metadata_storage(&filtered_names, runtime, decl); - let calls = expand_pallet_metadata_calls(&filtered_names, runtime, decl); - let event = expand_pallet_metadata_events(&filtered_names, runtime, scrate, decl); - let constants = expand_pallet_metadata_constants(runtime, decl); - let errors = expand_pallet_metadata_errors(runtime, decl); - let docs = expand_pallet_metadata_docs(runtime, decl); - let attr = decl - .cfg_pattern - .iter() - .fold(TokenStream::new(), |acc, pattern| { - let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) - .expect("was successfully parsed before; qed"); - quote! { - #acc - #attr - } - }); + let pallets = pallet_declarations + .iter() + .filter_map(|pallet_declaration| { + pallet_declaration.find_part("Pallet").map(|_| { + let filtered_names: Vec<_> = pallet_declaration + .pallet_parts() + .iter() + .filter(|part| part.name() != "Pallet") + .map(|part| part.name()) + .collect(); + (pallet_declaration, filtered_names) + }) + }) + .map(|(decl, filtered_names)| { + let name = &decl.name; + let index = &decl.index; + let storage = expand_pallet_metadata_storage(&filtered_names, runtime, decl); + let calls = expand_pallet_metadata_calls(&filtered_names, runtime, decl); + let event = expand_pallet_metadata_events(&filtered_names, runtime, scrate, decl); + let constants = expand_pallet_metadata_constants(runtime, decl); + let errors = expand_pallet_metadata_errors(runtime, decl); + let docs = expand_pallet_metadata_docs(runtime, decl); + let attr = decl.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); - quote! { - #attr - #scrate::__private::metadata_ir::PalletMetadataIR { - name: stringify!(#name), - index: #index, - storage: #storage, - calls: #calls, - event: #event, - constants: #constants, - error: #errors, - docs: #docs, - } - } - }) - .collect::>(); + quote! { + #attr + #scrate::__private::metadata_ir::PalletMetadataIR { + name: stringify!(#name), + index: #index, + storage: #storage, + calls: #calls, + event: #event, + constants: #constants, + error: #errors, + docs: #docs, + } + } + }) + .collect::>(); - quote! { - impl #runtime { - fn metadata_ir() -> #scrate::__private::metadata_ir::MetadataIR { - // Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata. - // The function is implemented by calling `impl_runtime_apis!`. - // - // However, the `construct_runtime!` may be called without calling `impl_runtime_apis!`. - // Rely on the `Deref` trait to differentiate between a runtime that implements - // APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro construct_runtime!). - // - // Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function. - // `InternalConstructRuntime` is implemented by the `construct_runtime!` for Runtime references (`& Runtime`), - // while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`). - // - // Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!` - // when both macros are called; and will resolve an empty `runtime_metadata` when only the `construct_runtime!` - // is called. - // - // `Deref` needs a reference for resolving the function call. - let rt = #runtime; + quote! { + impl #runtime { + fn metadata_ir() -> #scrate::__private::metadata_ir::MetadataIR { + // Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata. + // The function is implemented by calling `impl_runtime_apis!`. + // + // However, the `construct_runtime!` may be called without calling `impl_runtime_apis!`. + // Rely on the `Deref` trait to differentiate between a runtime that implements + // APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro construct_runtime!). + // + // Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function. + // `InternalConstructRuntime` is implemented by the `construct_runtime!` for Runtime references (`& Runtime`), + // while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`). + // + // Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!` + // when both macros are called; and will resolve an empty `runtime_metadata` when only the `construct_runtime!` + // is called. + // + // `Deref` needs a reference for resolving the function call. + let rt = #runtime; - let ty = #scrate::__private::scale_info::meta_type::<#extrinsic>(); - let address_ty = #scrate::__private::scale_info::meta_type::< - <<#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::SignaturePayload as #scrate::sp_runtime::traits::SignaturePayload>::SignatureAddress - >(); - let call_ty = #scrate::__private::scale_info::meta_type::< - <#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::Call - >(); - let signature_ty = #scrate::__private::scale_info::meta_type::< - <<#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::SignaturePayload as #scrate::sp_runtime::traits::SignaturePayload>::Signature - >(); - let extra_ty = #scrate::__private::scale_info::meta_type::< - <<#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::SignaturePayload as #scrate::sp_runtime::traits::SignaturePayload>::SignatureExtra - >(); + let ty = #scrate::__private::scale_info::meta_type::<#extrinsic>(); + let address_ty = #scrate::__private::scale_info::meta_type::< + <<#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::SignaturePayload as #scrate::sp_runtime::traits::SignaturePayload>::SignatureAddress + >(); + let call_ty = #scrate::__private::scale_info::meta_type::< + <#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::Call + >(); + let signature_ty = #scrate::__private::scale_info::meta_type::< + <<#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::SignaturePayload as #scrate::sp_runtime::traits::SignaturePayload>::Signature + >(); + let extra_ty = #scrate::__private::scale_info::meta_type::< + <<#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::SignaturePayload as #scrate::sp_runtime::traits::SignaturePayload>::SignatureExtra + >(); - #scrate::__private::metadata_ir::MetadataIR { - pallets: #scrate::__private::sp_std::vec![ #(#pallets),* ], - extrinsic: #scrate::__private::metadata_ir::ExtrinsicMetadataIR { - ty, - version: <#extrinsic as #scrate::sp_runtime::traits::ExtrinsicMetadata>::VERSION, - address_ty, - call_ty, - signature_ty, - extra_ty, - signed_extensions: < - < - #extrinsic as #scrate::sp_runtime::traits::ExtrinsicMetadata - >::SignedExtensions as #scrate::sp_runtime::traits::SignedExtension - >::metadata() - .into_iter() - .map(|meta| #scrate::__private::metadata_ir::SignedExtensionMetadataIR { - identifier: meta.identifier, - ty: meta.ty, - additional_signed: meta.additional_signed, - }) - .collect(), - }, - ty: #scrate::__private::scale_info::meta_type::<#runtime>(), - apis: (&rt).runtime_metadata(), - outer_enums: #scrate::__private::metadata_ir::OuterEnumsIR { - call_enum_ty: #scrate::__private::scale_info::meta_type::< - <#runtime as #system_path::Config>::RuntimeCall - >(), - event_enum_ty: #scrate::__private::scale_info::meta_type::(), - error_enum_ty: #scrate::__private::scale_info::meta_type::(), - } - } - } + #scrate::__private::metadata_ir::MetadataIR { + pallets: #scrate::__private::sp_std::vec![ #(#pallets),* ], + extrinsic: #scrate::__private::metadata_ir::ExtrinsicMetadataIR { + ty, + version: <#extrinsic as #scrate::sp_runtime::traits::ExtrinsicMetadata>::VERSION, + address_ty, + call_ty, + signature_ty, + extra_ty, + signed_extensions: < + < + #extrinsic as #scrate::sp_runtime::traits::ExtrinsicMetadata + >::SignedExtensions as #scrate::sp_runtime::traits::SignedExtension + >::metadata() + .into_iter() + .map(|meta| #scrate::__private::metadata_ir::SignedExtensionMetadataIR { + identifier: meta.identifier, + ty: meta.ty, + additional_signed: meta.additional_signed, + }) + .collect(), + }, + ty: #scrate::__private::scale_info::meta_type::<#runtime>(), + apis: (&rt).runtime_metadata(), + outer_enums: #scrate::__private::metadata_ir::OuterEnumsIR { + call_enum_ty: #scrate::__private::scale_info::meta_type::< + <#runtime as #system_path::Config>::RuntimeCall + >(), + event_enum_ty: #scrate::__private::scale_info::meta_type::(), + error_enum_ty: #scrate::__private::scale_info::meta_type::(), + } + } + } - pub fn metadata() -> #scrate::__private::metadata::RuntimeMetadataPrefixed { - // Note: this always returns the V14 version. The runtime API function - // must be deprecated. - #scrate::__private::metadata_ir::into_v14(#runtime::metadata_ir()) - } + pub fn metadata() -> #scrate::__private::metadata::RuntimeMetadataPrefixed { + // Note: this always returns the V14 version. The runtime API function + // must be deprecated. + #scrate::__private::metadata_ir::into_v14(#runtime::metadata_ir()) + } - pub fn metadata_at_version(version: u32) -> Option<#scrate::__private::OpaqueMetadata> { - #scrate::__private::metadata_ir::into_version(#runtime::metadata_ir(), version).map(|prefixed| { - #scrate::__private::OpaqueMetadata::new(prefixed.into()) - }) - } + pub fn metadata_at_version(version: u32) -> Option<#scrate::__private::OpaqueMetadata> { + #scrate::__private::metadata_ir::into_version(#runtime::metadata_ir(), version).map(|prefixed| { + #scrate::__private::OpaqueMetadata::new(prefixed.into()) + }) + } - pub fn metadata_versions() -> #scrate::__private::sp_std::vec::Vec { - #scrate::__private::metadata_ir::supported_versions() - } - } - } + pub fn metadata_versions() -> #scrate::__private::sp_std::vec::Vec { + #scrate::__private::metadata_ir::supported_versions() + } + } + } } fn expand_pallet_metadata_storage( - filtered_names: &[&'static str], - runtime: &Ident, - decl: &Pallet, + filtered_names: &[&'static str], + runtime: &Ident, + decl: &Pallet, ) -> TokenStream { - if filtered_names.contains(&"Storage") { - let instance = decl.instance.as_ref().into_iter(); - let path = &decl.path; + if filtered_names.contains(&"Storage") { + let instance = decl.instance.as_ref().into_iter(); + let path = &decl.path; - quote! { - Some(#path::Pallet::<#runtime #(, #path::#instance)*>::storage_metadata()) - } - } else { - quote!(None) - } + quote! { + Some(#path::Pallet::<#runtime #(, #path::#instance)*>::storage_metadata()) + } + } else { + quote!(None) + } } fn expand_pallet_metadata_calls( - filtered_names: &[&'static str], - runtime: &Ident, - decl: &Pallet, + filtered_names: &[&'static str], + runtime: &Ident, + decl: &Pallet, ) -> TokenStream { - if filtered_names.contains(&"Call") { - let instance = decl.instance.as_ref().into_iter(); - let path = &decl.path; + if filtered_names.contains(&"Call") { + let instance = decl.instance.as_ref().into_iter(); + let path = &decl.path; - quote! { - Some(#path::Pallet::<#runtime #(, #path::#instance)*>::call_functions()) - } - } else { - quote!(None) - } + quote! { + Some(#path::Pallet::<#runtime #(, #path::#instance)*>::call_functions()) + } + } else { + quote!(None) + } } fn expand_pallet_metadata_events( - filtered_names: &[&'static str], - runtime: &Ident, - scrate: &TokenStream, - decl: &Pallet, + filtered_names: &[&'static str], + runtime: &Ident, + scrate: &TokenStream, + decl: &Pallet, ) -> TokenStream { - if filtered_names.contains(&"Event") { - let path = &decl.path; - let part_is_generic = !decl - .find_part("Event") - .expect("Event part exists; qed") - .generics - .params - .is_empty(); - let pallet_event = match (decl.instance.as_ref(), part_is_generic) { - (Some(inst), true) => quote!(#path::Event::<#runtime, #path::#inst>), - (Some(inst), false) => quote!(#path::Event::<#path::#inst>), - (None, true) => quote!(#path::Event::<#runtime>), - (None, false) => quote!(#path::Event), - }; + if filtered_names.contains(&"Event") { + let path = &decl.path; + let part_is_generic = !decl + .find_part("Event") + .expect("Event part exists; qed") + .generics + .params + .is_empty(); + let pallet_event = match (decl.instance.as_ref(), part_is_generic) { + (Some(inst), true) => quote!(#path::Event::<#runtime, #path::#inst>), + (Some(inst), false) => quote!(#path::Event::<#path::#inst>), + (None, true) => quote!(#path::Event::<#runtime>), + (None, false) => quote!(#path::Event), + }; - quote! { - Some( - #scrate::__private::metadata_ir::PalletEventMetadataIR { - ty: #scrate::__private::scale_info::meta_type::<#pallet_event>() - } - ) - } - } else { - quote!(None) - } + quote! { + Some( + #scrate::__private::metadata_ir::PalletEventMetadataIR { + ty: #scrate::__private::scale_info::meta_type::<#pallet_event>() + } + ) + } + } else { + quote!(None) + } } fn expand_pallet_metadata_constants(runtime: &Ident, decl: &Pallet) -> TokenStream { - let path = &decl.path; - let instance = decl.instance.as_ref().into_iter(); + let path = &decl.path; + let instance = decl.instance.as_ref().into_iter(); - quote! { - #path::Pallet::<#runtime #(, #path::#instance)*>::pallet_constants_metadata() - } + quote! { + #path::Pallet::<#runtime #(, #path::#instance)*>::pallet_constants_metadata() + } } fn expand_pallet_metadata_errors(runtime: &Ident, decl: &Pallet) -> TokenStream { - let path = &decl.path; - let instance = decl.instance.as_ref().into_iter(); + let path = &decl.path; + let instance = decl.instance.as_ref().into_iter(); - quote! { - #path::Pallet::<#runtime #(, #path::#instance)*>::error_metadata() - } + quote! { + #path::Pallet::<#runtime #(, #path::#instance)*>::error_metadata() + } } fn expand_pallet_metadata_docs(runtime: &Ident, decl: &Pallet) -> TokenStream { - let path = &decl.path; - let instance = decl.instance.as_ref().into_iter(); + let path = &decl.path; + let instance = decl.instance.as_ref().into_iter(); - quote! { - #path::Pallet::<#runtime #(, #path::#instance)*>::pallet_documentation_metadata() - } + quote! { + #path::Pallet::<#runtime #(, #path::#instance)*>::pallet_documentation_metadata() + } } diff --git a/support/procedural-fork/src/construct_runtime/expand/origin.rs b/support/procedural-fork/src/construct_runtime/expand/origin.rs index 2d50777bf..83049919d 100644 --- a/support/procedural-fork/src/construct_runtime/expand/origin.rs +++ b/support/procedural-fork/src/construct_runtime/expand/origin.rs @@ -22,448 +22,434 @@ use std::str::FromStr; use syn::{Generics, Ident}; pub fn expand_outer_origin( - runtime: &Ident, - system_pallet: &Pallet, - pallets: &[Pallet], - scrate: &TokenStream, + runtime: &Ident, + system_pallet: &Pallet, + pallets: &[Pallet], + scrate: &TokenStream, ) -> syn::Result { - let mut caller_variants = TokenStream::new(); - let mut pallet_conversions = TokenStream::new(); - let mut query_origin_part_macros = Vec::new(); - - for pallet_decl in pallets - .iter() - .filter(|pallet| pallet.name != SYSTEM_PALLET_NAME) - { - if let Some(pallet_entry) = pallet_decl.find_part("Origin") { - let instance = pallet_decl.instance.as_ref(); - let index = pallet_decl.index; - let generics = &pallet_entry.generics; - let name = &pallet_decl.name; - let path = &pallet_decl.path; - - if instance.is_some() && generics.params.is_empty() { - let msg = format!( - "Instantiable pallet with no generic `Origin` cannot \ + let mut caller_variants = TokenStream::new(); + let mut pallet_conversions = TokenStream::new(); + let mut query_origin_part_macros = Vec::new(); + + for pallet_decl in pallets.iter().filter(|pallet| pallet.name != SYSTEM_PALLET_NAME) { + if let Some(pallet_entry) = pallet_decl.find_part("Origin") { + let instance = pallet_decl.instance.as_ref(); + let index = pallet_decl.index; + let generics = &pallet_entry.generics; + let name = &pallet_decl.name; + let path = &pallet_decl.path; + + if instance.is_some() && generics.params.is_empty() { + let msg = format!( + "Instantiable pallet with no generic `Origin` cannot \ be constructed: pallet `{}` must have generic `Origin`", - name - ); - return Err(syn::Error::new(name.span(), msg)); - } - - caller_variants.extend(expand_origin_caller_variant( - runtime, - pallet_decl, - index, - instance, - generics, - )); - pallet_conversions.extend(expand_origin_pallet_conversions( - scrate, - runtime, - pallet_decl, - instance, - generics, - )); - query_origin_part_macros.push(quote! { - #path::__substrate_origin_check::is_origin_part_defined!(#name); - }); - } - } - - let system_path = &system_pallet.path; - - let system_index = system_pallet.index; - - let system_path_name = system_path.module_name(); - - let doc_string = get_intra_doc_string( - "Origin is always created with the base filter configured in", - &system_path_name, - ); - - let doc_string_none_origin = - get_intra_doc_string("Create with system none origin and", &system_path_name); - - let doc_string_root_origin = - get_intra_doc_string("Create with system root origin and", &system_path_name); - - let doc_string_signed_origin = - get_intra_doc_string("Create with system signed origin and", &system_path_name); - - let doc_string_runtime_origin = get_intra_doc_string( - "Convert to runtime origin, using as filter:", - &system_path_name, - ); - - let doc_string_runtime_origin_with_caller = get_intra_doc_string( - "Convert to runtime origin with caller being system signed or none and use filter", - &system_path_name, - ); - - Ok(quote! { - #( #query_origin_part_macros )* - - /// The runtime origin type representing the origin of a call. - /// - #[doc = #doc_string] - #[derive(Clone)] - pub struct RuntimeOrigin { - pub caller: OriginCaller, - filter: #scrate::__private::sp_std::rc::Rc::RuntimeCall) -> bool>>, - } - - #[cfg(not(feature = "std"))] - impl #scrate::__private::sp_std::fmt::Debug for RuntimeOrigin { - fn fmt( - &self, - fmt: &mut #scrate::__private::sp_std::fmt::Formatter, - ) -> #scrate::__private::sp_std::result::Result<(), #scrate::__private::sp_std::fmt::Error> { - fmt.write_str("") - } - } - - #[cfg(feature = "std")] - impl #scrate::__private::sp_std::fmt::Debug for RuntimeOrigin { - fn fmt( - &self, - fmt: &mut #scrate::__private::sp_std::fmt::Formatter, - ) -> #scrate::__private::sp_std::result::Result<(), #scrate::__private::sp_std::fmt::Error> { - fmt.debug_struct("Origin") - .field("caller", &self.caller) - .field("filter", &"[function ptr]") - .finish() - } - } - - impl #scrate::traits::OriginTrait for RuntimeOrigin { - type Call = <#runtime as #system_path::Config>::RuntimeCall; - type PalletsOrigin = OriginCaller; - type AccountId = <#runtime as #system_path::Config>::AccountId; - - fn add_filter(&mut self, filter: impl Fn(&Self::Call) -> bool + 'static) { - let f = self.filter.clone(); - - self.filter = #scrate::__private::sp_std::rc::Rc::new(Box::new(move |call| { - f(call) && filter(call) - })); - } - - fn reset_filter(&mut self) { - let filter = < - <#runtime as #system_path::Config>::BaseCallFilter - as #scrate::traits::Contains<<#runtime as #system_path::Config>::RuntimeCall> - >::contains; - - self.filter = #scrate::__private::sp_std::rc::Rc::new(Box::new(filter)); - } - - fn set_caller_from(&mut self, other: impl Into) { - self.caller = other.into().caller; - } - - fn filter_call(&self, call: &Self::Call) -> bool { - match self.caller { - // Root bypasses all filters - OriginCaller::system(#system_path::Origin::<#runtime>::Root) => true, - _ => (self.filter)(call), - } - } - - fn caller(&self) -> &Self::PalletsOrigin { - &self.caller - } - - fn into_caller(self) -> Self::PalletsOrigin { - self.caller - } - - fn try_with_caller( - mut self, - f: impl FnOnce(Self::PalletsOrigin) -> Result, - ) -> Result { - match f(self.caller) { - Ok(r) => Ok(r), - Err(caller) => { self.caller = caller; Err(self) } - } - } - - fn none() -> Self { - #system_path::RawOrigin::None.into() - } - - fn root() -> Self { - #system_path::RawOrigin::Root.into() - } - - fn signed(by: Self::AccountId) -> Self { - #system_path::RawOrigin::Signed(by).into() - } - } - - #[derive( - Clone, PartialEq, Eq, #scrate::__private::RuntimeDebug, #scrate::__private::codec::Encode, - #scrate::__private::codec::Decode, #scrate::__private::scale_info::TypeInfo, #scrate::__private::codec::MaxEncodedLen, - )] - #[allow(non_camel_case_types)] - pub enum OriginCaller { - #[codec(index = #system_index)] - system(#system_path::Origin<#runtime>), - #caller_variants - #[allow(dead_code)] - Void(#scrate::__private::Void) - } - - // For backwards compatibility and ease of accessing these functions. - #[allow(dead_code)] - impl RuntimeOrigin { - #[doc = #doc_string_none_origin] - pub fn none() -> Self { - ::none() - } - - #[doc = #doc_string_root_origin] - pub fn root() -> Self { - ::root() - } - - #[doc = #doc_string_signed_origin] - pub fn signed(by: <#runtime as #system_path::Config>::AccountId) -> Self { - ::signed(by) - } - } - - impl From<#system_path::Origin<#runtime>> for OriginCaller { - fn from(x: #system_path::Origin<#runtime>) -> Self { - OriginCaller::system(x) - } - } - - impl #scrate::traits::CallerTrait<<#runtime as #system_path::Config>::AccountId> for OriginCaller { - fn into_system(self) -> Option<#system_path::RawOrigin<<#runtime as #system_path::Config>::AccountId>> { - match self { - OriginCaller::system(x) => Some(x), - _ => None, - } - } - fn as_system_ref(&self) -> Option<&#system_path::RawOrigin<<#runtime as #system_path::Config>::AccountId>> { - match &self { - OriginCaller::system(o) => Some(o), - _ => None, - } - } - } - - impl TryFrom for #system_path::Origin<#runtime> { - type Error = OriginCaller; - fn try_from(x: OriginCaller) - -> #scrate::__private::sp_std::result::Result<#system_path::Origin<#runtime>, OriginCaller> - { - if let OriginCaller::system(l) = x { - Ok(l) - } else { - Err(x) - } - } - } - - impl From<#system_path::Origin<#runtime>> for RuntimeOrigin { - - #[doc = #doc_string_runtime_origin] - fn from(x: #system_path::Origin<#runtime>) -> Self { - let o: OriginCaller = x.into(); - o.into() - } - } - - impl From for RuntimeOrigin { - fn from(x: OriginCaller) -> Self { - let mut o = RuntimeOrigin { - caller: x, - filter: #scrate::__private::sp_std::rc::Rc::new(Box::new(|_| true)), - }; - - #scrate::traits::OriginTrait::reset_filter(&mut o); - - o - } - } - - impl From for #scrate::__private::sp_std::result::Result<#system_path::Origin<#runtime>, RuntimeOrigin> { - /// NOTE: converting to pallet origin loses the origin filter information. - fn from(val: RuntimeOrigin) -> Self { - if let OriginCaller::system(l) = val.caller { - Ok(l) - } else { - Err(val) - } - } - } - impl From::AccountId>> for RuntimeOrigin { - #[doc = #doc_string_runtime_origin_with_caller] - fn from(x: Option<<#runtime as #system_path::Config>::AccountId>) -> Self { - <#system_path::Origin<#runtime>>::from(x).into() - } - } - - #pallet_conversions - }) + name + ); + return Err(syn::Error::new(name.span(), msg)) + } + + caller_variants.extend(expand_origin_caller_variant( + runtime, + pallet_decl, + index, + instance, + generics, + )); + pallet_conversions.extend(expand_origin_pallet_conversions( + scrate, + runtime, + pallet_decl, + instance, + generics, + )); + query_origin_part_macros.push(quote! { + #path::__substrate_origin_check::is_origin_part_defined!(#name); + }); + } + } + + let system_path = &system_pallet.path; + + let system_index = system_pallet.index; + + let system_path_name = system_path.module_name(); + + let doc_string = get_intra_doc_string( + "Origin is always created with the base filter configured in", + &system_path_name, + ); + + let doc_string_none_origin = + get_intra_doc_string("Create with system none origin and", &system_path_name); + + let doc_string_root_origin = + get_intra_doc_string("Create with system root origin and", &system_path_name); + + let doc_string_signed_origin = + get_intra_doc_string("Create with system signed origin and", &system_path_name); + + let doc_string_runtime_origin = + get_intra_doc_string("Convert to runtime origin, using as filter:", &system_path_name); + + let doc_string_runtime_origin_with_caller = get_intra_doc_string( + "Convert to runtime origin with caller being system signed or none and use filter", + &system_path_name, + ); + + Ok(quote! { + #( #query_origin_part_macros )* + + /// The runtime origin type representing the origin of a call. + /// + #[doc = #doc_string] + #[derive(Clone)] + pub struct RuntimeOrigin { + pub caller: OriginCaller, + filter: #scrate::__private::sp_std::rc::Rc::RuntimeCall) -> bool>>, + } + + #[cfg(not(feature = "std"))] + impl #scrate::__private::sp_std::fmt::Debug for RuntimeOrigin { + fn fmt( + &self, + fmt: &mut #scrate::__private::sp_std::fmt::Formatter, + ) -> #scrate::__private::sp_std::result::Result<(), #scrate::__private::sp_std::fmt::Error> { + fmt.write_str("") + } + } + + #[cfg(feature = "std")] + impl #scrate::__private::sp_std::fmt::Debug for RuntimeOrigin { + fn fmt( + &self, + fmt: &mut #scrate::__private::sp_std::fmt::Formatter, + ) -> #scrate::__private::sp_std::result::Result<(), #scrate::__private::sp_std::fmt::Error> { + fmt.debug_struct("Origin") + .field("caller", &self.caller) + .field("filter", &"[function ptr]") + .finish() + } + } + + impl #scrate::traits::OriginTrait for RuntimeOrigin { + type Call = <#runtime as #system_path::Config>::RuntimeCall; + type PalletsOrigin = OriginCaller; + type AccountId = <#runtime as #system_path::Config>::AccountId; + + fn add_filter(&mut self, filter: impl Fn(&Self::Call) -> bool + 'static) { + let f = self.filter.clone(); + + self.filter = #scrate::__private::sp_std::rc::Rc::new(Box::new(move |call| { + f(call) && filter(call) + })); + } + + fn reset_filter(&mut self) { + let filter = < + <#runtime as #system_path::Config>::BaseCallFilter + as #scrate::traits::Contains<<#runtime as #system_path::Config>::RuntimeCall> + >::contains; + + self.filter = #scrate::__private::sp_std::rc::Rc::new(Box::new(filter)); + } + + fn set_caller_from(&mut self, other: impl Into) { + self.caller = other.into().caller; + } + + fn filter_call(&self, call: &Self::Call) -> bool { + match self.caller { + // Root bypasses all filters + OriginCaller::system(#system_path::Origin::<#runtime>::Root) => true, + _ => (self.filter)(call), + } + } + + fn caller(&self) -> &Self::PalletsOrigin { + &self.caller + } + + fn into_caller(self) -> Self::PalletsOrigin { + self.caller + } + + fn try_with_caller( + mut self, + f: impl FnOnce(Self::PalletsOrigin) -> Result, + ) -> Result { + match f(self.caller) { + Ok(r) => Ok(r), + Err(caller) => { self.caller = caller; Err(self) } + } + } + + fn none() -> Self { + #system_path::RawOrigin::None.into() + } + + fn root() -> Self { + #system_path::RawOrigin::Root.into() + } + + fn signed(by: Self::AccountId) -> Self { + #system_path::RawOrigin::Signed(by).into() + } + } + + #[derive( + Clone, PartialEq, Eq, #scrate::__private::RuntimeDebug, #scrate::__private::codec::Encode, + #scrate::__private::codec::Decode, #scrate::__private::scale_info::TypeInfo, #scrate::__private::codec::MaxEncodedLen, + )] + #[allow(non_camel_case_types)] + pub enum OriginCaller { + #[codec(index = #system_index)] + system(#system_path::Origin<#runtime>), + #caller_variants + #[allow(dead_code)] + Void(#scrate::__private::Void) + } + + // For backwards compatibility and ease of accessing these functions. + #[allow(dead_code)] + impl RuntimeOrigin { + #[doc = #doc_string_none_origin] + pub fn none() -> Self { + ::none() + } + + #[doc = #doc_string_root_origin] + pub fn root() -> Self { + ::root() + } + + #[doc = #doc_string_signed_origin] + pub fn signed(by: <#runtime as #system_path::Config>::AccountId) -> Self { + ::signed(by) + } + } + + impl From<#system_path::Origin<#runtime>> for OriginCaller { + fn from(x: #system_path::Origin<#runtime>) -> Self { + OriginCaller::system(x) + } + } + + impl #scrate::traits::CallerTrait<<#runtime as #system_path::Config>::AccountId> for OriginCaller { + fn into_system(self) -> Option<#system_path::RawOrigin<<#runtime as #system_path::Config>::AccountId>> { + match self { + OriginCaller::system(x) => Some(x), + _ => None, + } + } + fn as_system_ref(&self) -> Option<&#system_path::RawOrigin<<#runtime as #system_path::Config>::AccountId>> { + match &self { + OriginCaller::system(o) => Some(o), + _ => None, + } + } + } + + impl TryFrom for #system_path::Origin<#runtime> { + type Error = OriginCaller; + fn try_from(x: OriginCaller) + -> #scrate::__private::sp_std::result::Result<#system_path::Origin<#runtime>, OriginCaller> + { + if let OriginCaller::system(l) = x { + Ok(l) + } else { + Err(x) + } + } + } + + impl From<#system_path::Origin<#runtime>> for RuntimeOrigin { + + #[doc = #doc_string_runtime_origin] + fn from(x: #system_path::Origin<#runtime>) -> Self { + let o: OriginCaller = x.into(); + o.into() + } + } + + impl From for RuntimeOrigin { + fn from(x: OriginCaller) -> Self { + let mut o = RuntimeOrigin { + caller: x, + filter: #scrate::__private::sp_std::rc::Rc::new(Box::new(|_| true)), + }; + + #scrate::traits::OriginTrait::reset_filter(&mut o); + + o + } + } + + impl From for #scrate::__private::sp_std::result::Result<#system_path::Origin<#runtime>, RuntimeOrigin> { + /// NOTE: converting to pallet origin loses the origin filter information. + fn from(val: RuntimeOrigin) -> Self { + if let OriginCaller::system(l) = val.caller { + Ok(l) + } else { + Err(val) + } + } + } + impl From::AccountId>> for RuntimeOrigin { + #[doc = #doc_string_runtime_origin_with_caller] + fn from(x: Option<<#runtime as #system_path::Config>::AccountId>) -> Self { + <#system_path::Origin<#runtime>>::from(x).into() + } + } + + #pallet_conversions + }) } fn expand_origin_caller_variant( - runtime: &Ident, - pallet: &Pallet, - index: u8, - instance: Option<&Ident>, - generics: &Generics, + runtime: &Ident, + pallet: &Pallet, + index: u8, + instance: Option<&Ident>, + generics: &Generics, ) -> TokenStream { - let part_is_generic = !generics.params.is_empty(); - let variant_name = &pallet.name; - let path = &pallet.path; - let attr = pallet - .cfg_pattern - .iter() - .fold(TokenStream::new(), |acc, pattern| { - let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) - .expect("was successfully parsed before; qed"); - quote! { - #acc - #attr - } - }); - - match instance { - Some(inst) if part_is_generic => quote! { - #attr - #[codec(index = #index)] - #variant_name(#path::Origin<#runtime, #path::#inst>), - }, - Some(inst) => quote! { - #attr - #[codec(index = #index)] - #variant_name(#path::Origin<#path::#inst>), - }, - None if part_is_generic => quote! { - #attr - #[codec(index = #index)] - #variant_name(#path::Origin<#runtime>), - }, - None => quote! { - #attr - #[codec(index = #index)] - #variant_name(#path::Origin), - }, - } + let part_is_generic = !generics.params.is_empty(); + let variant_name = &pallet.name; + let path = &pallet.path; + let attr = pallet.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); + + match instance { + Some(inst) if part_is_generic => quote! { + #attr + #[codec(index = #index)] + #variant_name(#path::Origin<#runtime, #path::#inst>), + }, + Some(inst) => quote! { + #attr + #[codec(index = #index)] + #variant_name(#path::Origin<#path::#inst>), + }, + None if part_is_generic => quote! { + #attr + #[codec(index = #index)] + #variant_name(#path::Origin<#runtime>), + }, + None => quote! { + #attr + #[codec(index = #index)] + #variant_name(#path::Origin), + }, + } } fn expand_origin_pallet_conversions( - scrate: &TokenStream, - runtime: &Ident, - pallet: &Pallet, - instance: Option<&Ident>, - generics: &Generics, + scrate: &TokenStream, + runtime: &Ident, + pallet: &Pallet, + instance: Option<&Ident>, + generics: &Generics, ) -> TokenStream { - let path = &pallet.path; - let variant_name = &pallet.name; - - let part_is_generic = !generics.params.is_empty(); - let pallet_origin = match instance { - Some(inst) if part_is_generic => quote!(#path::Origin<#runtime, #path::#inst>), - Some(inst) => quote!(#path::Origin<#path::#inst>), - None if part_is_generic => quote!(#path::Origin<#runtime>), - None => quote!(#path::Origin), - }; - - let doc_string = get_intra_doc_string(" Convert to runtime origin using", &path.module_name()); - let attr = pallet - .cfg_pattern - .iter() - .fold(TokenStream::new(), |acc, pattern| { - let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) - .expect("was successfully parsed before; qed"); - quote! { - #acc - #attr - } - }); - - quote! { - #attr - impl From<#pallet_origin> for OriginCaller { - fn from(x: #pallet_origin) -> Self { - OriginCaller::#variant_name(x) - } - } - - #attr - impl From<#pallet_origin> for RuntimeOrigin { - #[doc = #doc_string] - fn from(x: #pallet_origin) -> Self { - let x: OriginCaller = x.into(); - x.into() - } - } - - #attr - impl From for #scrate::__private::sp_std::result::Result<#pallet_origin, RuntimeOrigin> { - /// NOTE: converting to pallet origin loses the origin filter information. - fn from(val: RuntimeOrigin) -> Self { - if let OriginCaller::#variant_name(l) = val.caller { - Ok(l) - } else { - Err(val) - } - } - } - - #attr - impl TryFrom for #pallet_origin { - type Error = OriginCaller; - fn try_from( - x: OriginCaller, - ) -> #scrate::__private::sp_std::result::Result<#pallet_origin, OriginCaller> { - if let OriginCaller::#variant_name(l) = x { - Ok(l) - } else { - Err(x) - } - } - } - - #attr - impl<'a> TryFrom<&'a OriginCaller> for &'a #pallet_origin { - type Error = (); - fn try_from( - x: &'a OriginCaller, - ) -> #scrate::__private::sp_std::result::Result<&'a #pallet_origin, ()> { - if let OriginCaller::#variant_name(l) = x { - Ok(&l) - } else { - Err(()) - } - } - } - - #attr - impl<'a> TryFrom<&'a RuntimeOrigin> for &'a #pallet_origin { - type Error = (); - fn try_from( - x: &'a RuntimeOrigin, - ) -> #scrate::__private::sp_std::result::Result<&'a #pallet_origin, ()> { - if let OriginCaller::#variant_name(l) = &x.caller { - Ok(&l) - } else { - Err(()) - } - } - } - } + let path = &pallet.path; + let variant_name = &pallet.name; + + let part_is_generic = !generics.params.is_empty(); + let pallet_origin = match instance { + Some(inst) if part_is_generic => quote!(#path::Origin<#runtime, #path::#inst>), + Some(inst) => quote!(#path::Origin<#path::#inst>), + None if part_is_generic => quote!(#path::Origin<#runtime>), + None => quote!(#path::Origin), + }; + + let doc_string = get_intra_doc_string(" Convert to runtime origin using", &path.module_name()); + let attr = pallet.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); + + quote! { + #attr + impl From<#pallet_origin> for OriginCaller { + fn from(x: #pallet_origin) -> Self { + OriginCaller::#variant_name(x) + } + } + + #attr + impl From<#pallet_origin> for RuntimeOrigin { + #[doc = #doc_string] + fn from(x: #pallet_origin) -> Self { + let x: OriginCaller = x.into(); + x.into() + } + } + + #attr + impl From for #scrate::__private::sp_std::result::Result<#pallet_origin, RuntimeOrigin> { + /// NOTE: converting to pallet origin loses the origin filter information. + fn from(val: RuntimeOrigin) -> Self { + if let OriginCaller::#variant_name(l) = val.caller { + Ok(l) + } else { + Err(val) + } + } + } + + #attr + impl TryFrom for #pallet_origin { + type Error = OriginCaller; + fn try_from( + x: OriginCaller, + ) -> #scrate::__private::sp_std::result::Result<#pallet_origin, OriginCaller> { + if let OriginCaller::#variant_name(l) = x { + Ok(l) + } else { + Err(x) + } + } + } + + #attr + impl<'a> TryFrom<&'a OriginCaller> for &'a #pallet_origin { + type Error = (); + fn try_from( + x: &'a OriginCaller, + ) -> #scrate::__private::sp_std::result::Result<&'a #pallet_origin, ()> { + if let OriginCaller::#variant_name(l) = x { + Ok(&l) + } else { + Err(()) + } + } + } + + #attr + impl<'a> TryFrom<&'a RuntimeOrigin> for &'a #pallet_origin { + type Error = (); + fn try_from( + x: &'a RuntimeOrigin, + ) -> #scrate::__private::sp_std::result::Result<&'a #pallet_origin, ()> { + if let OriginCaller::#variant_name(l) = &x.caller { + Ok(&l) + } else { + Err(()) + } + } + } + } } // Get the actual documentation using the doc information and system path name fn get_intra_doc_string(doc_info: &str, system_path_name: &String) -> String { - format!( - " {} [`{}::Config::BaseCallFilter`].", - doc_info, system_path_name - ) + format!(" {} [`{}::Config::BaseCallFilter`].", doc_info, system_path_name) } diff --git a/support/procedural-fork/src/construct_runtime/expand/outer_enums.rs b/support/procedural-fork/src/construct_runtime/expand/outer_enums.rs index 28e39c7a2..80b242ccb 100644 --- a/support/procedural-fork/src/construct_runtime/expand/outer_enums.rs +++ b/support/procedural-fork/src/construct_runtime/expand/outer_enums.rs @@ -24,37 +24,37 @@ use syn::{Generics, Ident}; /// Represents the types supported for creating an outer enum. #[derive(Clone, Copy, PartialEq)] pub enum OuterEnumType { - /// Collects the Event enums from all pallets. - Event, - /// Collects the Error enums from all pallets. - Error, + /// Collects the Event enums from all pallets. + Event, + /// Collects the Error enums from all pallets. + Error, } impl OuterEnumType { - /// The name of the structure this enum represents. - fn struct_name(&self) -> &str { - match self { - OuterEnumType::Event => "RuntimeEvent", - OuterEnumType::Error => "RuntimeError", - } - } + /// The name of the structure this enum represents. + fn struct_name(&self) -> &str { + match self { + OuterEnumType::Event => "RuntimeEvent", + OuterEnumType::Error => "RuntimeError", + } + } - /// The name of the variant (ie `Event` or `Error`). - fn variant_name(&self) -> &str { - match self { - OuterEnumType::Event => "Event", - OuterEnumType::Error => "Error", - } - } + /// The name of the variant (ie `Event` or `Error`). + fn variant_name(&self) -> &str { + match self { + OuterEnumType::Event => "Event", + OuterEnumType::Error => "Error", + } + } } impl ToTokens for OuterEnumType { - fn to_tokens(&self, tokens: &mut TokenStream) { - match self { - OuterEnumType::Event => quote!(Event).to_tokens(tokens), - OuterEnumType::Error => quote!(Error).to_tokens(tokens), - } - } + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + OuterEnumType::Event => quote!(Event).to_tokens(tokens), + OuterEnumType::Error => quote!(Error).to_tokens(tokens), + } + } } /// Create an outer enum that encapsulates all pallets as variants. @@ -84,207 +84,196 @@ impl ToTokens for OuterEnumType { /// /// Notice that the pallet index is preserved using the `#[codec(index = ..)]` attribute. pub fn expand_outer_enum( - runtime: &Ident, - pallet_decls: &[Pallet], - scrate: &TokenStream, - enum_ty: OuterEnumType, + runtime: &Ident, + pallet_decls: &[Pallet], + scrate: &TokenStream, + enum_ty: OuterEnumType, ) -> syn::Result { - // Stores all pallet variants. - let mut enum_variants = TokenStream::new(); - // Generates the enum conversion between the `Runtime` outer enum and the pallet's enum. - let mut enum_conversions = TokenStream::new(); - // Specific for events to query via `is_event_part_defined!`. - let mut query_enum_part_macros = Vec::new(); + // Stores all pallet variants. + let mut enum_variants = TokenStream::new(); + // Generates the enum conversion between the `Runtime` outer enum and the pallet's enum. + let mut enum_conversions = TokenStream::new(); + // Specific for events to query via `is_event_part_defined!`. + let mut query_enum_part_macros = Vec::new(); - let enum_name_str = enum_ty.variant_name(); - let enum_name_ident = Ident::new(enum_ty.struct_name(), Span::call_site()); + let enum_name_str = enum_ty.variant_name(); + let enum_name_ident = Ident::new(enum_ty.struct_name(), Span::call_site()); - for pallet_decl in pallet_decls { - let Some(pallet_entry) = pallet_decl.find_part(enum_name_str) else { - continue; - }; + for pallet_decl in pallet_decls { + let Some(pallet_entry) = pallet_decl.find_part(enum_name_str) else { continue }; - let path = &pallet_decl.path; - let pallet_name = &pallet_decl.name; - let index = pallet_decl.index; - let instance = pallet_decl.instance.as_ref(); - let generics = &pallet_entry.generics; + let path = &pallet_decl.path; + let pallet_name = &pallet_decl.name; + let index = pallet_decl.index; + let instance = pallet_decl.instance.as_ref(); + let generics = &pallet_entry.generics; - if instance.is_some() && generics.params.is_empty() { - let msg = format!( - "Instantiable pallet with no generic `{}` cannot \ + if instance.is_some() && generics.params.is_empty() { + let msg = format!( + "Instantiable pallet with no generic `{}` cannot \ be constructed: pallet `{}` must have generic `{}`", - enum_name_str, pallet_name, enum_name_str, - ); - return Err(syn::Error::new(pallet_name.span(), msg)); - } + enum_name_str, pallet_name, enum_name_str, + ); + return Err(syn::Error::new(pallet_name.span(), msg)) + } - let part_is_generic = !generics.params.is_empty(); - let pallet_enum = match (instance, part_is_generic) { - (Some(inst), true) => quote!(#path::#enum_ty::<#runtime, #path::#inst>), - (Some(inst), false) => quote!(#path::#enum_ty::<#path::#inst>), - (None, true) => quote!(#path::#enum_ty::<#runtime>), - (None, false) => quote!(#path::#enum_ty), - }; + let part_is_generic = !generics.params.is_empty(); + let pallet_enum = match (instance, part_is_generic) { + (Some(inst), true) => quote!(#path::#enum_ty::<#runtime, #path::#inst>), + (Some(inst), false) => quote!(#path::#enum_ty::<#path::#inst>), + (None, true) => quote!(#path::#enum_ty::<#runtime>), + (None, false) => quote!(#path::#enum_ty), + }; - enum_variants.extend(expand_enum_variant( - runtime, - pallet_decl, - index, - instance, - generics, - enum_ty, - )); - enum_conversions.extend(expand_enum_conversion( - pallet_decl, - &pallet_enum, - &enum_name_ident, - )); + enum_variants.extend(expand_enum_variant( + runtime, + pallet_decl, + index, + instance, + generics, + enum_ty, + )); + enum_conversions.extend(expand_enum_conversion( + pallet_decl, + &pallet_enum, + &enum_name_ident, + )); - if enum_ty == OuterEnumType::Event { - query_enum_part_macros.push(quote! { - #path::__substrate_event_check::is_event_part_defined!(#pallet_name); - }); - } - } + if enum_ty == OuterEnumType::Event { + query_enum_part_macros.push(quote! { + #path::__substrate_event_check::is_event_part_defined!(#pallet_name); + }); + } + } - // Derives specific for the event. - let event_custom_derives = if enum_ty == OuterEnumType::Event { - quote!(Clone, PartialEq, Eq,) - } else { - quote!() - }; + // Derives specific for the event. + let event_custom_derives = + if enum_ty == OuterEnumType::Event { quote!(Clone, PartialEq, Eq,) } else { quote!() }; - // Implementation specific for errors. - let error_custom_impl = generate_error_impl(scrate, enum_ty); + // Implementation specific for errors. + let error_custom_impl = generate_error_impl(scrate, enum_ty); - Ok(quote! { - #( #query_enum_part_macros )* + Ok(quote! { + #( #query_enum_part_macros )* - #[derive( - #event_custom_derives - #scrate::__private::codec::Encode, - #scrate::__private::codec::Decode, - #scrate::__private::scale_info::TypeInfo, - #scrate::__private::RuntimeDebug, - )] - #[allow(non_camel_case_types)] - pub enum #enum_name_ident { - #enum_variants - } + #[derive( + #event_custom_derives + #scrate::__private::codec::Encode, + #scrate::__private::codec::Decode, + #scrate::__private::scale_info::TypeInfo, + #scrate::__private::RuntimeDebug, + )] + #[allow(non_camel_case_types)] + pub enum #enum_name_ident { + #enum_variants + } - #enum_conversions + #enum_conversions - #error_custom_impl - }) + #error_custom_impl + }) } fn expand_enum_variant( - runtime: &Ident, - pallet: &Pallet, - index: u8, - instance: Option<&Ident>, - generics: &Generics, - enum_ty: OuterEnumType, + runtime: &Ident, + pallet: &Pallet, + index: u8, + instance: Option<&Ident>, + generics: &Generics, + enum_ty: OuterEnumType, ) -> TokenStream { - let path = &pallet.path; - let variant_name = &pallet.name; - let part_is_generic = !generics.params.is_empty(); - let attr = pallet - .cfg_pattern - .iter() - .fold(TokenStream::new(), |acc, pattern| { - let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) - .expect("was successfully parsed before; qed"); - quote! { - #acc - #attr - } - }); + let path = &pallet.path; + let variant_name = &pallet.name; + let part_is_generic = !generics.params.is_empty(); + let attr = pallet.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); - match instance { - Some(inst) if part_is_generic => quote! { - #attr - #[codec(index = #index)] - #variant_name(#path::#enum_ty<#runtime, #path::#inst>), - }, - Some(inst) => quote! { - #attr - #[codec(index = #index)] - #variant_name(#path::#enum_ty<#path::#inst>), - }, - None if part_is_generic => quote! { - #attr - #[codec(index = #index)] - #variant_name(#path::#enum_ty<#runtime>), - }, - None => quote! { - #attr - #[codec(index = #index)] - #variant_name(#path::#enum_ty), - }, - } + match instance { + Some(inst) if part_is_generic => quote! { + #attr + #[codec(index = #index)] + #variant_name(#path::#enum_ty<#runtime, #path::#inst>), + }, + Some(inst) => quote! { + #attr + #[codec(index = #index)] + #variant_name(#path::#enum_ty<#path::#inst>), + }, + None if part_is_generic => quote! { + #attr + #[codec(index = #index)] + #variant_name(#path::#enum_ty<#runtime>), + }, + None => quote! { + #attr + #[codec(index = #index)] + #variant_name(#path::#enum_ty), + }, + } } fn expand_enum_conversion( - pallet: &Pallet, - pallet_enum: &TokenStream, - enum_name_ident: &Ident, + pallet: &Pallet, + pallet_enum: &TokenStream, + enum_name_ident: &Ident, ) -> TokenStream { - let variant_name = &pallet.name; - let attr = pallet - .cfg_pattern - .iter() - .fold(TokenStream::new(), |acc, pattern| { - let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) - .expect("was successfully parsed before; qed"); - quote! { - #acc - #attr - } - }); + let variant_name = &pallet.name; + let attr = pallet.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); - quote! { - #attr - impl From<#pallet_enum> for #enum_name_ident { - fn from(x: #pallet_enum) -> Self { - #enum_name_ident - ::#variant_name(x) - } - } - #attr - impl TryInto<#pallet_enum> for #enum_name_ident { - type Error = (); + quote! { + #attr + impl From<#pallet_enum> for #enum_name_ident { + fn from(x: #pallet_enum) -> Self { + #enum_name_ident + ::#variant_name(x) + } + } + #attr + impl TryInto<#pallet_enum> for #enum_name_ident { + type Error = (); - fn try_into(self) -> ::core::result::Result<#pallet_enum, Self::Error> { - match self { - Self::#variant_name(evt) => Ok(evt), - _ => Err(()), - } - } - } - } + fn try_into(self) -> ::core::result::Result<#pallet_enum, Self::Error> { + match self { + Self::#variant_name(evt) => Ok(evt), + _ => Err(()), + } + } + } + } } fn generate_error_impl(scrate: &TokenStream, enum_ty: OuterEnumType) -> TokenStream { - // Implementation is specific to `Error`s. - if enum_ty == OuterEnumType::Event { - return quote! {}; - } + // Implementation is specific to `Error`s. + if enum_ty == OuterEnumType::Event { + return quote! {} + } - let enum_name_ident = Ident::new(enum_ty.struct_name(), Span::call_site()); + let enum_name_ident = Ident::new(enum_ty.struct_name(), Span::call_site()); - quote! { - impl #enum_name_ident { - /// Optionally convert the `DispatchError` into the `RuntimeError`. - /// - /// Returns `Some` if the error matches the `DispatchError::Module` variant, otherwise `None`. - pub fn from_dispatch_error(err: #scrate::sp_runtime::DispatchError) -> Option { - let #scrate::sp_runtime::DispatchError::Module(module_error) = err else { return None }; + quote! { + impl #enum_name_ident { + /// Optionally convert the `DispatchError` into the `RuntimeError`. + /// + /// Returns `Some` if the error matches the `DispatchError::Module` variant, otherwise `None`. + pub fn from_dispatch_error(err: #scrate::sp_runtime::DispatchError) -> Option { + let #scrate::sp_runtime::DispatchError::Module(module_error) = err else { return None }; - let bytes = #scrate::__private::codec::Encode::encode(&module_error); - #scrate::__private::codec::Decode::decode(&mut &bytes[..]).ok() - } - } - } + let bytes = #scrate::__private::codec::Encode::encode(&module_error); + #scrate::__private::codec::Decode::decode(&mut &bytes[..]).ok() + } + } + } } diff --git a/support/procedural-fork/src/construct_runtime/expand/slash_reason.rs b/support/procedural-fork/src/construct_runtime/expand/slash_reason.rs index d9e9e9320..892b842b1 100644 --- a/support/procedural-fork/src/construct_runtime/expand/slash_reason.rs +++ b/support/procedural-fork/src/construct_runtime/expand/slash_reason.rs @@ -21,44 +21,44 @@ use proc_macro2::TokenStream; use quote::quote; pub fn expand_outer_slash_reason(pallet_decls: &[Pallet], scrate: &TokenStream) -> TokenStream { - let mut conversion_fns = Vec::new(); - let mut slash_reason_variants = Vec::new(); - for decl in pallet_decls { - if decl.find_part("SlashReason").is_some() { - let variant_name = &decl.name; - let path = &decl.path; - let index = decl.index; - let instance = decl.instance.as_ref(); + let mut conversion_fns = Vec::new(); + let mut slash_reason_variants = Vec::new(); + for decl in pallet_decls { + if let Some(_) = decl.find_part("SlashReason") { + let variant_name = &decl.name; + let path = &decl.path; + let index = decl.index; + let instance = decl.instance.as_ref(); - conversion_fns.push(composite_helper::expand_conversion_fn( - "SlashReason", - path, - instance, - variant_name, - )); + conversion_fns.push(composite_helper::expand_conversion_fn( + "SlashReason", + path, + instance, + variant_name, + )); - slash_reason_variants.push(composite_helper::expand_variant( - "SlashReason", - index, - path, - instance, - variant_name, - )); - } - } + slash_reason_variants.push(composite_helper::expand_variant( + "SlashReason", + index, + path, + instance, + variant_name, + )); + } + } - quote! { - /// A reason for slashing funds. - #[derive( - Copy, Clone, Eq, PartialEq, - #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, - #scrate::__private::scale_info::TypeInfo, - #scrate::__private::RuntimeDebug, - )] - pub enum RuntimeSlashReason { - #( #slash_reason_variants )* - } + quote! { + /// A reason for slashing funds. + #[derive( + Copy, Clone, Eq, PartialEq, + #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, + #scrate::__private::scale_info::TypeInfo, + #scrate::__private::RuntimeDebug, + )] + pub enum RuntimeSlashReason { + #( #slash_reason_variants )* + } - #( #conversion_fns )* - } + #( #conversion_fns )* + } } diff --git a/support/procedural-fork/src/construct_runtime/expand/task.rs b/support/procedural-fork/src/construct_runtime/expand/task.rs index dd8d93c27..6531c0e9e 100644 --- a/support/procedural-fork/src/construct_runtime/expand/task.rs +++ b/support/procedural-fork/src/construct_runtime/expand/task.rs @@ -21,111 +21,111 @@ use quote::quote; /// Expands aggregate `RuntimeTask` enum. pub fn expand_outer_task( - runtime_name: &Ident, - pallet_decls: &[Pallet], - scrate: &TokenStream2, + runtime_name: &Ident, + pallet_decls: &[Pallet], + scrate: &TokenStream2, ) -> TokenStream2 { - let mut from_impls = Vec::new(); - let mut task_variants = Vec::new(); - let mut variant_names = Vec::new(); - let mut task_paths = Vec::new(); - for decl in pallet_decls { - if decl.find_part("Task").is_none() { - continue; - } - - let variant_name = &decl.name; - let path = &decl.path; - let index = decl.index; - - from_impls.push(quote! { - impl From<#path::Task<#runtime_name>> for RuntimeTask { - fn from(hr: #path::Task<#runtime_name>) -> Self { - RuntimeTask::#variant_name(hr) - } - } - - impl TryInto<#path::Task<#runtime_name>> for RuntimeTask { - type Error = (); - - fn try_into(self) -> Result<#path::Task<#runtime_name>, Self::Error> { - match self { - RuntimeTask::#variant_name(hr) => Ok(hr), - _ => Err(()), - } - } - } - }); - - task_variants.push(quote! { - #[codec(index = #index)] - #variant_name(#path::Task<#runtime_name>), - }); - - variant_names.push(quote!(#variant_name)); - - task_paths.push(quote!(#path::Task)); - } - - let prelude = quote!(#scrate::traits::tasks::__private); - - const INCOMPLETE_MATCH_QED: &str = - "cannot have an instantiated RuntimeTask without some Task variant in the runtime. QED"; - - let output = quote! { - /// An aggregation of all `Task` enums across all pallets included in the current runtime. - #[derive( - Clone, Eq, PartialEq, - #scrate::__private::codec::Encode, - #scrate::__private::codec::Decode, - #scrate::__private::scale_info::TypeInfo, - #scrate::__private::RuntimeDebug, - )] - pub enum RuntimeTask { - #( #task_variants )* - } - - #[automatically_derived] - impl #scrate::traits::Task for RuntimeTask { - type Enumeration = #prelude::IntoIter; - - fn is_valid(&self) -> bool { - match self { - #(RuntimeTask::#variant_names(val) => val.is_valid(),)* - _ => unreachable!(#INCOMPLETE_MATCH_QED), - } - } - - fn run(&self) -> Result<(), #scrate::traits::tasks::__private::DispatchError> { - match self { - #(RuntimeTask::#variant_names(val) => val.run(),)* - _ => unreachable!(#INCOMPLETE_MATCH_QED), - } - } - - fn weight(&self) -> #scrate::pallet_prelude::Weight { - match self { - #(RuntimeTask::#variant_names(val) => val.weight(),)* - _ => unreachable!(#INCOMPLETE_MATCH_QED), - } - } - - fn task_index(&self) -> u32 { - match self { - #(RuntimeTask::#variant_names(val) => val.task_index(),)* - _ => unreachable!(#INCOMPLETE_MATCH_QED), - } - } - - fn iter() -> Self::Enumeration { - let mut all_tasks = Vec::new(); - #(all_tasks.extend(#task_paths::iter().map(RuntimeTask::from).collect::>());)* - all_tasks.into_iter() - } - } - - #( #from_impls )* - }; - - output + let mut from_impls = Vec::new(); + let mut task_variants = Vec::new(); + let mut variant_names = Vec::new(); + let mut task_paths = Vec::new(); + for decl in pallet_decls { + if decl.find_part("Task").is_none() { + continue + } + + let variant_name = &decl.name; + let path = &decl.path; + let index = decl.index; + + from_impls.push(quote! { + impl From<#path::Task<#runtime_name>> for RuntimeTask { + fn from(hr: #path::Task<#runtime_name>) -> Self { + RuntimeTask::#variant_name(hr) + } + } + + impl TryInto<#path::Task<#runtime_name>> for RuntimeTask { + type Error = (); + + fn try_into(self) -> Result<#path::Task<#runtime_name>, Self::Error> { + match self { + RuntimeTask::#variant_name(hr) => Ok(hr), + _ => Err(()), + } + } + } + }); + + task_variants.push(quote! { + #[codec(index = #index)] + #variant_name(#path::Task<#runtime_name>), + }); + + variant_names.push(quote!(#variant_name)); + + task_paths.push(quote!(#path::Task)); + } + + let prelude = quote!(#scrate::traits::tasks::__private); + + const INCOMPLETE_MATCH_QED: &'static str = + "cannot have an instantiated RuntimeTask without some Task variant in the runtime. QED"; + + let output = quote! { + /// An aggregation of all `Task` enums across all pallets included in the current runtime. + #[derive( + Clone, Eq, PartialEq, + #scrate::__private::codec::Encode, + #scrate::__private::codec::Decode, + #scrate::__private::scale_info::TypeInfo, + #scrate::__private::RuntimeDebug, + )] + pub enum RuntimeTask { + #( #task_variants )* + } + + #[automatically_derived] + impl #scrate::traits::Task for RuntimeTask { + type Enumeration = #prelude::IntoIter; + + fn is_valid(&self) -> bool { + match self { + #(RuntimeTask::#variant_names(val) => val.is_valid(),)* + _ => unreachable!(#INCOMPLETE_MATCH_QED), + } + } + + fn run(&self) -> Result<(), #scrate::traits::tasks::__private::DispatchError> { + match self { + #(RuntimeTask::#variant_names(val) => val.run(),)* + _ => unreachable!(#INCOMPLETE_MATCH_QED), + } + } + + fn weight(&self) -> #scrate::pallet_prelude::Weight { + match self { + #(RuntimeTask::#variant_names(val) => val.weight(),)* + _ => unreachable!(#INCOMPLETE_MATCH_QED), + } + } + + fn task_index(&self) -> u32 { + match self { + #(RuntimeTask::#variant_names(val) => val.task_index(),)* + _ => unreachable!(#INCOMPLETE_MATCH_QED), + } + } + + fn iter() -> Self::Enumeration { + let mut all_tasks = Vec::new(); + #(all_tasks.extend(#task_paths::iter().map(RuntimeTask::from).collect::>());)* + all_tasks.into_iter() + } + } + + #( #from_impls )* + }; + + output } diff --git a/support/procedural-fork/src/construct_runtime/expand/unsigned.rs b/support/procedural-fork/src/construct_runtime/expand/unsigned.rs index 109f7081c..33aadba0d 100644 --- a/support/procedural-fork/src/construct_runtime/expand/unsigned.rs +++ b/support/procedural-fork/src/construct_runtime/expand/unsigned.rs @@ -22,71 +22,68 @@ use std::str::FromStr; use syn::Ident; pub fn expand_outer_validate_unsigned( - runtime: &Ident, - pallet_decls: &[Pallet], - scrate: &TokenStream, + runtime: &Ident, + pallet_decls: &[Pallet], + scrate: &TokenStream, ) -> TokenStream { - let mut pallet_names = Vec::new(); - let mut pallet_attrs = Vec::new(); - let mut query_validate_unsigned_part_macros = Vec::new(); + let mut pallet_names = Vec::new(); + let mut pallet_attrs = Vec::new(); + let mut query_validate_unsigned_part_macros = Vec::new(); - for pallet_decl in pallet_decls { - if pallet_decl.exists_part("ValidateUnsigned") { - let name = &pallet_decl.name; - let path = &pallet_decl.path; - let attr = pallet_decl - .cfg_pattern - .iter() - .fold(TokenStream::new(), |acc, pattern| { - let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) - .expect("was successfully parsed before; qed"); - quote! { - #acc - #attr - } - }); + for pallet_decl in pallet_decls { + if pallet_decl.exists_part("ValidateUnsigned") { + let name = &pallet_decl.name; + let path = &pallet_decl.path; + let attr = pallet_decl.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); - pallet_names.push(name); - pallet_attrs.push(attr); - query_validate_unsigned_part_macros.push(quote! { + pallet_names.push(name); + pallet_attrs.push(attr); + query_validate_unsigned_part_macros.push(quote! { #path::__substrate_validate_unsigned_check::is_validate_unsigned_part_defined!(#name); }); - } - } + } + } - quote! { - #( #query_validate_unsigned_part_macros )* + quote! { + #( #query_validate_unsigned_part_macros )* - impl #scrate::unsigned::ValidateUnsigned for #runtime { - type Call = RuntimeCall; + impl #scrate::unsigned::ValidateUnsigned for #runtime { + type Call = RuntimeCall; - fn pre_dispatch(call: &Self::Call) -> Result<(), #scrate::unsigned::TransactionValidityError> { - #[allow(unreachable_patterns)] - match call { - #( - #pallet_attrs - RuntimeCall::#pallet_names(inner_call) => #pallet_names::pre_dispatch(inner_call), - )* - // pre-dispatch should not stop inherent extrinsics, validation should prevent - // including arbitrary (non-inherent) extrinsics to blocks. - _ => Ok(()), - } - } + fn pre_dispatch(call: &Self::Call) -> Result<(), #scrate::unsigned::TransactionValidityError> { + #[allow(unreachable_patterns)] + match call { + #( + #pallet_attrs + RuntimeCall::#pallet_names(inner_call) => #pallet_names::pre_dispatch(inner_call), + )* + // pre-dispatch should not stop inherent extrinsics, validation should prevent + // including arbitrary (non-inherent) extrinsics to blocks. + _ => Ok(()), + } + } - fn validate_unsigned( - #[allow(unused_variables)] - source: #scrate::unsigned::TransactionSource, - call: &Self::Call, - ) -> #scrate::unsigned::TransactionValidity { - #[allow(unreachable_patterns)] - match call { - #( - #pallet_attrs - RuntimeCall::#pallet_names(inner_call) => #pallet_names::validate_unsigned(source, inner_call), - )* - _ => #scrate::unsigned::UnknownTransaction::NoUnsignedValidator.into(), - } - } - } - } + fn validate_unsigned( + #[allow(unused_variables)] + source: #scrate::unsigned::TransactionSource, + call: &Self::Call, + ) -> #scrate::unsigned::TransactionValidity { + #[allow(unreachable_patterns)] + match call { + #( + #pallet_attrs + RuntimeCall::#pallet_names(inner_call) => #pallet_names::validate_unsigned(source, inner_call), + )* + _ => #scrate::unsigned::UnknownTransaction::NoUnsignedValidator.into(), + } + } + } + } } diff --git a/support/procedural-fork/src/construct_runtime/mod.rs b/support/procedural-fork/src/construct_runtime/mod.rs index f01ebe0dd..b083abbb2 100644 --- a/support/procedural-fork/src/construct_runtime/mod.rs +++ b/support/procedural-fork/src/construct_runtime/mod.rs @@ -214,7 +214,7 @@ pub(crate) mod parse; use crate::pallet::parse::helper::two128_str; use cfg_expr::Predicate; use frame_support_procedural_tools::{ - generate_access_from_frame_or_crate, generate_crate_access, generate_hidden_includes, + generate_access_from_frame_or_crate, generate_crate_access, generate_hidden_includes, }; use itertools::Itertools; use parse::{ExplicitRuntimeDeclaration, ImplicitRuntimeDeclaration, Pallet, RuntimeDeclaration}; @@ -230,48 +230,48 @@ const SYSTEM_PALLET_NAME: &str = "System"; /// Implementation of `construct_runtime` macro. Either expand to some code which will call /// `construct_runtime` again, or expand to the final runtime definition. pub fn construct_runtime(input: TokenStream) -> TokenStream { - let input_copy = input.clone(); - let definition = syn::parse_macro_input!(input as RuntimeDeclaration); - - let (check_pallet_number_res, res) = match definition { - RuntimeDeclaration::Implicit(implicit_def) => ( - check_pallet_number(input_copy.clone().into(), implicit_def.pallets.len()), - construct_runtime_implicit_to_explicit(input_copy.into(), implicit_def), - ), - RuntimeDeclaration::Explicit(explicit_decl) => ( - check_pallet_number(input_copy.clone().into(), explicit_decl.pallets.len()), - construct_runtime_explicit_to_explicit_expanded(input_copy.into(), explicit_decl), - ), - RuntimeDeclaration::ExplicitExpanded(explicit_decl) => ( - check_pallet_number(input_copy.into(), explicit_decl.pallets.len()), - construct_runtime_final_expansion(explicit_decl), - ), - }; - - let res = res.unwrap_or_else(|e| e.to_compile_error()); - - // We want to provide better error messages to the user and thus, handle the error here - // separately. If there is an error, we print the error and still generate all of the code to - // get in overall less errors for the user. - let res = if let Err(error) = check_pallet_number_res { - let error = error.to_compile_error(); - - quote! { - #error - - #res - } - } else { - res - }; - - let res = expander::Expander::new("construct_runtime") - .dry(std::env::var("EXPAND_MACROS").is_err()) - .verbose(true) - .write_to_out_dir(res) - .expect("Does not fail because of IO in OUT_DIR; qed"); - - res.into() + let input_copy = input.clone(); + let definition = syn::parse_macro_input!(input as RuntimeDeclaration); + + let (check_pallet_number_res, res) = match definition { + RuntimeDeclaration::Implicit(implicit_def) => ( + check_pallet_number(input_copy.clone().into(), implicit_def.pallets.len()), + construct_runtime_implicit_to_explicit(input_copy.into(), implicit_def), + ), + RuntimeDeclaration::Explicit(explicit_decl) => ( + check_pallet_number(input_copy.clone().into(), explicit_decl.pallets.len()), + construct_runtime_explicit_to_explicit_expanded(input_copy.into(), explicit_decl), + ), + RuntimeDeclaration::ExplicitExpanded(explicit_decl) => ( + check_pallet_number(input_copy.into(), explicit_decl.pallets.len()), + construct_runtime_final_expansion(explicit_decl), + ), + }; + + let res = res.unwrap_or_else(|e| e.to_compile_error()); + + // We want to provide better error messages to the user and thus, handle the error here + // separately. If there is an error, we print the error and still generate all of the code to + // get in overall less errors for the user. + let res = if let Err(error) = check_pallet_number_res { + let error = error.to_compile_error(); + + quote! { + #error + + #res + } + } else { + res + }; + + let res = expander::Expander::new("construct_runtime") + .dry(std::env::var("EXPAND_MACROS").is_err()) + .verbose(true) + .write_to_out_dir(res) + .expect("Does not fail because of IO in OUT_DIR; qed"); + + res.into() } /// All pallets that have implicit pallet parts (ie `System: frame_system`) are @@ -282,37 +282,30 @@ pub fn construct_runtime(input: TokenStream) -> TokenStream { /// /// For more details, please refer to the root documentation. fn construct_runtime_implicit_to_explicit( - input: TokenStream2, - definition: ImplicitRuntimeDeclaration, + input: TokenStream2, + definition: ImplicitRuntimeDeclaration, ) -> Result { - let frame_support = generate_access_from_frame_or_crate("frame-support")?; - let mut expansion = quote::quote!( - #frame_support::construct_runtime! { #input } - ); - for pallet in definition - .pallets - .iter() - .filter(|pallet| pallet.pallet_parts.is_none()) - { - let pallet_path = &pallet.path; - let pallet_name = &pallet.name; - let pallet_instance = pallet - .instance - .as_ref() - .map(|instance| quote::quote!(::<#instance>)); - expansion = quote::quote!( - #frame_support::__private::tt_call! { - macro = [{ #pallet_path::tt_default_parts }] - your_tt_return = [{ #frame_support::__private::tt_return }] - ~~> #frame_support::match_and_insert! { - target = [{ #expansion }] - pattern = [{ #pallet_name: #pallet_path #pallet_instance }] - } - } - ); - } - - Ok(expansion) + let frame_support = generate_access_from_frame_or_crate("frame-support")?; + let mut expansion = quote::quote!( + #frame_support::construct_runtime! { #input } + ); + for pallet in definition.pallets.iter().filter(|pallet| pallet.pallet_parts.is_none()) { + let pallet_path = &pallet.path; + let pallet_name = &pallet.name; + let pallet_instance = pallet.instance.as_ref().map(|instance| quote::quote!(::<#instance>)); + expansion = quote::quote!( + #frame_support::__private::tt_call! { + macro = [{ #pallet_path::tt_default_parts }] + your_tt_return = [{ #frame_support::__private::tt_return }] + ~~> #frame_support::match_and_insert! { + target = [{ #expansion }] + pattern = [{ #pallet_name: #pallet_path #pallet_instance }] + } + } + ); + } + + Ok(expansion) } /// All pallets that have @@ -325,279 +318,264 @@ fn construct_runtime_implicit_to_explicit( /// /// For more details, please refer to the root documentation. fn construct_runtime_explicit_to_explicit_expanded( - input: TokenStream2, - definition: ExplicitRuntimeDeclaration, + input: TokenStream2, + definition: ExplicitRuntimeDeclaration, ) -> Result { - let frame_support = generate_access_from_frame_or_crate("frame-support")?; - let mut expansion = quote::quote!( - #frame_support::construct_runtime! { #input } - ); - for pallet in definition - .pallets - .iter() - .filter(|pallet| !pallet.is_expanded) - { - let pallet_path = &pallet.path; - let pallet_name = &pallet.name; - let pallet_instance = pallet - .instance - .as_ref() - .map(|instance| quote::quote!(::<#instance>)); - expansion = quote::quote!( - #frame_support::__private::tt_call! { - macro = [{ #pallet_path::tt_extra_parts }] - your_tt_return = [{ #frame_support::__private::tt_return }] - ~~> #frame_support::match_and_insert! { - target = [{ #expansion }] - pattern = [{ #pallet_name: #pallet_path #pallet_instance }] - } - } - ); - } - - Ok(expansion) + let frame_support = generate_access_from_frame_or_crate("frame-support")?; + let mut expansion = quote::quote!( + #frame_support::construct_runtime! { #input } + ); + for pallet in definition.pallets.iter().filter(|pallet| !pallet.is_expanded) { + let pallet_path = &pallet.path; + let pallet_name = &pallet.name; + let pallet_instance = pallet.instance.as_ref().map(|instance| quote::quote!(::<#instance>)); + expansion = quote::quote!( + #frame_support::__private::tt_call! { + macro = [{ #pallet_path::tt_extra_parts }] + your_tt_return = [{ #frame_support::__private::tt_return }] + ~~> #frame_support::match_and_insert! { + target = [{ #expansion }] + pattern = [{ #pallet_name: #pallet_path #pallet_instance }] + } + } + ); + } + + Ok(expansion) } /// All pallets have explicit definition of parts, this will expand to the runtime declaration. fn construct_runtime_final_expansion( - definition: ExplicitRuntimeDeclaration, + definition: ExplicitRuntimeDeclaration, ) -> Result { - let ExplicitRuntimeDeclaration { - name, - pallets, - pallets_token, - where_section, - } = definition; - - let system_pallet = pallets - .iter() - .find(|decl| decl.name == SYSTEM_PALLET_NAME) - .ok_or_else(|| { - syn::Error::new( - pallets_token.span.join(), - "`System` pallet declaration is missing. \ + let ExplicitRuntimeDeclaration { name, pallets, pallets_token, where_section } = definition; + + let system_pallet = + pallets.iter().find(|decl| decl.name == SYSTEM_PALLET_NAME).ok_or_else(|| { + syn::Error::new( + pallets_token.span.join(), + "`System` pallet declaration is missing. \ Please add this line: `System: frame_system,`", - ) - })?; - if !system_pallet.cfg_pattern.is_empty() { - return Err(syn::Error::new( - system_pallet.name.span(), - "`System` pallet declaration is feature gated, please remove any `#[cfg]` attributes", - )); - } - - let features = pallets - .iter() - .filter(|&decl| (!decl.cfg_pattern.is_empty())) - .flat_map(|decl| { - decl.cfg_pattern.iter().flat_map(|attr| { - attr.predicates().filter_map(|pred| match pred { - Predicate::Feature(feat) => Some(feat), - Predicate::Test => Some("test"), - _ => None, - }) - }) - }) - .collect::>(); - - 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 frame_system = generate_access_from_frame_or_crate("frame-system")?; - let block = quote!(<#name as #frame_system::Config>::Block); - let unchecked_extrinsic = quote!(<#block as #scrate::sp_runtime::traits::Block>::Extrinsic); - - let outer_event = - expand::expand_outer_enum(&name, &pallets, &scrate, expand::OuterEnumType::Event)?; - let outer_error = - expand::expand_outer_enum(&name, &pallets, &scrate, expand::OuterEnumType::Error)?; - - let outer_origin = expand::expand_outer_origin(&name, system_pallet, &pallets, &scrate)?; - let all_pallets = decl_all_pallets(&name, pallets.iter(), &features); - let pallet_to_index = decl_pallet_runtime_setup(&name, &pallets, &scrate); - - let dispatch = expand::expand_outer_dispatch(&name, system_pallet, &pallets, &scrate); - let tasks = expand::expand_outer_task(&name, &pallets, &scrate); - let metadata = expand::expand_runtime_metadata( - &name, - &pallets, - &scrate, - &unchecked_extrinsic, - &system_pallet.path, - ); - let outer_config = expand::expand_outer_config(&name, &pallets, &scrate); - let inherent = - expand::expand_outer_inherent(&name, &block, &unchecked_extrinsic, &pallets, &scrate); - let validate_unsigned = expand::expand_outer_validate_unsigned(&name, &pallets, &scrate); - let freeze_reason = expand::expand_outer_freeze_reason(&pallets, &scrate); - let hold_reason = expand::expand_outer_hold_reason(&pallets, &scrate); - let lock_id = expand::expand_outer_lock_id(&pallets, &scrate); - let slash_reason = expand::expand_outer_slash_reason(&pallets, &scrate); - let integrity_test = decl_integrity_test(&scrate); - let static_assertions = decl_static_assertions(&name, &pallets, &scrate); - - let warning = where_section.map(|where_section| { - proc_macro_warning::Warning::new_deprecated("WhereSection") - .old("use a `where` clause in `construct_runtime`") - .new( - "use `frame_system::Config` to set the `Block` type and delete this clause. + ) + })?; + if !system_pallet.cfg_pattern.is_empty() { + return Err(syn::Error::new( + system_pallet.name.span(), + "`System` pallet declaration is feature gated, please remove any `#[cfg]` attributes", + )) + } + + let features = pallets + .iter() + .filter_map(|decl| { + (!decl.cfg_pattern.is_empty()).then(|| { + decl.cfg_pattern.iter().flat_map(|attr| { + attr.predicates().filter_map(|pred| match pred { + Predicate::Feature(feat) => Some(feat), + Predicate::Test => Some("test"), + _ => None, + }) + }) + }) + }) + .flatten() + .collect::>(); + + 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 frame_system = generate_access_from_frame_or_crate("frame-system")?; + let block = quote!(<#name as #frame_system::Config>::Block); + let unchecked_extrinsic = quote!(<#block as #scrate::sp_runtime::traits::Block>::Extrinsic); + + let outer_event = + expand::expand_outer_enum(&name, &pallets, &scrate, expand::OuterEnumType::Event)?; + let outer_error = + expand::expand_outer_enum(&name, &pallets, &scrate, expand::OuterEnumType::Error)?; + + let outer_origin = expand::expand_outer_origin(&name, system_pallet, &pallets, &scrate)?; + let all_pallets = decl_all_pallets(&name, pallets.iter(), &features); + let pallet_to_index = decl_pallet_runtime_setup(&name, &pallets, &scrate); + + let dispatch = expand::expand_outer_dispatch(&name, system_pallet, &pallets, &scrate); + let tasks = expand::expand_outer_task(&name, &pallets, &scrate); + let metadata = expand::expand_runtime_metadata( + &name, + &pallets, + &scrate, + &unchecked_extrinsic, + &system_pallet.path, + ); + let outer_config = expand::expand_outer_config(&name, &pallets, &scrate); + let inherent = + expand::expand_outer_inherent(&name, &block, &unchecked_extrinsic, &pallets, &scrate); + let validate_unsigned = expand::expand_outer_validate_unsigned(&name, &pallets, &scrate); + let freeze_reason = expand::expand_outer_freeze_reason(&pallets, &scrate); + let hold_reason = expand::expand_outer_hold_reason(&pallets, &scrate); + let lock_id = expand::expand_outer_lock_id(&pallets, &scrate); + let slash_reason = expand::expand_outer_slash_reason(&pallets, &scrate); + let integrity_test = decl_integrity_test(&scrate); + let static_assertions = decl_static_assertions(&name, &pallets, &scrate); + + let warning = where_section.map_or(None, |where_section| { + Some( + proc_macro_warning::Warning::new_deprecated("WhereSection") + .old("use a `where` clause in `construct_runtime`") + .new( + "use `frame_system::Config` to set the `Block` type and delete this clause. It is planned to be removed in December 2023", - ) - .help_links(&["https://github.com/paritytech/substrate/pull/14437"]) - .span(where_section.span) - .build_or_panic() - }); - - let res = quote!( - #warning - - #scrate_decl - - // Prevent UncheckedExtrinsic to print unused warning. - const _: () = { - #[allow(unused)] - type __hidden_use_of_unchecked_extrinsic = #unchecked_extrinsic; - }; - - #[derive( - Clone, Copy, PartialEq, Eq, #scrate::sp_runtime::RuntimeDebug, - #scrate::__private::scale_info::TypeInfo - )] - pub struct #name; - impl #scrate::sp_runtime::traits::GetRuntimeBlockType for #name { - type RuntimeBlock = #block; - } - - // Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata. - // The function is implemented by calling `impl_runtime_apis!`. - // - // However, the `construct_runtime!` may be called without calling `impl_runtime_apis!`. - // Rely on the `Deref` trait to differentiate between a runtime that implements - // APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro construct_runtime!). - // - // Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function. - // `InternalConstructRuntime` is implemented by the `construct_runtime!` for Runtime references (`& Runtime`), - // while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`). - // - // Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!` - // when both macros are called; and will resolve an empty `runtime_metadata` when only the `construct_runtime!` - // is called. - - #[doc(hidden)] - trait InternalConstructRuntime { - #[inline(always)] - fn runtime_metadata(&self) -> #scrate::__private::sp_std::vec::Vec<#scrate::__private::metadata_ir::RuntimeApiMetadataIR> { - Default::default() - } - } - #[doc(hidden)] - impl InternalConstructRuntime for &#name {} + ) + .help_links(&["https://github.com/paritytech/substrate/pull/14437"]) + .span(where_section.span) + .build_or_panic(), + ) + }); + + let res = quote!( + #warning + + #scrate_decl + + // Prevent UncheckedExtrinsic to print unused warning. + const _: () = { + #[allow(unused)] + type __hidden_use_of_unchecked_extrinsic = #unchecked_extrinsic; + }; + + #[derive( + Clone, Copy, PartialEq, Eq, #scrate::sp_runtime::RuntimeDebug, + #scrate::__private::scale_info::TypeInfo + )] + pub struct #name; + impl #scrate::sp_runtime::traits::GetRuntimeBlockType for #name { + type RuntimeBlock = #block; + } + + // Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata. + // The function is implemented by calling `impl_runtime_apis!`. + // + // However, the `construct_runtime!` may be called without calling `impl_runtime_apis!`. + // Rely on the `Deref` trait to differentiate between a runtime that implements + // APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro construct_runtime!). + // + // Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function. + // `InternalConstructRuntime` is implemented by the `construct_runtime!` for Runtime references (`& Runtime`), + // while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`). + // + // Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!` + // when both macros are called; and will resolve an empty `runtime_metadata` when only the `construct_runtime!` + // is called. + + #[doc(hidden)] + trait InternalConstructRuntime { + #[inline(always)] + fn runtime_metadata(&self) -> #scrate::__private::sp_std::vec::Vec<#scrate::__private::metadata_ir::RuntimeApiMetadataIR> { + Default::default() + } + } + #[doc(hidden)] + impl InternalConstructRuntime for &#name {} - #outer_event + #outer_event - #outer_error + #outer_error - #outer_origin + #outer_origin - #all_pallets + #all_pallets - #pallet_to_index + #pallet_to_index - #dispatch + #dispatch - #tasks + #tasks - #metadata + #metadata - #outer_config + #outer_config - #inherent + #inherent - #validate_unsigned + #validate_unsigned - #freeze_reason + #freeze_reason - #hold_reason + #hold_reason - #lock_id + #lock_id - #slash_reason + #slash_reason - #integrity_test + #integrity_test - #static_assertions - ); + #static_assertions + ); - Ok(res) + Ok(res) } pub(crate) fn decl_all_pallets<'a>( - runtime: &'a Ident, - pallet_declarations: impl Iterator, - features: &HashSet<&str>, + runtime: &'a Ident, + pallet_declarations: impl Iterator, + features: &HashSet<&str>, ) -> TokenStream2 { - let mut types = TokenStream2::new(); - - // Every feature set to the pallet names that should be included by this feature set. - let mut features_to_names = features - .iter() - .copied() - .powerset() - .map(|feat| (HashSet::from_iter(feat), Vec::new())) - .collect::, Vec<_>)>>(); - - for pallet_declaration in pallet_declarations { - let type_name = &pallet_declaration.name; - let pallet = &pallet_declaration.path; - let mut generics = vec![quote!(#runtime)]; - generics.extend( - pallet_declaration - .instance - .iter() - .map(|name| quote!(#pallet::#name)), - ); - let mut attrs = Vec::new(); - for cfg in &pallet_declaration.cfg_pattern { - let feat = format!("#[cfg({})]\n", cfg.original()); - attrs.extend(TokenStream2::from_str(&feat).expect("was parsed successfully; qed")); - } - let type_decl = quote!( - #(#attrs)* - pub type #type_name = #pallet::Pallet <#(#generics),*>; - ); - types.extend(type_decl); - - if pallet_declaration.cfg_pattern.is_empty() { - for (_, names) in features_to_names.iter_mut() { - names.push(&pallet_declaration.name); - } - } else { - for (feature_set, names) in &mut features_to_names { - // Rust tidbit: if we have multiple `#[cfg]` feature on the same item, then the - // predicates listed in all `#[cfg]` attributes are effectively joined by `and()`, - // meaning that all of them must match in order to activate the item - let is_feature_active = pallet_declaration.cfg_pattern.iter().all(|expr| { - expr.eval(|pred| match pred { - Predicate::Feature(f) => feature_set.contains(f), - Predicate::Test => feature_set.contains(&"test"), - _ => false, - }) - }); - - if is_feature_active { - names.push(&pallet_declaration.name); - } - } - } - } - - // All possible features. This will be used below for the empty feature set. - let mut all_features = features_to_names - .iter() - .flat_map(|f| f.0.iter().cloned()) - .collect::>(); - let attribute_to_names = features_to_names + let mut types = TokenStream2::new(); + + // Every feature set to the pallet names that should be included by this feature set. + let mut features_to_names = features + .iter() + .map(|f| *f) + .powerset() + .map(|feat| (HashSet::from_iter(feat), Vec::new())) + .collect::, Vec<_>)>>(); + + for pallet_declaration in pallet_declarations { + let type_name = &pallet_declaration.name; + let pallet = &pallet_declaration.path; + let mut generics = vec![quote!(#runtime)]; + generics.extend(pallet_declaration.instance.iter().map(|name| quote!(#pallet::#name))); + let mut attrs = Vec::new(); + for cfg in &pallet_declaration.cfg_pattern { + let feat = format!("#[cfg({})]\n", cfg.original()); + attrs.extend(TokenStream2::from_str(&feat).expect("was parsed successfully; qed")); + } + let type_decl = quote!( + #(#attrs)* + pub type #type_name = #pallet::Pallet <#(#generics),*>; + ); + types.extend(type_decl); + + if pallet_declaration.cfg_pattern.is_empty() { + for (_, names) in features_to_names.iter_mut() { + names.push(&pallet_declaration.name); + } + } else { + for (feature_set, names) in &mut features_to_names { + // Rust tidbit: if we have multiple `#[cfg]` feature on the same item, then the + // predicates listed in all `#[cfg]` attributes are effectively joined by `and()`, + // meaning that all of them must match in order to activate the item + let is_feature_active = pallet_declaration.cfg_pattern.iter().all(|expr| { + expr.eval(|pred| match pred { + Predicate::Feature(f) => feature_set.contains(f), + Predicate::Test => feature_set.contains(&"test"), + _ => false, + }) + }); + + if is_feature_active { + names.push(&pallet_declaration.name); + } + } + } + } + + // All possible features. This will be used below for the empty feature set. + let mut all_features = features_to_names + .iter() + .flat_map(|f| f.0.iter().cloned()) + .collect::>(); + let attribute_to_names = features_to_names .into_iter() .map(|(mut features, names)| { // If this is the empty feature set, it needs to be changed to negate all available @@ -620,222 +598,212 @@ pub(crate) fn decl_all_pallets<'a>( }) .collect::>(); - let all_pallets_without_system = attribute_to_names.iter().map(|(attr, names)| { - let names = names.iter().filter(|n| **n != SYSTEM_PALLET_NAME); - quote! { - #attr - /// All pallets included in the runtime as a nested tuple of types. - /// Excludes the System pallet. - pub type AllPalletsWithoutSystem = ( #(#names,)* ); - } - }); - - let all_pallets_with_system = attribute_to_names.iter().map(|(attr, names)| { - quote! { - #attr - /// All pallets included in the runtime as a nested tuple of types. - pub type AllPalletsWithSystem = ( #(#names,)* ); - } - }); - - quote!( - #types - - #( #all_pallets_with_system )* - - #( #all_pallets_without_system )* - ) + let all_pallets_without_system = attribute_to_names.iter().map(|(attr, names)| { + let names = names.iter().filter(|n| **n != SYSTEM_PALLET_NAME); + quote! { + #attr + /// All pallets included in the runtime as a nested tuple of types. + /// Excludes the System pallet. + pub type AllPalletsWithoutSystem = ( #(#names,)* ); + } + }); + + let all_pallets_with_system = attribute_to_names.iter().map(|(attr, names)| { + quote! { + #attr + /// All pallets included in the runtime as a nested tuple of types. + pub type AllPalletsWithSystem = ( #(#names,)* ); + } + }); + + quote!( + #types + + #( #all_pallets_with_system )* + + #( #all_pallets_without_system )* + ) } pub(crate) fn decl_pallet_runtime_setup( - runtime: &Ident, - pallet_declarations: &[Pallet], - scrate: &TokenStream2, + runtime: &Ident, + pallet_declarations: &[Pallet], + scrate: &TokenStream2, ) -> TokenStream2 { - let names = pallet_declarations - .iter() - .map(|d| &d.name) - .collect::>(); - let name_strings = pallet_declarations.iter().map(|d| d.name.to_string()); - let name_hashes = pallet_declarations - .iter() - .map(|d| two128_str(&d.name.to_string())); - let module_names = pallet_declarations.iter().map(|d| d.path.module_name()); - let indices = pallet_declarations - .iter() - .map(|pallet| pallet.index as usize); - let pallet_structs = pallet_declarations - .iter() - .map(|pallet| { - let path = &pallet.path; - match pallet.instance.as_ref() { - Some(inst) => quote!(#path::Pallet<#runtime, #path::#inst>), - None => quote!(#path::Pallet<#runtime>), - } - }) - .collect::>(); - let pallet_attrs = pallet_declarations - .iter() - .map(|pallet| { - pallet - .cfg_pattern - .iter() - .fold(TokenStream2::new(), |acc, pattern| { - let attr = TokenStream2::from_str(&format!("#[cfg({})]", pattern.original())) - .expect("was successfully parsed before; qed"); - quote! { - #acc - #attr - } - }) - }) - .collect::>(); - - quote!( - /// Provides an implementation of `PalletInfo` to provide information - /// about the pallet setup in the runtime. - pub struct PalletInfo; - - impl #scrate::traits::PalletInfo for PalletInfo { - - fn index() -> Option { - let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); - #( - #pallet_attrs - if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { - return Some(#indices) - } - )* - - None - } - - fn name() -> Option<&'static str> { - let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); - #( - #pallet_attrs - if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { - return Some(#name_strings) - } - )* - - None - } - - fn name_hash() -> Option<[u8; 16]> { - let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); - #( - #pallet_attrs - if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { - return Some(#name_hashes) - } - )* - - None - } - - fn module_name() -> Option<&'static str> { - let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); - #( - #pallet_attrs - if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { - return Some(#module_names) - } - )* - - None - } - - fn crate_version() -> Option<#scrate::traits::CrateVersion> { - let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); - #( - #pallet_attrs - if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { - return Some( - <#pallet_structs as #scrate::traits::PalletInfoAccess>::crate_version() - ) - } - )* - - None - } - } - ) + let names = pallet_declarations.iter().map(|d| &d.name).collect::>(); + let name_strings = pallet_declarations.iter().map(|d| d.name.to_string()); + let name_hashes = pallet_declarations.iter().map(|d| two128_str(&d.name.to_string())); + let module_names = pallet_declarations.iter().map(|d| d.path.module_name()); + let indices = pallet_declarations.iter().map(|pallet| pallet.index as usize); + let pallet_structs = pallet_declarations + .iter() + .map(|pallet| { + let path = &pallet.path; + match pallet.instance.as_ref() { + Some(inst) => quote!(#path::Pallet<#runtime, #path::#inst>), + None => quote!(#path::Pallet<#runtime>), + } + }) + .collect::>(); + let pallet_attrs = pallet_declarations + .iter() + .map(|pallet| { + pallet.cfg_pattern.iter().fold(TokenStream2::new(), |acc, pattern| { + let attr = TokenStream2::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }) + }) + .collect::>(); + + quote!( + /// Provides an implementation of `PalletInfo` to provide information + /// about the pallet setup in the runtime. + pub struct PalletInfo; + + impl #scrate::traits::PalletInfo for PalletInfo { + + fn index() -> Option { + let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); + #( + #pallet_attrs + if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { + return Some(#indices) + } + )* + + None + } + + fn name() -> Option<&'static str> { + let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); + #( + #pallet_attrs + if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { + return Some(#name_strings) + } + )* + + None + } + + fn name_hash() -> Option<[u8; 16]> { + let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); + #( + #pallet_attrs + if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { + return Some(#name_hashes) + } + )* + + None + } + + fn module_name() -> Option<&'static str> { + let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); + #( + #pallet_attrs + if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { + return Some(#module_names) + } + )* + + None + } + + fn crate_version() -> Option<#scrate::traits::CrateVersion> { + let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); + #( + #pallet_attrs + if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { + return Some( + <#pallet_structs as #scrate::traits::PalletInfoAccess>::crate_version() + ) + } + )* + + None + } + } + ) } pub(crate) fn decl_integrity_test(scrate: &TokenStream2) -> TokenStream2 { - quote!( - #[cfg(test)] - mod __construct_runtime_integrity_test { - use super::*; - - #[test] - pub fn runtime_integrity_tests() { - #scrate::__private::sp_tracing::try_init_simple(); - ::integrity_test(); - } - } - ) + quote!( + #[cfg(test)] + mod __construct_runtime_integrity_test { + use super::*; + + #[test] + pub fn runtime_integrity_tests() { + #scrate::__private::sp_tracing::try_init_simple(); + ::integrity_test(); + } + } + ) } pub(crate) fn decl_static_assertions( - runtime: &Ident, - pallet_decls: &[Pallet], - scrate: &TokenStream2, + runtime: &Ident, + pallet_decls: &[Pallet], + scrate: &TokenStream2, ) -> TokenStream2 { - let error_encoded_size_check = pallet_decls.iter().map(|decl| { - let path = &decl.path; - let assert_message = format!( - "The maximum encoded size of the error type in the `{}` pallet exceeds \ + let error_encoded_size_check = pallet_decls.iter().map(|decl| { + let path = &decl.path; + let assert_message = format!( + "The maximum encoded size of the error type in the `{}` pallet exceeds \ `MAX_MODULE_ERROR_ENCODED_SIZE`", - decl.name, - ); - - quote! { - #scrate::__private::tt_call! { - macro = [{ #path::tt_error_token }] - your_tt_return = [{ #scrate::__private::tt_return }] - ~~> #scrate::assert_error_encoded_size! { - path = [{ #path }] - runtime = [{ #runtime }] - assert_message = [{ #assert_message }] - } - } - } - }); - - quote! { - #(#error_encoded_size_check)* - } + decl.name, + ); + + quote! { + #scrate::__private::tt_call! { + macro = [{ #path::tt_error_token }] + your_tt_return = [{ #scrate::__private::tt_return }] + ~~> #scrate::assert_error_encoded_size! { + path = [{ #path }] + runtime = [{ #runtime }] + assert_message = [{ #assert_message }] + } + } + } + }); + + quote! { + #(#error_encoded_size_check)* + } } pub(crate) fn check_pallet_number(input: TokenStream2, pallet_num: usize) -> Result<()> { - let max_pallet_num = { - if cfg!(feature = "tuples-96") { - 96 - } else if cfg!(feature = "tuples-128") { - 128 - } else { - 64 - } - }; - - if pallet_num > max_pallet_num { - let no_feature = max_pallet_num == 128; - return Err(syn::Error::new( - input.span(), - format!( - "{} To increase this limit, enable the tuples-{} feature of [frame_support]. {}", - "The number of pallets exceeds the maximum number of tuple elements.", - max_pallet_num + 32, - if no_feature { - "If the feature does not exist - it needs to be implemented." - } else { - "" - }, - ), - )); - } - - Ok(()) + let max_pallet_num = { + if cfg!(feature = "tuples-96") { + 96 + } else if cfg!(feature = "tuples-128") { + 128 + } else { + 64 + } + }; + + if pallet_num > max_pallet_num { + let no_feature = max_pallet_num == 128; + return Err(syn::Error::new( + input.span(), + format!( + "{} To increase this limit, enable the tuples-{} feature of [frame_support]. {}", + "The number of pallets exceeds the maximum number of tuple elements.", + max_pallet_num + 32, + if no_feature { + "If the feature does not exist - it needs to be implemented." + } else { + "" + }, + ), + )) + } + + Ok(()) } diff --git a/support/procedural-fork/src/construct_runtime/parse.rs b/support/procedural-fork/src/construct_runtime/parse.rs index 26fbb4dee..31866c787 100644 --- a/support/procedural-fork/src/construct_runtime/parse.rs +++ b/support/procedural-fork/src/construct_runtime/parse.rs @@ -20,34 +20,34 @@ use proc_macro2::{Span, TokenStream}; use quote::ToTokens; use std::collections::{HashMap, HashSet}; use syn::{ - ext::IdentExt, - parse::{Parse, ParseStream}, - punctuated::Punctuated, - spanned::Spanned, - token, Attribute, Error, Ident, Path, Result, Token, + ext::IdentExt, + parse::{Parse, ParseStream}, + punctuated::Punctuated, + spanned::Spanned, + token, Attribute, Error, Ident, Path, Result, Token, }; mod keyword { - syn::custom_keyword!(Block); - syn::custom_keyword!(NodeBlock); - syn::custom_keyword!(UncheckedExtrinsic); - syn::custom_keyword!(Pallet); - syn::custom_keyword!(Call); - syn::custom_keyword!(Storage); - syn::custom_keyword!(Event); - syn::custom_keyword!(Error); - syn::custom_keyword!(Config); - syn::custom_keyword!(Origin); - syn::custom_keyword!(Inherent); - syn::custom_keyword!(ValidateUnsigned); - syn::custom_keyword!(FreezeReason); - syn::custom_keyword!(HoldReason); - syn::custom_keyword!(Task); - syn::custom_keyword!(LockId); - syn::custom_keyword!(SlashReason); - syn::custom_keyword!(exclude_parts); - syn::custom_keyword!(use_parts); - syn::custom_keyword!(expanded); + syn::custom_keyword!(Block); + syn::custom_keyword!(NodeBlock); + syn::custom_keyword!(UncheckedExtrinsic); + syn::custom_keyword!(Pallet); + syn::custom_keyword!(Call); + syn::custom_keyword!(Storage); + syn::custom_keyword!(Event); + syn::custom_keyword!(Error); + syn::custom_keyword!(Config); + syn::custom_keyword!(Origin); + syn::custom_keyword!(Inherent); + syn::custom_keyword!(ValidateUnsigned); + syn::custom_keyword!(FreezeReason); + syn::custom_keyword!(HoldReason); + syn::custom_keyword!(Task); + syn::custom_keyword!(LockId); + syn::custom_keyword!(SlashReason); + syn::custom_keyword!(exclude_parts); + syn::custom_keyword!(use_parts); + syn::custom_keyword!(expanded); } /// Declaration of a runtime. @@ -57,298 +57,266 @@ mod keyword { /// implicit. #[derive(Debug)] pub enum RuntimeDeclaration { - Implicit(ImplicitRuntimeDeclaration), - Explicit(ExplicitRuntimeDeclaration), - ExplicitExpanded(ExplicitRuntimeDeclaration), + Implicit(ImplicitRuntimeDeclaration), + Explicit(ExplicitRuntimeDeclaration), + ExplicitExpanded(ExplicitRuntimeDeclaration), } /// Declaration of a runtime with some pallet with implicit declaration of parts. #[derive(Debug)] pub struct ImplicitRuntimeDeclaration { - pub name: Ident, - pub where_section: Option, - pub pallets: Vec, + pub name: Ident, + pub where_section: Option, + pub pallets: Vec, } /// Declaration of a runtime with all pallet having explicit declaration of parts. #[derive(Debug)] pub struct ExplicitRuntimeDeclaration { - pub name: Ident, - pub where_section: Option, - pub pallets: Vec, - pub pallets_token: token::Brace, + pub name: Ident, + pub where_section: Option, + pub pallets: Vec, + pub pallets_token: token::Brace, } impl Parse for RuntimeDeclaration { - fn parse(input: ParseStream) -> Result { - input.parse::()?; - - // Support either `enum` or `struct`. - if input.peek(Token![struct]) { - input.parse::()?; - } else { - input.parse::()?; - } - - let name = input.parse::()?; - let where_section = if input.peek(token::Where) { - Some(input.parse()?) - } else { - None - }; - let pallets = - input.parse::>>()?; - let pallets_token = pallets.token; - - match convert_pallets(pallets.content.inner.into_iter().collect())? { - PalletsConversion::Implicit(pallets) => { - Ok(RuntimeDeclaration::Implicit(ImplicitRuntimeDeclaration { - name, - where_section, - pallets, - })) - } - PalletsConversion::Explicit(pallets) => { - Ok(RuntimeDeclaration::Explicit(ExplicitRuntimeDeclaration { - name, - where_section, - pallets, - pallets_token, - })) - } - PalletsConversion::ExplicitExpanded(pallets) => Ok( - RuntimeDeclaration::ExplicitExpanded(ExplicitRuntimeDeclaration { - name, - where_section, - pallets, - pallets_token, - }), - ), - } - } + fn parse(input: ParseStream) -> Result { + input.parse::()?; + + // Support either `enum` or `struct`. + if input.peek(Token![struct]) { + input.parse::()?; + } else { + input.parse::()?; + } + + let name = input.parse::()?; + let where_section = if input.peek(token::Where) { Some(input.parse()?) } else { None }; + let pallets = + input.parse::>>()?; + let pallets_token = pallets.token; + + match convert_pallets(pallets.content.inner.into_iter().collect())? { + PalletsConversion::Implicit(pallets) => + Ok(RuntimeDeclaration::Implicit(ImplicitRuntimeDeclaration { + name, + where_section, + pallets, + })), + PalletsConversion::Explicit(pallets) => + Ok(RuntimeDeclaration::Explicit(ExplicitRuntimeDeclaration { + name, + where_section, + pallets, + pallets_token, + })), + PalletsConversion::ExplicitExpanded(pallets) => + Ok(RuntimeDeclaration::ExplicitExpanded(ExplicitRuntimeDeclaration { + name, + where_section, + pallets, + pallets_token, + })), + } + } } #[derive(Debug)] pub struct WhereSection { - pub span: Span, - pub block: syn::TypePath, - pub node_block: syn::TypePath, - pub unchecked_extrinsic: syn::TypePath, + pub span: Span, + 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 declaration for `{:?}`.", - kind, kind - ); - return Err(Error::new(*kind_span, msg)); - } - Ok(Self { - span: input.span(), - block, - node_block, - unchecked_extrinsic, - }) - } + 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 declaration for `{:?}`.", + kind, kind + ); + return Err(Error::new(*kind_span, msg)) + } + Ok(Self { span: input.span(), block, node_block, unchecked_extrinsic }) + } } #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] pub enum WhereKind { - Block, - NodeBlock, - UncheckedExtrinsic, + Block, + NodeBlock, + UncheckedExtrinsic, } #[derive(Debug)] pub struct WhereDefinition { - pub kind_span: Span, - pub kind: WhereKind, - pub value: syn::TypePath, + 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()? - }, - }) - } + 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()? + }, + }) + } } /// The declaration of a pallet. #[derive(Debug, Clone)] pub struct PalletDeclaration { - /// Is this pallet fully expanded? - pub is_expanded: bool, - /// The name of the pallet, e.g.`System` in `System: frame_system`. - pub name: Ident, - /// Optional attributes tagged right above a pallet declaration. - pub attrs: Vec, - /// Optional fixed index, e.g. `MyPallet ... = 3,`. - pub index: Option, - /// The path of the pallet, e.g. `frame_system` in `System: frame_system`. - pub path: PalletPath, - /// The instance of the pallet, e.g. `Instance1` in `Council: pallet_collective::`. - pub instance: Option, - /// The declared pallet parts, - /// e.g. `Some([Pallet, Call])` for `System: system::{Pallet, Call}` - /// or `None` for `System: system`. - pub pallet_parts: Option>, - /// The specified parts, either use_parts or exclude_parts. - pub specified_parts: SpecifiedParts, + /// Is this pallet fully expanded? + pub is_expanded: bool, + /// The name of the pallet, e.g.`System` in `System: frame_system`. + pub name: Ident, + /// Optional attributes tagged right above a pallet declaration. + pub attrs: Vec, + /// Optional fixed index, e.g. `MyPallet ... = 3,`. + pub index: Option, + /// The path of the pallet, e.g. `frame_system` in `System: frame_system`. + pub path: PalletPath, + /// The instance of the pallet, e.g. `Instance1` in `Council: pallet_collective::`. + pub instance: Option, + /// The declared pallet parts, + /// e.g. `Some([Pallet, Call])` for `System: system::{Pallet, Call}` + /// or `None` for `System: system`. + pub pallet_parts: Option>, + /// The specified parts, either use_parts or exclude_parts. + pub specified_parts: SpecifiedParts, } /// The possible declaration of pallet parts to use. #[derive(Debug, Clone)] pub enum SpecifiedParts { - /// Use all the pallet parts except those specified. - Exclude(Vec), - /// Use only the specified pallet parts. - Use(Vec), - /// Use the all the pallet parts. - All, + /// Use all the pallet parts except those specified. + Exclude(Vec), + /// Use only the specified pallet parts. + Use(Vec), + /// Use the all the pallet parts. + All, } impl Parse for PalletDeclaration { - fn parse(input: ParseStream) -> Result { - let attrs = input.call(Attribute::parse_outer)?; - - let name = input.parse()?; - let _: Token![:] = input.parse()?; - let path = input.parse()?; - - // Parse for instance. - 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 if !(input.peek(Token![::]) && input.peek3(token::Brace)) - && !input.peek(keyword::expanded) - && !input.peek(keyword::exclude_parts) - && !input.peek(keyword::use_parts) - && !input.peek(Token![=]) - && !input.peek(Token![,]) - && !input.is_empty() - { - return Err(input.error( + fn parse(input: ParseStream) -> Result { + let attrs = input.call(Attribute::parse_outer)?; + + let name = input.parse()?; + let _: Token![:] = input.parse()?; + let path = input.parse()?; + + // Parse for instance. + 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 if !(input.peek(Token![::]) && input.peek3(token::Brace)) && + !input.peek(keyword::expanded) && + !input.peek(keyword::exclude_parts) && + !input.peek(keyword::use_parts) && + !input.peek(Token![=]) && + !input.peek(Token![,]) && + !input.is_empty() + { + return Err(input.error( "Unexpected tokens, expected one of `::$ident` `::{`, `exclude_parts`, `use_parts`, `=`, `,`", )); - } else { - None - }; - - // Check if the pallet is fully expanded. - let (is_expanded, extra_parts) = if input.peek(keyword::expanded) { - let _: keyword::expanded = input.parse()?; - let _: Token![::] = input.parse()?; - (true, parse_pallet_parts(input)?) - } else { - (false, vec![]) - }; - - // Parse for explicit parts - let pallet_parts = if input.peek(Token![::]) && input.peek3(token::Brace) { - let _: Token![::] = input.parse()?; - let mut parts = parse_pallet_parts(input)?; - parts.extend(extra_parts); - Some(parts) - } else if !input.peek(keyword::exclude_parts) - && !input.peek(keyword::use_parts) - && !input.peek(Token![=]) - && !input.peek(Token![,]) - && !input.is_empty() - { - return Err(input.error( - "Unexpected tokens, expected one of `::{`, `exclude_parts`, `use_parts`, `=`, `,`", - )); - } else { - is_expanded.then_some(extra_parts) - }; - - // Parse for specified parts - let specified_parts = if input.peek(keyword::exclude_parts) { - let _: keyword::exclude_parts = input.parse()?; - SpecifiedParts::Exclude(parse_pallet_parts_no_generic(input)?) - } else if input.peek(keyword::use_parts) { - let _: keyword::use_parts = input.parse()?; - SpecifiedParts::Use(parse_pallet_parts_no_generic(input)?) - } else if !input.peek(Token![=]) && !input.peek(Token![,]) && !input.is_empty() { - return Err(input.error("Unexpected tokens, expected one of `exclude_parts`, `=`, `,`")); - } else { - SpecifiedParts::All - }; - - // Parse for pallet index - let index = if input.peek(Token![=]) { - input.parse::()?; - let index = input.parse::()?; - let index = index.base10_parse::()?; - Some(index) - } else if !input.peek(Token![,]) && !input.is_empty() { - return Err(input.error("Unexpected tokens, expected one of `=`, `,`")); - } else { - None - }; - - Ok(Self { - is_expanded, - attrs, - name, - path, - instance, - pallet_parts, - specified_parts, - index, - }) - } + } else { + None + }; + + // Check if the pallet is fully expanded. + let (is_expanded, extra_parts) = if input.peek(keyword::expanded) { + let _: keyword::expanded = input.parse()?; + let _: Token![::] = input.parse()?; + (true, parse_pallet_parts(input)?) + } else { + (false, vec![]) + }; + + // Parse for explicit parts + let pallet_parts = if input.peek(Token![::]) && input.peek3(token::Brace) { + let _: Token![::] = input.parse()?; + let mut parts = parse_pallet_parts(input)?; + parts.extend(extra_parts.into_iter()); + Some(parts) + } else if !input.peek(keyword::exclude_parts) && + !input.peek(keyword::use_parts) && + !input.peek(Token![=]) && + !input.peek(Token![,]) && + !input.is_empty() + { + return Err(input.error( + "Unexpected tokens, expected one of `::{`, `exclude_parts`, `use_parts`, `=`, `,`", + )) + } else { + is_expanded.then_some(extra_parts) + }; + + // Parse for specified parts + let specified_parts = if input.peek(keyword::exclude_parts) { + let _: keyword::exclude_parts = input.parse()?; + SpecifiedParts::Exclude(parse_pallet_parts_no_generic(input)?) + } else if input.peek(keyword::use_parts) { + let _: keyword::use_parts = input.parse()?; + SpecifiedParts::Use(parse_pallet_parts_no_generic(input)?) + } else if !input.peek(Token![=]) && !input.peek(Token![,]) && !input.is_empty() { + return Err(input.error("Unexpected tokens, expected one of `exclude_parts`, `=`, `,`")) + } else { + SpecifiedParts::All + }; + + // Parse for pallet index + let index = if input.peek(Token![=]) { + input.parse::()?; + let index = input.parse::()?; + let index = index.base10_parse::()?; + Some(index) + } else if !input.peek(Token![,]) && !input.is_empty() { + return Err(input.error("Unexpected tokens, expected one of `=`, `,`")) + } else { + None + }; + + Ok(Self { is_expanded, attrs, name, path, instance, pallet_parts, specified_parts, index }) + } } /// A struct representing a path to a pallet. `PalletPath` is almost identical to the standard @@ -357,312 +325,303 @@ impl Parse for PalletDeclaration { /// - Path segments can only consist of identifiers separated by colons #[derive(Debug, Clone)] pub struct PalletPath { - pub inner: Path, + pub inner: Path, } impl PalletPath { - pub fn module_name(&self) -> String { - self.inner - .segments - .iter() - .fold(String::new(), |mut acc, segment| { - if !acc.is_empty() { - acc.push_str("::"); - } - acc.push_str(&segment.ident.to_string()); - acc - }) - } + pub fn module_name(&self) -> String { + self.inner.segments.iter().fold(String::new(), |mut acc, segment| { + if !acc.is_empty() { + acc.push_str("::"); + } + acc.push_str(&segment.ident.to_string()); + acc + }) + } } impl Parse for PalletPath { - fn parse(input: ParseStream) -> Result { - let mut res = PalletPath { - inner: Path { - leading_colon: None, - segments: Punctuated::new(), - }, - }; - - let lookahead = input.lookahead1(); - if lookahead.peek(Token![crate]) - || lookahead.peek(Token![self]) - || lookahead.peek(Token![super]) - || lookahead.peek(Ident) - { - let ident = input.call(Ident::parse_any)?; - res.inner.segments.push(ident.into()); - } else { - return Err(lookahead.error()); - } - - while input.peek(Token![::]) && input.peek3(Ident) { - input.parse::()?; - let ident = input.parse::()?; - res.inner.segments.push(ident.into()); - } - Ok(res) - } + fn parse(input: ParseStream) -> Result { + let mut res = + PalletPath { inner: Path { leading_colon: None, segments: Punctuated::new() } }; + + let lookahead = input.lookahead1(); + if lookahead.peek(Token![crate]) || + lookahead.peek(Token![self]) || + lookahead.peek(Token![super]) || + lookahead.peek(Ident) + { + let ident = input.call(Ident::parse_any)?; + res.inner.segments.push(ident.into()); + } else { + return Err(lookahead.error()) + } + + while input.peek(Token![::]) && input.peek3(Ident) { + input.parse::()?; + let ident = input.parse::()?; + res.inner.segments.push(ident.into()); + } + Ok(res) + } } impl quote::ToTokens for PalletPath { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.inner.to_tokens(tokens); - } + fn to_tokens(&self, tokens: &mut TokenStream) { + self.inner.to_tokens(tokens); + } } /// Parse [`PalletPart`]'s from a braces enclosed list that is split by commas, e.g. /// /// `{ Call, Event }` fn parse_pallet_parts(input: ParseStream) -> Result> { - let pallet_parts: ext::Braces> = input.parse()?; + let pallet_parts: ext::Braces> = input.parse()?; - let mut resolved = HashSet::new(); - for part in pallet_parts.content.inner.iter() { - if !resolved.insert(part.name()) { - let msg = format!( - "`{}` was already declared before. Please remove the duplicate declaration", - part.name(), - ); - return Err(Error::new(part.keyword.span(), msg)); - } - } + let mut resolved = HashSet::new(); + for part in pallet_parts.content.inner.iter() { + if !resolved.insert(part.name()) { + let msg = format!( + "`{}` was already declared before. Please remove the duplicate declaration", + part.name(), + ); + return Err(Error::new(part.keyword.span(), msg)) + } + } - Ok(pallet_parts.content.inner.into_iter().collect()) + Ok(pallet_parts.content.inner.into_iter().collect()) } #[derive(Debug, Clone)] pub enum PalletPartKeyword { - Pallet(keyword::Pallet), - Call(keyword::Call), - Storage(keyword::Storage), - Event(keyword::Event), - Error(keyword::Error), - Config(keyword::Config), - Origin(keyword::Origin), - Inherent(keyword::Inherent), - ValidateUnsigned(keyword::ValidateUnsigned), - FreezeReason(keyword::FreezeReason), - HoldReason(keyword::HoldReason), - Task(keyword::Task), - LockId(keyword::LockId), - SlashReason(keyword::SlashReason), + Pallet(keyword::Pallet), + Call(keyword::Call), + Storage(keyword::Storage), + Event(keyword::Event), + Error(keyword::Error), + Config(keyword::Config), + Origin(keyword::Origin), + Inherent(keyword::Inherent), + ValidateUnsigned(keyword::ValidateUnsigned), + FreezeReason(keyword::FreezeReason), + HoldReason(keyword::HoldReason), + Task(keyword::Task), + LockId(keyword::LockId), + SlashReason(keyword::SlashReason), } impl Parse for PalletPartKeyword { - fn parse(input: ParseStream) -> Result { - let lookahead = input.lookahead1(); - - if lookahead.peek(keyword::Pallet) { - Ok(Self::Pallet(input.parse()?)) - } else if lookahead.peek(keyword::Call) { - Ok(Self::Call(input.parse()?)) - } else if lookahead.peek(keyword::Storage) { - Ok(Self::Storage(input.parse()?)) - } else if lookahead.peek(keyword::Event) { - Ok(Self::Event(input.parse()?)) - } else if lookahead.peek(keyword::Error) { - Ok(Self::Error(input.parse()?)) - } else if lookahead.peek(keyword::Config) { - Ok(Self::Config(input.parse()?)) - } else if lookahead.peek(keyword::Origin) { - Ok(Self::Origin(input.parse()?)) - } else if lookahead.peek(keyword::Inherent) { - Ok(Self::Inherent(input.parse()?)) - } else if lookahead.peek(keyword::ValidateUnsigned) { - Ok(Self::ValidateUnsigned(input.parse()?)) - } else if lookahead.peek(keyword::FreezeReason) { - Ok(Self::FreezeReason(input.parse()?)) - } else if lookahead.peek(keyword::HoldReason) { - Ok(Self::HoldReason(input.parse()?)) - } else if lookahead.peek(keyword::Task) { - Ok(Self::Task(input.parse()?)) - } else if lookahead.peek(keyword::LockId) { - Ok(Self::LockId(input.parse()?)) - } else if lookahead.peek(keyword::SlashReason) { - Ok(Self::SlashReason(input.parse()?)) - } else { - Err(lookahead.error()) - } - } + fn parse(input: ParseStream) -> Result { + let lookahead = input.lookahead1(); + + if lookahead.peek(keyword::Pallet) { + Ok(Self::Pallet(input.parse()?)) + } else if lookahead.peek(keyword::Call) { + Ok(Self::Call(input.parse()?)) + } else if lookahead.peek(keyword::Storage) { + Ok(Self::Storage(input.parse()?)) + } else if lookahead.peek(keyword::Event) { + Ok(Self::Event(input.parse()?)) + } else if lookahead.peek(keyword::Error) { + Ok(Self::Error(input.parse()?)) + } else if lookahead.peek(keyword::Config) { + Ok(Self::Config(input.parse()?)) + } else if lookahead.peek(keyword::Origin) { + Ok(Self::Origin(input.parse()?)) + } else if lookahead.peek(keyword::Inherent) { + Ok(Self::Inherent(input.parse()?)) + } else if lookahead.peek(keyword::ValidateUnsigned) { + Ok(Self::ValidateUnsigned(input.parse()?)) + } else if lookahead.peek(keyword::FreezeReason) { + Ok(Self::FreezeReason(input.parse()?)) + } else if lookahead.peek(keyword::HoldReason) { + Ok(Self::HoldReason(input.parse()?)) + } else if lookahead.peek(keyword::Task) { + Ok(Self::Task(input.parse()?)) + } else if lookahead.peek(keyword::LockId) { + Ok(Self::LockId(input.parse()?)) + } else if lookahead.peek(keyword::SlashReason) { + Ok(Self::SlashReason(input.parse()?)) + } else { + Err(lookahead.error()) + } + } } impl PalletPartKeyword { - /// Returns the name of `Self`. - fn name(&self) -> &'static str { - match self { - Self::Pallet(_) => "Pallet", - Self::Call(_) => "Call", - Self::Storage(_) => "Storage", - Self::Event(_) => "Event", - Self::Error(_) => "Error", - Self::Config(_) => "Config", - Self::Origin(_) => "Origin", - Self::Inherent(_) => "Inherent", - Self::ValidateUnsigned(_) => "ValidateUnsigned", - Self::FreezeReason(_) => "FreezeReason", - Self::HoldReason(_) => "HoldReason", - Self::Task(_) => "Task", - Self::LockId(_) => "LockId", - Self::SlashReason(_) => "SlashReason", - } - } - - /// Returns `true` if this pallet part is allowed to have generic arguments. - fn allows_generic(&self) -> bool { - Self::all_generic_arg().iter().any(|n| *n == self.name()) - } - - /// Returns the names of all pallet parts that allow to have a generic argument. - fn all_generic_arg() -> &'static [&'static str] { - &["Event", "Error", "Origin", "Config", "Task"] - } + /// Returns the name of `Self`. + fn name(&self) -> &'static str { + match self { + Self::Pallet(_) => "Pallet", + Self::Call(_) => "Call", + Self::Storage(_) => "Storage", + Self::Event(_) => "Event", + Self::Error(_) => "Error", + Self::Config(_) => "Config", + Self::Origin(_) => "Origin", + Self::Inherent(_) => "Inherent", + Self::ValidateUnsigned(_) => "ValidateUnsigned", + Self::FreezeReason(_) => "FreezeReason", + Self::HoldReason(_) => "HoldReason", + Self::Task(_) => "Task", + Self::LockId(_) => "LockId", + Self::SlashReason(_) => "SlashReason", + } + } + + /// Returns `true` if this pallet part is allowed to have generic arguments. + fn allows_generic(&self) -> bool { + Self::all_generic_arg().iter().any(|n| *n == self.name()) + } + + /// Returns the names of all pallet parts that allow to have a generic argument. + fn all_generic_arg() -> &'static [&'static str] { + &["Event", "Error", "Origin", "Config", "Task"] + } } impl ToTokens for PalletPartKeyword { - fn to_tokens(&self, tokens: &mut TokenStream) { - match self { - Self::Pallet(inner) => inner.to_tokens(tokens), - Self::Call(inner) => inner.to_tokens(tokens), - Self::Storage(inner) => inner.to_tokens(tokens), - Self::Event(inner) => inner.to_tokens(tokens), - Self::Error(inner) => inner.to_tokens(tokens), - Self::Config(inner) => inner.to_tokens(tokens), - Self::Origin(inner) => inner.to_tokens(tokens), - Self::Inherent(inner) => inner.to_tokens(tokens), - Self::ValidateUnsigned(inner) => inner.to_tokens(tokens), - Self::FreezeReason(inner) => inner.to_tokens(tokens), - Self::HoldReason(inner) => inner.to_tokens(tokens), - Self::Task(inner) => inner.to_tokens(tokens), - Self::LockId(inner) => inner.to_tokens(tokens), - Self::SlashReason(inner) => inner.to_tokens(tokens), - } - } + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + Self::Pallet(inner) => inner.to_tokens(tokens), + Self::Call(inner) => inner.to_tokens(tokens), + Self::Storage(inner) => inner.to_tokens(tokens), + Self::Event(inner) => inner.to_tokens(tokens), + Self::Error(inner) => inner.to_tokens(tokens), + Self::Config(inner) => inner.to_tokens(tokens), + Self::Origin(inner) => inner.to_tokens(tokens), + Self::Inherent(inner) => inner.to_tokens(tokens), + Self::ValidateUnsigned(inner) => inner.to_tokens(tokens), + Self::FreezeReason(inner) => inner.to_tokens(tokens), + Self::HoldReason(inner) => inner.to_tokens(tokens), + Self::Task(inner) => inner.to_tokens(tokens), + Self::LockId(inner) => inner.to_tokens(tokens), + Self::SlashReason(inner) => inner.to_tokens(tokens), + } + } } #[derive(Debug, Clone)] pub struct PalletPart { - pub keyword: PalletPartKeyword, - pub generics: syn::Generics, + pub keyword: PalletPartKeyword, + pub generics: syn::Generics, } impl Parse for PalletPart { - fn parse(input: ParseStream) -> Result { - let keyword: PalletPartKeyword = input.parse()?; - - let generics: syn::Generics = input.parse()?; - if !generics.params.is_empty() && !keyword.allows_generic() { - let valid_generics = PalletPart::format_names(PalletPartKeyword::all_generic_arg()); - let msg = format!( - "`{}` is not allowed to have generics. \ + fn parse(input: ParseStream) -> Result { + let keyword: PalletPartKeyword = input.parse()?; + + let generics: syn::Generics = input.parse()?; + if !generics.params.is_empty() && !keyword.allows_generic() { + let valid_generics = PalletPart::format_names(PalletPartKeyword::all_generic_arg()); + let msg = format!( + "`{}` is not allowed to have generics. \ Only the following pallets are allowed to have generics: {}.", - keyword.name(), - valid_generics, - ); - return Err(syn::Error::new(keyword.span(), msg)); - } + keyword.name(), + valid_generics, + ); + return Err(syn::Error::new(keyword.span(), msg)) + } - Ok(Self { keyword, generics }) - } + Ok(Self { keyword, generics }) + } } impl PalletPart { - pub fn format_names(names: &[&'static str]) -> String { - let res: Vec<_> = names.iter().map(|s| format!("`{}`", s)).collect(); - res.join(", ") - } + pub fn format_names(names: &[&'static str]) -> String { + let res: Vec<_> = names.iter().map(|s| format!("`{}`", s)).collect(); + res.join(", ") + } - /// The name of this pallet part. - pub fn name(&self) -> &'static str { - self.keyword.name() - } + /// The name of this pallet part. + pub fn name(&self) -> &'static str { + self.keyword.name() + } } fn remove_kind( - input: ParseStream, - kind: WhereKind, - definitions: &mut Vec, + 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)) - } + 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)) + } } /// The declaration of a part without its generics #[derive(Debug, Clone)] pub struct PalletPartNoGeneric { - keyword: PalletPartKeyword, + keyword: PalletPartKeyword, } impl Parse for PalletPartNoGeneric { - fn parse(input: ParseStream) -> Result { - Ok(Self { - keyword: input.parse()?, - }) - } + fn parse(input: ParseStream) -> Result { + Ok(Self { keyword: input.parse()? }) + } } /// Parse [`PalletPartNoGeneric`]'s from a braces enclosed list that is split by commas, e.g. /// /// `{ Call, Event }` fn parse_pallet_parts_no_generic(input: ParseStream) -> Result> { - let pallet_parts: ext::Braces> = - input.parse()?; + let pallet_parts: ext::Braces> = + input.parse()?; - let mut resolved = HashSet::new(); - for part in pallet_parts.content.inner.iter() { - if !resolved.insert(part.keyword.name()) { - let msg = format!( - "`{}` was already declared before. Please remove the duplicate declaration", - part.keyword.name(), - ); - return Err(Error::new(part.keyword.span(), msg)); - } - } + let mut resolved = HashSet::new(); + for part in pallet_parts.content.inner.iter() { + if !resolved.insert(part.keyword.name()) { + let msg = format!( + "`{}` was already declared before. Please remove the duplicate declaration", + part.keyword.name(), + ); + return Err(Error::new(part.keyword.span(), msg)) + } + } - Ok(pallet_parts.content.inner.into_iter().collect()) + Ok(pallet_parts.content.inner.into_iter().collect()) } /// The final definition of a pallet with the resulting fixed index and explicit parts. #[derive(Debug, Clone)] pub struct Pallet { - /// Is this pallet fully expanded? - pub is_expanded: bool, - /// The name of the pallet, e.g.`System` in `System: frame_system`. - pub name: Ident, - /// Either automatically inferred, or defined (e.g. `MyPallet ... = 3,`). - pub index: u8, - /// The path of the pallet, e.g. `frame_system` in `System: frame_system`. - pub path: PalletPath, - /// The instance of the pallet, e.g. `Instance1` in `Council: pallet_collective::`. - pub instance: Option, - /// The pallet parts to use for the pallet. - pub pallet_parts: Vec, - /// Expressions specified inside of a #[cfg] attribute. - pub cfg_pattern: Vec, + /// Is this pallet fully expanded? + pub is_expanded: bool, + /// The name of the pallet, e.g.`System` in `System: frame_system`. + pub name: Ident, + /// Either automatically inferred, or defined (e.g. `MyPallet ... = 3,`). + pub index: u8, + /// The path of the pallet, e.g. `frame_system` in `System: frame_system`. + pub path: PalletPath, + /// The instance of the pallet, e.g. `Instance1` in `Council: pallet_collective::`. + pub instance: Option, + /// The pallet parts to use for the pallet. + pub pallet_parts: Vec, + /// Expressions specified inside of a #[cfg] attribute. + pub cfg_pattern: Vec, } impl Pallet { - /// Get resolved pallet parts - pub fn pallet_parts(&self) -> &[PalletPart] { - &self.pallet_parts - } + /// Get resolved pallet parts + pub fn pallet_parts(&self) -> &[PalletPart] { + &self.pallet_parts + } - /// Find matching parts - pub fn find_part(&self, name: &str) -> Option<&PalletPart> { - self.pallet_parts.iter().find(|part| part.name() == name) - } + /// Find matching parts + pub fn find_part(&self, name: &str) -> Option<&PalletPart> { + self.pallet_parts.iter().find(|part| part.name() == name) + } - /// Return whether pallet contains part - pub fn exists_part(&self, name: &str) -> bool { - self.find_part(name).is_some() - } + /// Return whether pallet contains part + pub fn exists_part(&self, name: &str) -> bool { + self.find_part(name).is_some() + } } /// Result of a conversion of a declaration of pallets. @@ -675,26 +634,26 @@ impl Pallet { /// +----------+ +----------+ +------------------+ /// ``` enum PalletsConversion { - /// Pallets implicitly declare parts. - /// - /// `System: frame_system`. - Implicit(Vec), - /// Pallets explicitly declare parts. - /// - /// `System: frame_system::{Pallet, Call}` - /// - /// However, for backwards compatibility with Polkadot/Kusama - /// we must propagate some other parts to the pallet by default. - Explicit(Vec), - /// Pallets explicitly declare parts that are fully expanded. - /// - /// This is the end state that contains extra parts included by - /// default by Substrate. - /// - /// `System: frame_system expanded::{Error} ::{Pallet, Call}` - /// - /// For this example, the `Pallet`, `Call` and `Error` parts are collected. - ExplicitExpanded(Vec), + /// Pallets implicitly declare parts. + /// + /// `System: frame_system`. + Implicit(Vec), + /// Pallets explicitly declare parts. + /// + /// `System: frame_system::{Pallet, Call}` + /// + /// However, for backwards compatibility with Polkadot/Kusama + /// we must propagate some other parts to the pallet by default. + Explicit(Vec), + /// Pallets explicitly declare parts that are fully expanded. + /// + /// This is the end state that contains extra parts included by + /// default by Substrate. + /// + /// `System: frame_system expanded::{Error} ::{Pallet, Call}` + /// + /// For this example, the `Pallet`, `Call` and `Error` parts are collected. + ExplicitExpanded(Vec), } /// Convert from the parsed pallet declaration to their final information. @@ -703,137 +662,125 @@ enum PalletsConversion { /// pallet using same rules as rust for fieldless enum. I.e. implicit are assigned number /// incrementally from last explicit or 0. fn convert_pallets(pallets: Vec) -> syn::Result { - if pallets.iter().any(|pallet| pallet.pallet_parts.is_none()) { - return Ok(PalletsConversion::Implicit(pallets)); - } - - let mut indices = HashMap::new(); - let mut last_index: Option = None; - let mut names = HashMap::new(); - let mut is_expanded = true; - - let pallets = pallets - .into_iter() - .map(|pallet| { - let final_index = match pallet.index { - Some(i) => i, - None => last_index - .map_or(Some(0), |i| i.checked_add(1)) - .ok_or_else(|| { - let msg = "Pallet index doesn't fit into u8, index is 256"; - syn::Error::new(pallet.name.span(), msg) - })?, - }; - - last_index = Some(final_index); - - if let Some(used_pallet) = indices.insert(final_index, pallet.name.clone()) { - let msg = format!( - "Pallet indices are conflicting: Both pallets {} and {} are at index {}", - used_pallet, pallet.name, final_index, - ); - let mut err = syn::Error::new(used_pallet.span(), &msg); - err.combine(syn::Error::new(pallet.name.span(), msg)); - return Err(err); - } - - if let Some(used_pallet) = names.insert(pallet.name.clone(), pallet.name.span()) { - let msg = "Two pallets with the same name!"; - - let mut err = syn::Error::new(used_pallet, msg); - err.combine(syn::Error::new(pallet.name.span(), msg)); - return Err(err); - } - - let mut pallet_parts = pallet.pallet_parts.expect("Checked above"); - - let available_parts = pallet_parts - .iter() - .map(|part| part.keyword.name()) - .collect::>(); - - // Check parts are correctly specified - match &pallet.specified_parts { - SpecifiedParts::Exclude(parts) | SpecifiedParts::Use(parts) => { - for part in parts { - if !available_parts.contains(part.keyword.name()) { - let msg = format!( - "Invalid pallet part specified, the pallet `{}` doesn't have the \ + if pallets.iter().any(|pallet| pallet.pallet_parts.is_none()) { + return Ok(PalletsConversion::Implicit(pallets)) + } + + let mut indices = HashMap::new(); + let mut last_index: Option = None; + let mut names = HashMap::new(); + let mut is_expanded = true; + + let pallets = pallets + .into_iter() + .map(|pallet| { + let final_index = match pallet.index { + Some(i) => i, + None => last_index.map_or(Some(0), |i| i.checked_add(1)).ok_or_else(|| { + let msg = "Pallet index doesn't fit into u8, index is 256"; + syn::Error::new(pallet.name.span(), msg) + })?, + }; + + last_index = Some(final_index); + + if let Some(used_pallet) = indices.insert(final_index, pallet.name.clone()) { + let msg = format!( + "Pallet indices are conflicting: Both pallets {} and {} are at index {}", + used_pallet, pallet.name, final_index, + ); + let mut err = syn::Error::new(used_pallet.span(), &msg); + err.combine(syn::Error::new(pallet.name.span(), msg)); + return Err(err) + } + + if let Some(used_pallet) = names.insert(pallet.name.clone(), pallet.name.span()) { + let msg = "Two pallets with the same name!"; + + let mut err = syn::Error::new(used_pallet, &msg); + err.combine(syn::Error::new(pallet.name.span(), &msg)); + return Err(err) + } + + let mut pallet_parts = pallet.pallet_parts.expect("Checked above"); + + let available_parts = + pallet_parts.iter().map(|part| part.keyword.name()).collect::>(); + + // Check parts are correctly specified + match &pallet.specified_parts { + SpecifiedParts::Exclude(parts) | SpecifiedParts::Use(parts) => + for part in parts { + if !available_parts.contains(part.keyword.name()) { + let msg = format!( + "Invalid pallet part specified, the pallet `{}` doesn't have the \ `{}` part. Available parts are: {}.", - pallet.name, - part.keyword.name(), - pallet_parts.iter().fold(String::new(), |fold, part| { - if fold.is_empty() { - format!("`{}`", part.keyword.name()) - } else { - format!("{}, `{}`", fold, part.keyword.name()) - } - }) - ); - return Err(syn::Error::new(part.keyword.span(), msg)); - } - } - } - SpecifiedParts::All => (), - } - - // Set only specified parts. - match pallet.specified_parts { - SpecifiedParts::Exclude(excluded_parts) => pallet_parts.retain(|part| { - !excluded_parts - .iter() - .any(|excluded_part| excluded_part.keyword.name() == part.keyword.name()) - }), - SpecifiedParts::Use(used_parts) => pallet_parts.retain(|part| { - used_parts - .iter() - .any(|use_part| use_part.keyword.name() == part.keyword.name()) - }), - SpecifiedParts::All => (), - } - - let cfg_pattern = pallet - .attrs - .iter() - .map(|attr| { - if attr - .path() - .segments - .first() - .map_or(false, |s| s.ident != "cfg") - { - let msg = "Unsupported attribute, only #[cfg] is supported on pallet \ + pallet.name, + part.keyword.name(), + pallet_parts.iter().fold(String::new(), |fold, part| { + if fold.is_empty() { + format!("`{}`", part.keyword.name()) + } else { + format!("{}, `{}`", fold, part.keyword.name()) + } + }) + ); + return Err(syn::Error::new(part.keyword.span(), msg)) + } + }, + SpecifiedParts::All => (), + } + + // Set only specified parts. + match pallet.specified_parts { + SpecifiedParts::Exclude(excluded_parts) => pallet_parts.retain(|part| { + !excluded_parts + .iter() + .any(|excluded_part| excluded_part.keyword.name() == part.keyword.name()) + }), + SpecifiedParts::Use(used_parts) => pallet_parts.retain(|part| { + used_parts.iter().any(|use_part| use_part.keyword.name() == part.keyword.name()) + }), + SpecifiedParts::All => (), + } + + let cfg_pattern = pallet + .attrs + .iter() + .map(|attr| { + if attr.path().segments.first().map_or(false, |s| s.ident != "cfg") { + let msg = "Unsupported attribute, only #[cfg] is supported on pallet \ declarations in `construct_runtime`"; - return Err(syn::Error::new(attr.span(), msg)); - } - - attr.parse_args_with(|input: syn::parse::ParseStream| { - // Required, otherwise the parse stream doesn't advance and will result in - // an error. - let input = input.parse::()?; - cfg_expr::Expression::parse(&input.to_string()) - .map_err(|e| syn::Error::new(attr.span(), e.to_string())) - }) - }) - .collect::>>()?; - - is_expanded &= pallet.is_expanded; - - Ok(Pallet { - is_expanded: pallet.is_expanded, - name: pallet.name, - index: final_index, - path: pallet.path, - instance: pallet.instance, - cfg_pattern, - pallet_parts, - }) - }) - .collect::>>()?; - - if is_expanded { - Ok(PalletsConversion::ExplicitExpanded(pallets)) - } else { - Ok(PalletsConversion::Explicit(pallets)) - } + return Err(syn::Error::new(attr.span(), msg)) + } + + attr.parse_args_with(|input: syn::parse::ParseStream| { + // Required, otherwise the parse stream doesn't advance and will result in + // an error. + let input = input.parse::()?; + cfg_expr::Expression::parse(&input.to_string()) + .map_err(|e| syn::Error::new(attr.span(), e.to_string())) + }) + }) + .collect::>>()?; + + is_expanded &= pallet.is_expanded; + + Ok(Pallet { + is_expanded: pallet.is_expanded, + name: pallet.name, + index: final_index, + path: pallet.path, + instance: pallet.instance, + cfg_pattern, + pallet_parts, + }) + }) + .collect::>>()?; + + if is_expanded { + Ok(PalletsConversion::ExplicitExpanded(pallets)) + } else { + Ok(PalletsConversion::Explicit(pallets)) + } } diff --git a/support/procedural-fork/src/crate_version.rs b/support/procedural-fork/src/crate_version.rs index 63e7c7279..8c8975a42 100644 --- a/support/procedural-fork/src/crate_version.rs +++ b/support/procedural-fork/src/crate_version.rs @@ -24,31 +24,31 @@ use syn::{Error, Result}; /// Create an error that will be shown by rustc at the call site of the macro. fn create_error(message: &str) -> Error { - Error::new(Span::call_site(), message) + Error::new(Span::call_site(), message) } /// Implementation of the `crate_to_crate_version!` macro. pub fn crate_to_crate_version(input: proc_macro::TokenStream) -> Result { - if !input.is_empty() { - return Err(create_error("No arguments expected!")); - } + if !input.is_empty() { + return Err(create_error("No arguments expected!")) + } - let major_version = get_cargo_env_var::("CARGO_PKG_VERSION_MAJOR") - .map_err(|_| create_error("Major version needs to fit into `u16`"))?; + let major_version = get_cargo_env_var::("CARGO_PKG_VERSION_MAJOR") + .map_err(|_| create_error("Major version needs to fit into `u16`"))?; - let minor_version = get_cargo_env_var::("CARGO_PKG_VERSION_MINOR") - .map_err(|_| create_error("Minor version needs to fit into `u8`"))?; + let minor_version = get_cargo_env_var::("CARGO_PKG_VERSION_MINOR") + .map_err(|_| create_error("Minor version needs to fit into `u8`"))?; - let patch_version = get_cargo_env_var::("CARGO_PKG_VERSION_PATCH") - .map_err(|_| create_error("Patch version needs to fit into `u8`"))?; + let patch_version = get_cargo_env_var::("CARGO_PKG_VERSION_PATCH") + .map_err(|_| create_error("Patch version needs to fit into `u8`"))?; - let crate_ = generate_access_from_frame_or_crate("frame-support")?; + let crate_ = generate_access_from_frame_or_crate("frame-support")?; - Ok(quote::quote! { - #crate_::traits::CrateVersion { - major: #major_version, - minor: #minor_version, - patch: #patch_version, - } - }) + Ok(quote::quote! { + #crate_::traits::CrateVersion { + major: #major_version, + minor: #minor_version, + patch: #patch_version, + } + }) } diff --git a/support/procedural-fork/src/derive_impl.rs b/support/procedural-fork/src/derive_impl.rs index 177a95e70..54755f116 100644 --- a/support/procedural-fork/src/derive_impl.rs +++ b/support/procedural-fork/src/derive_impl.rs @@ -23,67 +23,65 @@ use proc_macro2::TokenStream as TokenStream2; use quote::{quote, ToTokens}; use std::collections::HashSet; use syn::{ - parse2, parse_quote, spanned::Spanned, token, Ident, ImplItem, ItemImpl, Path, Result, Token, + parse2, parse_quote, spanned::Spanned, token, Ident, ImplItem, ItemImpl, Path, Result, Token, }; mod keyword { - syn::custom_keyword!(inject_runtime_type); - syn::custom_keyword!(no_aggregated_types); + syn::custom_keyword!(inject_runtime_type); + syn::custom_keyword!(no_aggregated_types); } #[derive(derive_syn_parse::Parse, PartialEq, Eq)] pub enum PalletAttrType { - #[peek(keyword::inject_runtime_type, name = "inject_runtime_type")] - RuntimeType(keyword::inject_runtime_type), + #[peek(keyword::inject_runtime_type, name = "inject_runtime_type")] + RuntimeType(keyword::inject_runtime_type), } #[derive(derive_syn_parse::Parse)] pub struct PalletAttr { - _pound: Token![#], - #[bracket] - _bracket: token::Bracket, - #[inside(_bracket)] - typ: PalletAttrType, + _pound: Token![#], + #[bracket] + _bracket: token::Bracket, + #[inside(_bracket)] + typ: PalletAttrType, } fn is_runtime_type(item: &syn::ImplItemType) -> bool { - item.attrs.iter().any(|attr| { - if let Ok(PalletAttr { - typ: PalletAttrType::RuntimeType(_), - .. - }) = parse2::(attr.into_token_stream()) - { - return true; - } - false - }) + item.attrs.iter().any(|attr| { + if let Ok(PalletAttr { typ: PalletAttrType::RuntimeType(_), .. }) = + parse2::(attr.into_token_stream()) + { + return true + } + false + }) } #[derive(Parse, Debug)] pub struct DeriveImplAttrArgs { - pub default_impl_path: Path, - _as: Option, - #[parse_if(_as.is_some())] - pub disambiguation_path: Option, - _comma: Option, - #[parse_if(_comma.is_some())] - pub no_aggregated_types: Option, + pub default_impl_path: Path, + _as: Option, + #[parse_if(_as.is_some())] + pub disambiguation_path: Option, + _comma: Option, + #[parse_if(_comma.is_some())] + pub no_aggregated_types: Option, } impl ForeignPath for DeriveImplAttrArgs { - fn foreign_path(&self) -> &Path { - &self.default_impl_path - } + fn foreign_path(&self) -> &Path { + &self.default_impl_path + } } impl ToTokens for DeriveImplAttrArgs { - fn to_tokens(&self, tokens: &mut TokenStream2) { - tokens.extend(self.default_impl_path.to_token_stream()); - tokens.extend(self._as.to_token_stream()); - tokens.extend(self.disambiguation_path.to_token_stream()); - tokens.extend(self._comma.to_token_stream()); - tokens.extend(self.no_aggregated_types.to_token_stream()); - } + fn to_tokens(&self, tokens: &mut TokenStream2) { + tokens.extend(self.default_impl_path.to_token_stream()); + tokens.extend(self._as.to_token_stream()); + tokens.extend(self.disambiguation_path.to_token_stream()); + tokens.extend(self._comma.to_token_stream()); + tokens.extend(self.no_aggregated_types.to_token_stream()); + } } /// Gets the [`Ident`] representation of the given [`ImplItem`], if one exists. Otherwise @@ -92,13 +90,13 @@ impl ToTokens for DeriveImplAttrArgs { /// Used by [`combine_impls`] to determine whether we can compare [`ImplItem`]s by [`Ident`] /// or not. fn impl_item_ident(impl_item: &ImplItem) -> Option<&Ident> { - match impl_item { - ImplItem::Const(item) => Some(&item.ident), - ImplItem::Fn(item) => Some(&item.sig.ident), - ImplItem::Type(item) => Some(&item.ident), - ImplItem::Macro(item) => item.mac.path.get_ident(), - _ => None, - } + match impl_item { + ImplItem::Const(item) => Some(&item.ident), + ImplItem::Fn(item) => Some(&item.sig.ident), + ImplItem::Type(item) => Some(&item.ident), + ImplItem::Macro(item) => item.mac.path.get_ident(), + _ => None, + } } /// The real meat behind `derive_impl`. Takes in a `local_impl`, which is the impl for which we @@ -114,68 +112,64 @@ fn impl_item_ident(impl_item: &ImplItem) -> Option<&Ident> { /// into `local_impl`. Items that lack an ident and also exist verbatim in `local_impl` are not /// copied over. fn combine_impls( - local_impl: ItemImpl, - foreign_impl: ItemImpl, - default_impl_path: Path, - disambiguation_path: Path, - inject_runtime_types: bool, + local_impl: ItemImpl, + foreign_impl: ItemImpl, + default_impl_path: Path, + disambiguation_path: Path, + inject_runtime_types: bool, ) -> ItemImpl { - let (existing_local_keys, existing_unsupported_items): (HashSet, HashSet) = - local_impl - .items - .iter() - .cloned() - .partition(|impl_item| impl_item_ident(impl_item).is_some()); - let existing_local_keys: HashSet = existing_local_keys - .into_iter() - .filter_map(|item| impl_item_ident(&item).cloned()) - .collect(); - let mut final_impl = local_impl; - let extended_items = foreign_impl.items.into_iter().filter_map(|item| { - if let Some(ident) = impl_item_ident(&item) { - if existing_local_keys.contains(ident) { - // do not copy colliding items that have an ident - return None; - } - if let ImplItem::Type(typ) = item.clone() { - let cfg_attrs = typ - .attrs - .iter() - .filter(|attr| { - attr.path() - .get_ident() - .map_or(false, |ident| ident == "cfg") - }) - .map(|attr| attr.to_token_stream()); - if is_runtime_type(&typ) { - let item: ImplItem = if inject_runtime_types { - parse_quote! { - #( #cfg_attrs )* - type #ident = #ident; - } - } else { - item - }; - return Some(item); - } - // modify and insert uncolliding type items - let modified_item: ImplItem = parse_quote! { - #( #cfg_attrs )* - type #ident = <#default_impl_path as #disambiguation_path>::#ident; - }; - return Some(modified_item); - } - // copy uncolliding non-type items that have an ident - Some(item) - } else { - // do not copy colliding items that lack an ident - (!existing_unsupported_items.contains(&item)) - // copy uncolliding items without an ident verbatim - .then_some(item) - } - }); - final_impl.items.extend(extended_items); - final_impl + let (existing_local_keys, existing_unsupported_items): (HashSet, HashSet) = + local_impl + .items + .iter() + .cloned() + .partition(|impl_item| impl_item_ident(impl_item).is_some()); + let existing_local_keys: HashSet = existing_local_keys + .into_iter() + .filter_map(|item| impl_item_ident(&item).cloned()) + .collect(); + let mut final_impl = local_impl; + let extended_items = foreign_impl.items.into_iter().filter_map(|item| { + if let Some(ident) = impl_item_ident(&item) { + if existing_local_keys.contains(&ident) { + // do not copy colliding items that have an ident + return None + } + if let ImplItem::Type(typ) = item.clone() { + let cfg_attrs = typ + .attrs + .iter() + .filter(|attr| attr.path().get_ident().map_or(false, |ident| ident == "cfg")) + .map(|attr| attr.to_token_stream()); + if is_runtime_type(&typ) { + let item: ImplItem = if inject_runtime_types { + parse_quote! { + #( #cfg_attrs )* + type #ident = #ident; + } + } else { + item + }; + return Some(item) + } + // modify and insert uncolliding type items + let modified_item: ImplItem = parse_quote! { + #( #cfg_attrs )* + type #ident = <#default_impl_path as #disambiguation_path>::#ident; + }; + return Some(modified_item) + } + // copy uncolliding non-type items that have an ident + Some(item) + } else { + // do not copy colliding items that lack an ident + (!existing_unsupported_items.contains(&item)) + // copy uncolliding items without an ident verbatim + .then_some(item) + } + }); + final_impl.items.extend(extended_items); + final_impl } /// Computes the disambiguation path for the `derive_impl` attribute macro. @@ -184,26 +178,25 @@ fn combine_impls( /// disambiguation is used as is. If not, we infer the disambiguation path from the /// `foreign_impl_path` and the computed scope. fn compute_disambiguation_path( - disambiguation_path: Option, - foreign_impl: ItemImpl, - default_impl_path: Path, + disambiguation_path: Option, + foreign_impl: ItemImpl, + default_impl_path: Path, ) -> Result { - match (disambiguation_path, foreign_impl.clone().trait_) { - (Some(disambiguation_path), _) => Ok(disambiguation_path), - (None, Some((_, foreign_impl_path, _))) => { - if default_impl_path.segments.len() > 1 { - let scope = default_impl_path.segments.first(); - Ok(parse_quote!(#scope :: #foreign_impl_path)) - } else { - Ok(foreign_impl_path) - } - } - _ => Err(syn::Error::new( - default_impl_path.span(), - "Impl statement must have a defined type being implemented \ + match (disambiguation_path, foreign_impl.clone().trait_) { + (Some(disambiguation_path), _) => Ok(disambiguation_path), + (None, Some((_, foreign_impl_path, _))) => + if default_impl_path.segments.len() > 1 { + let scope = default_impl_path.segments.first(); + Ok(parse_quote!(#scope :: #foreign_impl_path)) + } else { + Ok(foreign_impl_path) + }, + _ => Err(syn::Error::new( + default_impl_path.span(), + "Impl statement must have a defined type being implemented \ for a defined type such as `impl A for B`", - )), - } + )), + } } /// Internal implementation behind [`#[derive_impl(..)]`](`macro@crate::derive_impl`). @@ -218,100 +211,93 @@ fn compute_disambiguation_path( /// `disambiguation_path`: the module path of the external trait we will use to qualify /// defaults imported from the external `impl` statement pub fn derive_impl( - default_impl_path: TokenStream2, - foreign_tokens: TokenStream2, - local_tokens: TokenStream2, - disambiguation_path: Option, - no_aggregated_types: Option, + default_impl_path: TokenStream2, + foreign_tokens: TokenStream2, + local_tokens: TokenStream2, + disambiguation_path: Option, + no_aggregated_types: Option, ) -> Result { - let local_impl = parse2::(local_tokens)?; - let foreign_impl = parse2::(foreign_tokens)?; - let default_impl_path = parse2::(default_impl_path)?; + let local_impl = parse2::(local_tokens)?; + let foreign_impl = parse2::(foreign_tokens)?; + let default_impl_path = parse2::(default_impl_path)?; - let disambiguation_path = compute_disambiguation_path( - disambiguation_path, - foreign_impl.clone(), - default_impl_path.clone(), - )?; + let disambiguation_path = compute_disambiguation_path( + disambiguation_path, + foreign_impl.clone(), + default_impl_path.clone(), + )?; - // generate the combined impl - let combined_impl = combine_impls( - local_impl, - foreign_impl, - default_impl_path, - disambiguation_path, - no_aggregated_types.is_none(), - ); + // generate the combined impl + let combined_impl = combine_impls( + local_impl, + foreign_impl, + default_impl_path, + disambiguation_path, + no_aggregated_types.is_none(), + ); - Ok(quote!(#combined_impl)) + Ok(quote!(#combined_impl)) } #[test] fn test_derive_impl_attr_args_parsing() { - parse2::(quote!( - some::path::TestDefaultConfig as some::path::DefaultConfig - )) - .unwrap(); - parse2::(quote!( - frame_system::prelude::testing::TestDefaultConfig as DefaultConfig - )) - .unwrap(); - parse2::(quote!(Something as some::path::DefaultConfig)).unwrap(); - parse2::(quote!(Something as DefaultConfig)).unwrap(); - parse2::(quote!(DefaultConfig)).unwrap(); - assert!(parse2::(quote!()).is_err()); - assert!(parse2::(quote!(Config Config)).is_err()); + parse2::(quote!( + some::path::TestDefaultConfig as some::path::DefaultConfig + )) + .unwrap(); + parse2::(quote!( + frame_system::prelude::testing::TestDefaultConfig as DefaultConfig + )) + .unwrap(); + parse2::(quote!(Something as some::path::DefaultConfig)).unwrap(); + parse2::(quote!(Something as DefaultConfig)).unwrap(); + parse2::(quote!(DefaultConfig)).unwrap(); + assert!(parse2::(quote!()).is_err()); + assert!(parse2::(quote!(Config Config)).is_err()); } #[test] fn test_runtime_type_with_doc() { - #[allow(unused)] - trait TestTrait { - type Test; - } - #[allow(unused)] - struct TestStruct; - let p = parse2::(quote!( - impl TestTrait for TestStruct { - /// Some doc - #[inject_runtime_type] - type Test = u32; - } - )) - .unwrap(); - for item in p.items { - if let ImplItem::Type(typ) = item { - assert!(is_runtime_type(&typ)); - } - } + trait TestTrait { + type Test; + } + #[allow(unused)] + struct TestStruct; + let p = parse2::(quote!( + impl TestTrait for TestStruct { + /// Some doc + #[inject_runtime_type] + type Test = u32; + } + )) + .unwrap(); + for item in p.items { + if let ImplItem::Type(typ) = item { + assert_eq!(is_runtime_type(&typ), true); + } + } } #[test] fn test_disambiguation_path() { - let foreign_impl: ItemImpl = parse_quote!(impl SomeTrait for SomeType {}); - let default_impl_path: Path = parse_quote!(SomeScope::SomeType); + let foreign_impl: ItemImpl = parse_quote!(impl SomeTrait for SomeType {}); + let default_impl_path: Path = parse_quote!(SomeScope::SomeType); - // disambiguation path is specified - let disambiguation_path = compute_disambiguation_path( - Some(parse_quote!(SomeScope::SomePath)), - foreign_impl.clone(), - default_impl_path.clone(), - ); - assert_eq!( - disambiguation_path.unwrap(), - parse_quote!(SomeScope::SomePath) - ); + // disambiguation path is specified + let disambiguation_path = compute_disambiguation_path( + Some(parse_quote!(SomeScope::SomePath)), + foreign_impl.clone(), + default_impl_path.clone(), + ); + assert_eq!(disambiguation_path.unwrap(), parse_quote!(SomeScope::SomePath)); - // disambiguation path is not specified and the default_impl_path has more than one segment - let disambiguation_path = - compute_disambiguation_path(None, foreign_impl.clone(), default_impl_path.clone()); - assert_eq!( - disambiguation_path.unwrap(), - parse_quote!(SomeScope::SomeTrait) - ); + // disambiguation path is not specified and the default_impl_path has more than one segment + let disambiguation_path = + compute_disambiguation_path(None, foreign_impl.clone(), default_impl_path.clone()); + assert_eq!(disambiguation_path.unwrap(), parse_quote!(SomeScope::SomeTrait)); - // disambiguation path is not specified and the default_impl_path has only one segment - let disambiguation_path = - compute_disambiguation_path(None, foreign_impl.clone(), parse_quote!(SomeType)); - assert_eq!(disambiguation_path.unwrap(), parse_quote!(SomeTrait)); + // disambiguation path is not specified and the default_impl_path has only one segment + let disambiguation_path = + compute_disambiguation_path(None, foreign_impl.clone(), parse_quote!(SomeType)); + assert_eq!(disambiguation_path.unwrap(), parse_quote!(SomeTrait)); } diff --git a/support/procedural-fork/src/dummy_part_checker.rs b/support/procedural-fork/src/dummy_part_checker.rs index 6bed541d1..34d9a3e23 100644 --- a/support/procedural-fork/src/dummy_part_checker.rs +++ b/support/procedural-fork/src/dummy_part_checker.rs @@ -19,63 +19,61 @@ use crate::COUNTER; use proc_macro::TokenStream; pub fn generate_dummy_part_checker(input: TokenStream) -> TokenStream { - if !input.is_empty() { - return syn::Error::new(proc_macro2::Span::call_site(), "No arguments expected") - .to_compile_error() - .into(); - } + if !input.is_empty() { + return syn::Error::new(proc_macro2::Span::call_site(), "No arguments expected") + .to_compile_error() + .into() + } - let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); - let no_op_macro_ident = syn::Ident::new( - &format!("__dummy_part_checker_{}", count), - proc_macro2::Span::call_site(), - ); + let no_op_macro_ident = + syn::Ident::new(&format!("__dummy_part_checker_{}", count), proc_macro2::Span::call_site()); - quote::quote!( - #[macro_export] - #[doc(hidden)] - macro_rules! #no_op_macro_ident { - ( $( $tt:tt )* ) => {}; - } + quote::quote!( + #[macro_export] + #[doc(hidden)] + macro_rules! #no_op_macro_ident { + ( $( $tt:tt )* ) => {}; + } - #[doc(hidden)] - pub mod __substrate_genesis_config_check { - #[doc(hidden)] - pub use #no_op_macro_ident as is_genesis_config_defined; - #[doc(hidden)] - pub use #no_op_macro_ident as is_std_enabled_for_genesis; - } + #[doc(hidden)] + pub mod __substrate_genesis_config_check { + #[doc(hidden)] + pub use #no_op_macro_ident as is_genesis_config_defined; + #[doc(hidden)] + pub use #no_op_macro_ident as is_std_enabled_for_genesis; + } - #[doc(hidden)] - pub mod __substrate_event_check { - #[doc(hidden)] - pub use #no_op_macro_ident as is_event_part_defined; - } + #[doc(hidden)] + pub mod __substrate_event_check { + #[doc(hidden)] + pub use #no_op_macro_ident as is_event_part_defined; + } - #[doc(hidden)] - pub mod __substrate_inherent_check { - #[doc(hidden)] - pub use #no_op_macro_ident as is_inherent_part_defined; - } + #[doc(hidden)] + pub mod __substrate_inherent_check { + #[doc(hidden)] + pub use #no_op_macro_ident as is_inherent_part_defined; + } - #[doc(hidden)] - pub mod __substrate_validate_unsigned_check { - #[doc(hidden)] - pub use #no_op_macro_ident as is_validate_unsigned_part_defined; - } + #[doc(hidden)] + pub mod __substrate_validate_unsigned_check { + #[doc(hidden)] + pub use #no_op_macro_ident as is_validate_unsigned_part_defined; + } - #[doc(hidden)] - pub mod __substrate_call_check { - #[doc(hidden)] - pub use #no_op_macro_ident as is_call_part_defined; - } + #[doc(hidden)] + pub mod __substrate_call_check { + #[doc(hidden)] + pub use #no_op_macro_ident as is_call_part_defined; + } - #[doc(hidden)] - pub mod __substrate_origin_check { - #[doc(hidden)] - pub use #no_op_macro_ident as is_origin_part_defined; - } - ) - .into() + #[doc(hidden)] + pub mod __substrate_origin_check { + #[doc(hidden)] + pub use #no_op_macro_ident as is_origin_part_defined; + } + ) + .into() } diff --git a/support/procedural-fork/src/dynamic_params.rs b/support/procedural-fork/src/dynamic_params.rs index b302f2758..29399a885 100644 --- a/support/procedural-fork/src/dynamic_params.rs +++ b/support/procedural-fork/src/dynamic_params.rs @@ -26,132 +26,123 @@ use syn::{parse2, spanned::Spanned, visit_mut, visit_mut::VisitMut, Result, Toke /// Parse and expand a `#[dynamic_params(..)]` module. pub fn dynamic_params(attr: TokenStream, item: TokenStream) -> Result { - DynamicParamModAttr::parse(attr, item).map(ToTokens::into_token_stream) + DynamicParamModAttr::parse(attr, item).map(ToTokens::into_token_stream) } /// Parse and expand `#[dynamic_pallet_params(..)]` attribute. pub fn dynamic_pallet_params(attr: TokenStream, item: TokenStream) -> Result { - DynamicPalletParamAttr::parse(attr, item).map(ToTokens::into_token_stream) + DynamicPalletParamAttr::parse(attr, item).map(ToTokens::into_token_stream) } /// Parse and expand `#[dynamic_aggregated_params_internal]` attribute. pub fn dynamic_aggregated_params_internal( - _attr: TokenStream, - item: TokenStream, + _attr: TokenStream, + item: TokenStream, ) -> Result { - parse2::(item).map(ToTokens::into_token_stream) + parse2::(item).map(ToTokens::into_token_stream) } /// A top `#[dynamic_params(..)]` attribute together with a mod. #[derive(derive_syn_parse::Parse)] pub struct DynamicParamModAttr { - params_mod: syn::ItemMod, - meta: DynamicParamModAttrMeta, + params_mod: syn::ItemMod, + meta: DynamicParamModAttrMeta, } /// The inner meta of a `#[dynamic_params(..)]` attribute. #[derive(derive_syn_parse::Parse)] pub struct DynamicParamModAttrMeta { - name: syn::Ident, - _comma: Option, - #[parse_if(_comma.is_some())] - params_pallet: Option, + name: syn::Ident, + _comma: Option, + #[parse_if(_comma.is_some())] + params_pallet: Option, } impl DynamicParamModAttr { - pub fn parse(attr: TokenStream, item: TokenStream) -> Result { - let params_mod = parse2(item)?; - let meta = parse2(attr)?; - Ok(Self { params_mod, meta }) - } - - pub fn inner_mods(&self) -> Vec { - self.params_mod - .content - .as_ref() - .map_or(Vec::new(), |(_, items)| { - items - .iter() - .filter_map(|i| match i { - syn::Item::Mod(m) => Some(m), - _ => None, - }) - .cloned() - .collect() - }) - } + pub fn parse(attr: TokenStream, item: TokenStream) -> Result { + let params_mod = parse2(item)?; + let meta = parse2(attr)?; + Ok(Self { params_mod, meta }) + } + + pub fn inner_mods(&self) -> Vec { + self.params_mod.content.as_ref().map_or(Vec::new(), |(_, items)| { + items + .iter() + .filter_map(|i| match i { + syn::Item::Mod(m) => Some(m), + _ => None, + }) + .cloned() + .collect() + }) + } } impl ToTokens for DynamicParamModAttr { - fn to_tokens(&self, tokens: &mut TokenStream) { - let scrate = match crate_access() { - Ok(path) => path, - Err(err) => return tokens.extend(err), - }; - let (mut params_mod, name) = (self.params_mod.clone(), &self.meta.name); - let dynam_params_ident = ¶ms_mod.ident; - - let mut quoted_enum = quote! {}; - for m in self.inner_mods() { - let aggregate_name = - syn::Ident::new(&m.ident.to_string().to_class_case(), m.ident.span()); - let mod_name = &m.ident; - - let mut attrs = m.attrs.clone(); - attrs.retain(|attr| !attr.path().is_ident("dynamic_pallet_params")); - if let Err(err) = ensure_codec_index(&attrs, m.span()) { - tokens.extend(err.into_compile_error()); - return; - } - - quoted_enum.extend(quote! { - #(#attrs)* - #aggregate_name(#dynam_params_ident::#mod_name::Parameters), - }); - } - - // Inject the outer args into the inner `#[dynamic_pallet_params(..)]` attribute. - if let Some(params_pallet) = &self.meta.params_pallet { - MacroInjectArgs { - runtime_params: name.clone(), - params_pallet: params_pallet.clone(), - } - .visit_item_mod_mut(&mut params_mod); - } - - tokens.extend(quote! { - #params_mod - - #[#scrate::dynamic_params::dynamic_aggregated_params_internal] - pub enum #name { - #quoted_enum - } - }); - } + fn to_tokens(&self, tokens: &mut TokenStream) { + let scrate = match crate_access() { + Ok(path) => path, + Err(err) => return tokens.extend(err), + }; + let (mut params_mod, name) = (self.params_mod.clone(), &self.meta.name); + let dynam_params_ident = ¶ms_mod.ident; + + let mut quoted_enum = quote! {}; + for m in self.inner_mods() { + let aggregate_name = + syn::Ident::new(&m.ident.to_string().to_class_case(), m.ident.span()); + let mod_name = &m.ident; + + let mut attrs = m.attrs.clone(); + attrs.retain(|attr| !attr.path().is_ident("dynamic_pallet_params")); + if let Err(err) = ensure_codec_index(&attrs, m.span()) { + tokens.extend(err.into_compile_error()); + return + } + + quoted_enum.extend(quote! { + #(#attrs)* + #aggregate_name(#dynam_params_ident::#mod_name::Parameters), + }); + } + + // Inject the outer args into the inner `#[dynamic_pallet_params(..)]` attribute. + if let Some(params_pallet) = &self.meta.params_pallet { + MacroInjectArgs { runtime_params: name.clone(), params_pallet: params_pallet.clone() } + .visit_item_mod_mut(&mut params_mod); + } + + tokens.extend(quote! { + #params_mod + + #[#scrate::dynamic_params::dynamic_aggregated_params_internal] + pub enum #name { + #quoted_enum + } + }); + } } /// Ensure there is a `#[codec(index = ..)]` attribute. fn ensure_codec_index(attrs: &Vec, span: Span) -> Result<()> { - let mut found = false; - - for attr in attrs.iter() { - if attr.path().is_ident("codec") { - let meta: syn::ExprAssign = attr.parse_args()?; - if meta.left.to_token_stream().to_string() == "index" { - found = true; - break; - } - } - } - - if !found { - Err(syn::Error::new( - span, - "Missing explicit `#[codec(index = ..)]` attribute", - )) - } else { - Ok(()) - } + let mut found = false; + + for attr in attrs.iter() { + if attr.path().is_ident("codec") { + let meta: syn::ExprAssign = attr.parse_args()?; + if meta.left.to_token_stream().to_string() == "index" { + found = true; + break + } + } + } + + if !found { + Err(syn::Error::new(span, "Missing explicit `#[codec(index = ..)]` attribute")) + } else { + Ok(()) + } } /// Used to inject arguments into the inner `#[dynamic_pallet_params(..)]` attribute. @@ -159,121 +150,110 @@ fn ensure_codec_index(attrs: &Vec, span: Span) -> Result<()> { /// This allows the outer `#[dynamic_params(..)]` attribute to specify some arguments that don't /// need to be repeated every time. struct MacroInjectArgs { - runtime_params: syn::Ident, - params_pallet: syn::Type, + runtime_params: syn::Ident, + params_pallet: syn::Type, } impl VisitMut for MacroInjectArgs { - fn visit_item_mod_mut(&mut self, item: &mut syn::ItemMod) { - // Check if the mod has a `#[dynamic_pallet_params(..)]` attribute. - let attr = item - .attrs - .iter_mut() - .find(|attr| attr.path().is_ident("dynamic_pallet_params")); - - if let Some(attr) = attr { - if let syn::Meta::Path(path) = &attr.meta { - assert_eq!(path.to_token_stream().to_string(), "dynamic_pallet_params") - } - - let runtime_params = &self.runtime_params; - let params_pallet = &self.params_pallet; - - attr.meta = syn::parse2::(quote! { - dynamic_pallet_params(#runtime_params, #params_pallet) - }) - .unwrap(); - } - - visit_mut::visit_item_mod_mut(self, item); - } + fn visit_item_mod_mut(&mut self, item: &mut syn::ItemMod) { + // Check if the mod has a `#[dynamic_pallet_params(..)]` attribute. + let attr = item.attrs.iter_mut().find(|attr| attr.path().is_ident("dynamic_pallet_params")); + + if let Some(attr) = attr { + match &attr.meta { + syn::Meta::Path(path) => + assert_eq!(path.to_token_stream().to_string(), "dynamic_pallet_params"), + _ => (), + } + + let runtime_params = &self.runtime_params; + let params_pallet = &self.params_pallet; + + attr.meta = syn::parse2::(quote! { + dynamic_pallet_params(#runtime_params, #params_pallet) + }) + .unwrap() + .into(); + } + + visit_mut::visit_item_mod_mut(self, item); + } } /// The helper attribute of a `#[dynamic_pallet_params(runtime_params, params_pallet)]` /// attribute. #[derive(derive_syn_parse::Parse)] pub struct DynamicPalletParamAttr { - inner_mod: syn::ItemMod, - meta: DynamicPalletParamAttrMeta, + inner_mod: syn::ItemMod, + meta: DynamicPalletParamAttrMeta, } /// The inner meta of a `#[dynamic_pallet_params(..)]` attribute. #[derive(derive_syn_parse::Parse)] pub struct DynamicPalletParamAttrMeta { - runtime_params: syn::Ident, - _comma: Token![,], - parameter_pallet: syn::Type, + runtime_params: syn::Ident, + _comma: Token![,], + parameter_pallet: syn::Type, } impl DynamicPalletParamAttr { - pub fn parse(attr: TokenStream, item: TokenStream) -> Result { - Ok(Self { - inner_mod: parse2(item)?, - meta: parse2(attr)?, - }) - } - - pub fn statics(&self) -> Vec { - self.inner_mod - .content - .as_ref() - .map_or(Vec::new(), |(_, items)| { - items - .iter() - .filter_map(|i| match i { - syn::Item::Static(s) => Some(s), - _ => None, - }) - .cloned() - .collect() - }) - } + pub fn parse(attr: TokenStream, item: TokenStream) -> Result { + Ok(Self { inner_mod: parse2(item)?, meta: parse2(attr)? }) + } + + pub fn statics(&self) -> Vec { + self.inner_mod.content.as_ref().map_or(Vec::new(), |(_, items)| { + items + .iter() + .filter_map(|i| match i { + syn::Item::Static(s) => Some(s), + _ => None, + }) + .cloned() + .collect() + }) + } } impl ToTokens for DynamicPalletParamAttr { - fn to_tokens(&self, tokens: &mut TokenStream) { - let scrate = match crate_access() { - Ok(path) => path, - Err(err) => return tokens.extend(err), - }; - let (params_mod, parameter_pallet, runtime_params) = ( - &self.inner_mod, - &self.meta.parameter_pallet, - &self.meta.runtime_params, - ); - - let aggregate_name = syn::Ident::new( - ¶ms_mod.ident.to_string().to_class_case(), - params_mod.ident.span(), - ); - let (mod_name, vis) = (¶ms_mod.ident, ¶ms_mod.vis); - let statics = self.statics(); - - let (mut key_names, mut key_values, mut defaults, mut attrs, mut value_types): ( - Vec<_>, - Vec<_>, - Vec<_>, - Vec<_>, - Vec<_>, - ) = Default::default(); - - for s in statics.iter() { - if let Err(err) = ensure_codec_index(&s.attrs, s.span()) { - tokens.extend(err.into_compile_error()); - return; - } - - key_names.push(&s.ident); - key_values.push(format_ident!("{}Value", &s.ident)); - defaults.push(&s.expr); - attrs.push(&s.attrs); - value_types.push(&s.ty); - } - - let key_ident = syn::Ident::new("ParametersKey", params_mod.ident.span()); - let value_ident = syn::Ident::new("ParametersValue", params_mod.ident.span()); - let runtime_key_ident = format_ident!("{}Key", runtime_params); - let runtime_value_ident = format_ident!("{}Value", runtime_params); - - tokens.extend(quote! { + fn to_tokens(&self, tokens: &mut TokenStream) { + let scrate = match crate_access() { + Ok(path) => path, + Err(err) => return tokens.extend(err), + }; + let (params_mod, parameter_pallet, runtime_params) = + (&self.inner_mod, &self.meta.parameter_pallet, &self.meta.runtime_params); + + let aggregate_name = + syn::Ident::new(¶ms_mod.ident.to_string().to_class_case(), params_mod.ident.span()); + let (mod_name, vis) = (¶ms_mod.ident, ¶ms_mod.vis); + let statics = self.statics(); + + let (mut key_names, mut key_values, mut defaults, mut attrs, mut value_types): ( + Vec<_>, + Vec<_>, + Vec<_>, + Vec<_>, + Vec<_>, + ) = Default::default(); + + for s in statics.iter() { + if let Err(err) = ensure_codec_index(&s.attrs, s.span()) { + tokens.extend(err.into_compile_error()); + return + } + + key_names.push(&s.ident); + key_values.push(format_ident!("{}Value", &s.ident)); + defaults.push(&s.expr); + attrs.push(&s.attrs); + value_types.push(&s.ty); + } + + let key_ident = syn::Ident::new("ParametersKey", params_mod.ident.span()); + let value_ident = syn::Ident::new("ParametersValue", params_mod.ident.span()); + let runtime_key_ident = format_ident!("{}Key", runtime_params); + let runtime_value_ident = format_ident!("{}Value", runtime_params); + + tokens.extend(quote! { pub mod #mod_name { use super::*; @@ -446,44 +426,44 @@ impl ToTokens for DynamicPalletParamAttr { )* } }); - } + } } #[derive(derive_syn_parse::Parse)] pub struct DynamicParamAggregatedEnum { - aggregated_enum: syn::ItemEnum, + aggregated_enum: syn::ItemEnum, } impl ToTokens for DynamicParamAggregatedEnum { - fn to_tokens(&self, tokens: &mut TokenStream) { - let scrate = match crate_access() { - Ok(path) => path, - Err(err) => return tokens.extend(err), - }; - let params_enum = &self.aggregated_enum; - let (name, vis) = (¶ms_enum.ident, ¶ms_enum.vis); - - let (mut indices, mut param_names, mut param_types): (Vec<_>, Vec<_>, Vec<_>) = - Default::default(); - let mut attributes = Vec::new(); - for (i, variant) in params_enum.variants.iter().enumerate() { - indices.push(i); - param_names.push(&variant.ident); - attributes.push(&variant.attrs); - - param_types.push(match &variant.fields { + fn to_tokens(&self, tokens: &mut TokenStream) { + let scrate = match crate_access() { + Ok(path) => path, + Err(err) => return tokens.extend(err), + }; + let params_enum = &self.aggregated_enum; + let (name, vis) = (¶ms_enum.ident, ¶ms_enum.vis); + + let (mut indices, mut param_names, mut param_types): (Vec<_>, Vec<_>, Vec<_>) = + Default::default(); + let mut attributes = Vec::new(); + for (i, variant) in params_enum.variants.iter().enumerate() { + indices.push(i); + param_names.push(&variant.ident); + attributes.push(&variant.attrs); + + param_types.push(match &variant.fields { syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty, _ => { *tokens = quote! { compile_error!("Only unnamed enum variants with one inner item are supported") }; return }, }); - } + } - let params_key_ident = format_ident!("{}Key", params_enum.ident); - let params_value_ident = format_ident!("{}Value", params_enum.ident); + let params_key_ident = format_ident!("{}Key", params_enum.ident); + let params_value_ident = format_ident!("{}Value", params_enum.ident); - tokens.extend(quote! { + tokens.extend(quote! { #[doc(hidden)] #[derive( Clone, @@ -574,10 +554,10 @@ impl ToTokens for DynamicParamAggregatedEnum { } )* }); - } + } } /// Get access to the current crate and convert the error to a compile error. fn crate_access() -> core::result::Result { - generate_access_from_frame_or_crate("frame-support").map_err(|e| e.to_compile_error()) + generate_access_from_frame_or_crate("frame-support").map_err(|e| e.to_compile_error()) } diff --git a/support/procedural-fork/src/key_prefix.rs b/support/procedural-fork/src/key_prefix.rs index aea60ce3b..7f1ab6866 100644 --- a/support/procedural-fork/src/key_prefix.rs +++ b/support/procedural-fork/src/key_prefix.rs @@ -23,84 +23,82 @@ use syn::{Ident, Result}; const MAX_IDENTS: usize = 18; pub fn impl_key_prefix_for_tuples(input: proc_macro::TokenStream) -> Result { - if !input.is_empty() { - return Err(syn::Error::new(Span::call_site(), "No arguments expected")); - } - - let mut all_trait_impls = TokenStream::new(); - let frame_support = generate_access_from_frame_or_crate("frame-support")?; - - for i in 2..=MAX_IDENTS { - let current_tuple = (0..i) - .map(|n| Ident::new(&format!("Tuple{}", n), Span::call_site())) - .collect::>(); - - for prefix_count in 1..i { - let (prefixes, suffixes) = current_tuple.split_at(prefix_count); - - let hashers = current_tuple - .iter() - .map(|ident| format_ident!("Hasher{}", ident)) - .collect::>(); - let kargs = prefixes - .iter() - .map(|ident| format_ident!("KArg{}", ident)) - .collect::>(); - let partial_keygen = generate_keygen(prefixes); - let suffix_keygen = generate_keygen(suffixes); - let suffix_tuple = generate_tuple(suffixes); - - let trait_impls = quote! { - impl< - #(#current_tuple: FullCodec + StaticTypeInfo,)* - #(#hashers: StorageHasher,)* - #(#kargs: EncodeLike<#prefixes>),* - > HasKeyPrefix<( #( #kargs, )* )> for ( #( Key<#hashers, #current_tuple>, )* ) { - type Suffix = #suffix_tuple; - - fn partial_key(prefix: ( #( #kargs, )* )) -> Vec { - <#partial_keygen>::final_key(prefix) - } - } - - impl< - #(#current_tuple: FullCodec + StaticTypeInfo,)* - #(#hashers: ReversibleStorageHasher,)* - #(#kargs: EncodeLike<#prefixes>),* - > HasReversibleKeyPrefix<( #( #kargs, )* )> for ( #( Key<#hashers, #current_tuple>, )* ) { - fn decode_partial_key(key_material: &[u8]) -> Result< - Self::Suffix, - #frame_support::__private::codec::Error, - > { - <#suffix_keygen>::decode_final_key(key_material).map(|k| k.0) - } - } - }; - - all_trait_impls.extend(trait_impls); - } - } - - Ok(all_trait_impls) + if !input.is_empty() { + return Err(syn::Error::new(Span::call_site(), "No arguments expected")) + } + + let mut all_trait_impls = TokenStream::new(); + let frame_support = generate_access_from_frame_or_crate("frame-support")?; + + for i in 2..=MAX_IDENTS { + let current_tuple = (0..i) + .map(|n| Ident::new(&format!("Tuple{}", n), Span::call_site())) + .collect::>(); + + for prefix_count in 1..i { + let (prefixes, suffixes) = current_tuple.split_at(prefix_count); + + let hashers = current_tuple + .iter() + .map(|ident| format_ident!("Hasher{}", ident)) + .collect::>(); + let kargs = + prefixes.iter().map(|ident| format_ident!("KArg{}", ident)).collect::>(); + let partial_keygen = generate_keygen(prefixes); + let suffix_keygen = generate_keygen(suffixes); + let suffix_tuple = generate_tuple(suffixes); + + let trait_impls = quote! { + impl< + #(#current_tuple: FullCodec + StaticTypeInfo,)* + #(#hashers: StorageHasher,)* + #(#kargs: EncodeLike<#prefixes>),* + > HasKeyPrefix<( #( #kargs, )* )> for ( #( Key<#hashers, #current_tuple>, )* ) { + type Suffix = #suffix_tuple; + + fn partial_key(prefix: ( #( #kargs, )* )) -> Vec { + <#partial_keygen>::final_key(prefix) + } + } + + impl< + #(#current_tuple: FullCodec + StaticTypeInfo,)* + #(#hashers: ReversibleStorageHasher,)* + #(#kargs: EncodeLike<#prefixes>),* + > HasReversibleKeyPrefix<( #( #kargs, )* )> for ( #( Key<#hashers, #current_tuple>, )* ) { + fn decode_partial_key(key_material: &[u8]) -> Result< + Self::Suffix, + #frame_support::__private::codec::Error, + > { + <#suffix_keygen>::decode_final_key(key_material).map(|k| k.0) + } + } + }; + + all_trait_impls.extend(trait_impls); + } + } + + Ok(all_trait_impls) } fn generate_tuple(idents: &[Ident]) -> TokenStream { - if idents.len() == 1 { - idents[0].to_token_stream() - } else { - quote!((#(#idents),*)) - } + if idents.len() == 1 { + idents[0].to_token_stream() + } else { + quote!((#(#idents),*)) + } } fn generate_keygen(idents: &[Ident]) -> TokenStream { - if idents.len() == 1 { - let key = &idents[0]; - let hasher = format_ident!("Hasher{}", key); + if idents.len() == 1 { + let key = &idents[0]; + let hasher = format_ident!("Hasher{}", key); - quote!(Key<#hasher, #key>) - } else { - let hashers = idents.iter().map(|ident| format_ident!("Hasher{}", ident)); + quote!(Key<#hasher, #key>) + } else { + let hashers = idents.iter().map(|ident| format_ident!("Hasher{}", ident)); - quote!((#(Key<#hashers, #idents>),*)) - } + quote!((#(Key<#hashers, #idents>),*)) + } } diff --git a/support/procedural-fork/src/match_and_insert.rs b/support/procedural-fork/src/match_and_insert.rs index a80b6e95f..aa9cc56d7 100644 --- a/support/procedural-fork/src/match_and_insert.rs +++ b/support/procedural-fork/src/match_and_insert.rs @@ -22,152 +22,138 @@ use std::iter::once; use syn::spanned::Spanned; mod keyword { - syn::custom_keyword!(target); - syn::custom_keyword!(pattern); - syn::custom_keyword!(tokens); + syn::custom_keyword!(target); + syn::custom_keyword!(pattern); + syn::custom_keyword!(tokens); } pub fn match_and_insert(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let MatchAndInsertDef { - pattern, - tokens, - target, - } = syn::parse_macro_input!(input as MatchAndInsertDef); - - match expand_in_stream(&pattern, &mut Some(tokens), target) { - Ok(stream) => stream.into(), - Err(err) => err.to_compile_error().into(), - } + let MatchAndInsertDef { pattern, tokens, target } = + syn::parse_macro_input!(input as MatchAndInsertDef); + + match expand_in_stream(&pattern, &mut Some(tokens), target) { + Ok(stream) => stream.into(), + Err(err) => err.to_compile_error().into(), + } } struct MatchAndInsertDef { - // Token stream to search and insert tokens into. - target: TokenStream, - // Pattern to match against, this is ensured to have no TokenTree::Group nor TokenTree::Literal - // (i.e. contains only Punct or Ident), and not being empty. - pattern: Vec, - // Token stream to insert after the match pattern. - tokens: TokenStream, + // Token stream to search and insert tokens into. + target: TokenStream, + // Pattern to match against, this is ensured to have no TokenTree::Group nor TokenTree::Literal + // (i.e. contains only Punct or Ident), and not being empty. + pattern: Vec, + // Token stream to insert after the match pattern. + tokens: TokenStream, } impl syn::parse::Parse for MatchAndInsertDef { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let mut target; - let _ = input.parse::()?; - let _ = input.parse::()?; - let _replace_with_bracket: syn::token::Bracket = syn::bracketed!(target in input); - let _replace_with_brace: syn::token::Brace = syn::braced!(target in target); - let target = target.parse()?; - - let mut pattern; - let _ = input.parse::()?; - let _ = input.parse::()?; - let _replace_with_bracket: syn::token::Bracket = syn::bracketed!(pattern in input); - let _replace_with_brace: syn::token::Brace = syn::braced!(pattern in pattern); - let pattern = pattern - .parse::()? - .into_iter() - .collect::>(); - - if let Some(t) = pattern.iter().find(|t| matches!(t, TokenTree::Group(_))) { - return Err(syn::Error::new(t.span(), "Unexpected group token tree")); - } - if let Some(t) = pattern.iter().find(|t| matches!(t, TokenTree::Literal(_))) { - return Err(syn::Error::new(t.span(), "Unexpected literal token tree")); - } - - if pattern.is_empty() { - return Err(syn::Error::new( - Span::call_site(), - "empty match pattern is invalid", - )); - } - - let mut tokens; - let _ = input.parse::()?; - let _ = input.parse::()?; - let _replace_with_bracket: syn::token::Bracket = syn::bracketed!(tokens in input); - let _replace_with_brace: syn::token::Brace = syn::braced!(tokens in tokens); - let tokens = tokens.parse()?; - - Ok(Self { - tokens, - pattern, - target, - }) - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut target; + let _ = input.parse::()?; + let _ = input.parse::()?; + let _replace_with_bracket: syn::token::Bracket = syn::bracketed!(target in input); + let _replace_with_brace: syn::token::Brace = syn::braced!(target in target); + let target = target.parse()?; + + let mut pattern; + let _ = input.parse::()?; + let _ = input.parse::()?; + let _replace_with_bracket: syn::token::Bracket = syn::bracketed!(pattern in input); + let _replace_with_brace: syn::token::Brace = syn::braced!(pattern in pattern); + let pattern = pattern.parse::()?.into_iter().collect::>(); + + if let Some(t) = pattern.iter().find(|t| matches!(t, TokenTree::Group(_))) { + return Err(syn::Error::new(t.span(), "Unexpected group token tree")) + } + if let Some(t) = pattern.iter().find(|t| matches!(t, TokenTree::Literal(_))) { + return Err(syn::Error::new(t.span(), "Unexpected literal token tree")) + } + + if pattern.is_empty() { + return Err(syn::Error::new(Span::call_site(), "empty match pattern is invalid")) + } + + let mut tokens; + let _ = input.parse::()?; + let _ = input.parse::()?; + let _replace_with_bracket: syn::token::Bracket = syn::bracketed!(tokens in input); + let _replace_with_brace: syn::token::Brace = syn::braced!(tokens in tokens); + let tokens = tokens.parse()?; + + Ok(Self { tokens, pattern, target }) + } } // Insert `tokens` after the first matching `pattern`. // `tokens` must be some (Option is used for internal simplification). // `pattern` must not be empty and should only contain Ident or Punct. fn expand_in_stream( - pattern: &[TokenTree], - tokens: &mut Option, - stream: TokenStream, + pattern: &[TokenTree], + tokens: &mut Option, + stream: TokenStream, ) -> syn::Result { - assert!( - tokens.is_some(), - "`tokens` must be some, Option is used because `tokens` is used only once" - ); - assert!( - !pattern.is_empty(), - "`pattern` must not be empty, otherwise there is nothing to match against" - ); - - let stream_span = stream.span(); - let mut stream = stream.into_iter(); - let mut extended = TokenStream::new(); - let mut match_cursor = 0; - - while let Some(token) = stream.next() { - match token { - TokenTree::Group(group) => { - match_cursor = 0; - let group_stream = group.stream(); - match expand_in_stream(pattern, tokens, group_stream) { - Ok(s) => { - extended.extend(once(TokenTree::Group(Group::new(group.delimiter(), s)))); - extended.extend(stream); - return Ok(extended); - } - Err(_) => { - extended.extend(once(TokenTree::Group(group))); - } - } - } - other => { - advance_match_cursor(&other, pattern, &mut match_cursor); - - extended.extend(once(other)); - - if match_cursor == pattern.len() { - extended.extend(once( - tokens.take().expect("tokens is used to replace only once"), - )); - extended.extend(stream); - return Ok(extended); - } - } - } - } - // if we reach this point, it means the stream is empty and we haven't found a matching pattern - let msg = format!("Cannot find pattern `{:?}` in given token stream", pattern); - Err(syn::Error::new(stream_span, msg)) + assert!( + tokens.is_some(), + "`tokens` must be some, Option is used because `tokens` is used only once" + ); + assert!( + !pattern.is_empty(), + "`pattern` must not be empty, otherwise there is nothing to match against" + ); + + let stream_span = stream.span(); + let mut stream = stream.into_iter(); + let mut extended = TokenStream::new(); + let mut match_cursor = 0; + + while let Some(token) = stream.next() { + match token { + TokenTree::Group(group) => { + match_cursor = 0; + let group_stream = group.stream(); + match expand_in_stream(pattern, tokens, group_stream) { + Ok(s) => { + extended.extend(once(TokenTree::Group(Group::new(group.delimiter(), s)))); + extended.extend(stream); + return Ok(extended) + }, + Err(_) => { + extended.extend(once(TokenTree::Group(group))); + }, + } + }, + other => { + advance_match_cursor(&other, pattern, &mut match_cursor); + + extended.extend(once(other)); + + if match_cursor == pattern.len() { + extended + .extend(once(tokens.take().expect("tokens is used to replace only once"))); + extended.extend(stream); + return Ok(extended) + } + }, + } + } + // if we reach this point, it means the stream is empty and we haven't found a matching pattern + let msg = format!("Cannot find pattern `{:?}` in given token stream", pattern); + Err(syn::Error::new(stream_span, msg)) } fn advance_match_cursor(other: &TokenTree, pattern: &[TokenTree], match_cursor: &mut usize) { - use TokenTree::{Ident, Punct}; - - let does_match_other_pattern = match (other, &pattern[*match_cursor]) { - (Ident(i1), Ident(i2)) => i1 == i2, - (Punct(p1), Punct(p2)) => p1.as_char() == p2.as_char(), - _ => false, - }; - - if does_match_other_pattern { - *match_cursor += 1; - } else { - *match_cursor = 0; - } + use TokenTree::{Ident, Punct}; + + let does_match_other_pattern = match (other, &pattern[*match_cursor]) { + (Ident(i1), Ident(i2)) => i1 == i2, + (Punct(p1), Punct(p2)) => p1.as_char() == p2.as_char(), + _ => false, + }; + + if does_match_other_pattern { + *match_cursor += 1; + } else { + *match_cursor = 0; + } } diff --git a/support/procedural-fork/src/no_bound/clone.rs b/support/procedural-fork/src/no_bound/clone.rs index 17039bdc8..346bf450f 100644 --- a/support/procedural-fork/src/no_bound/clone.rs +++ b/support/procedural-fork/src/no_bound/clone.rs @@ -19,93 +19,89 @@ use syn::spanned::Spanned; /// Derive Clone but do not bound any generic. pub fn derive_clone_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input = syn::parse_macro_input!(input as syn::DeriveInput); + let input = syn::parse_macro_input!(input as syn::DeriveInput); - let name = &input.ident; - let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + let name = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - let impl_ = match input.data { - syn::Data::Struct(struct_) => match struct_.fields { - syn::Fields::Named(named) => { - let fields = named.named.iter().map(|i| &i.ident).map(|i| { - quote::quote_spanned!(i.span() => - #i: ::core::clone::Clone::clone(&self.#i) - ) - }); + let impl_ = match input.data { + syn::Data::Struct(struct_) => match struct_.fields { + syn::Fields::Named(named) => { + let fields = named.named.iter().map(|i| &i.ident).map(|i| { + quote::quote_spanned!(i.span() => + #i: ::core::clone::Clone::clone(&self.#i) + ) + }); - quote::quote!( Self { #( #fields, )* } ) - } - syn::Fields::Unnamed(unnamed) => { - let fields = unnamed - .unnamed - .iter() - .enumerate() - .map(|(i, _)| syn::Index::from(i)) - .map(|i| { - quote::quote_spanned!(i.span() => - ::core::clone::Clone::clone(&self.#i) - ) - }); + quote::quote!( Self { #( #fields, )* } ) + }, + syn::Fields::Unnamed(unnamed) => { + let fields = + unnamed.unnamed.iter().enumerate().map(|(i, _)| syn::Index::from(i)).map(|i| { + quote::quote_spanned!(i.span() => + ::core::clone::Clone::clone(&self.#i) + ) + }); - quote::quote!( Self ( #( #fields, )* ) ) - } - syn::Fields::Unit => { - quote::quote!(Self) - } - }, - syn::Data::Enum(enum_) => { - let variants = enum_.variants.iter().map(|variant| { - let ident = &variant.ident; - match &variant.fields { - syn::Fields::Named(named) => { - let captured = named.named.iter().map(|i| &i.ident); - let cloned = captured.clone().map(|i| { - ::quote::quote_spanned!(i.span() => - #i: ::core::clone::Clone::clone(#i) - ) - }); - quote::quote!( - Self::#ident { #( ref #captured, )* } => Self::#ident { #( #cloned, )*} - ) - } - syn::Fields::Unnamed(unnamed) => { - let captured = unnamed - .unnamed - .iter() - .enumerate() - .map(|(i, f)| syn::Ident::new(&format!("_{}", i), f.span())); - let cloned = captured.clone().map(|i| { - quote::quote_spanned!(i.span() => - ::core::clone::Clone::clone(#i) - ) - }); - quote::quote!( - Self::#ident ( #( ref #captured, )* ) => Self::#ident ( #( #cloned, )*) - ) - } - syn::Fields::Unit => quote::quote!( Self::#ident => Self::#ident ), - } - }); + quote::quote!( Self ( #( #fields, )* ) ) + }, + syn::Fields::Unit => { + quote::quote!(Self) + }, + }, + syn::Data::Enum(enum_) => { + let variants = enum_.variants.iter().map(|variant| { + let ident = &variant.ident; + match &variant.fields { + syn::Fields::Named(named) => { + let captured = named.named.iter().map(|i| &i.ident); + let cloned = captured.clone().map(|i| { + ::quote::quote_spanned!(i.span() => + #i: ::core::clone::Clone::clone(#i) + ) + }); + quote::quote!( + Self::#ident { #( ref #captured, )* } => Self::#ident { #( #cloned, )*} + ) + }, + syn::Fields::Unnamed(unnamed) => { + let captured = unnamed + .unnamed + .iter() + .enumerate() + .map(|(i, f)| syn::Ident::new(&format!("_{}", i), f.span())); + let cloned = captured.clone().map(|i| { + quote::quote_spanned!(i.span() => + ::core::clone::Clone::clone(#i) + ) + }); + quote::quote!( + Self::#ident ( #( ref #captured, )* ) => Self::#ident ( #( #cloned, )*) + ) + }, + syn::Fields::Unit => quote::quote!( Self::#ident => Self::#ident ), + } + }); - quote::quote!(match self { - #( #variants, )* - }) - } - syn::Data::Union(_) => { - let msg = "Union type not supported by `derive(CloneNoBound)`"; - return syn::Error::new(input.span(), msg).to_compile_error().into(); - } - }; + quote::quote!(match self { + #( #variants, )* + }) + }, + syn::Data::Union(_) => { + let msg = "Union type not supported by `derive(CloneNoBound)`"; + return syn::Error::new(input.span(), msg).to_compile_error().into() + }, + }; - quote::quote!( - const _: () = { - #[automatically_derived] - impl #impl_generics ::core::clone::Clone for #name #ty_generics #where_clause { - fn clone(&self) -> Self { - #impl_ - } - } - }; - ) - .into() + quote::quote!( + const _: () = { + #[automatically_derived] + impl #impl_generics ::core::clone::Clone for #name #ty_generics #where_clause { + fn clone(&self) -> Self { + #impl_ + } + } + }; + ) + .into() } diff --git a/support/procedural-fork/src/no_bound/debug.rs b/support/procedural-fork/src/no_bound/debug.rs index 8034bb5ec..a1b3f4f0d 100644 --- a/support/procedural-fork/src/no_bound/debug.rs +++ b/support/procedural-fork/src/no_bound/debug.rs @@ -19,103 +19,103 @@ use syn::spanned::Spanned; /// Derive Debug but do not bound any generics. pub fn derive_debug_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input = syn::parse_macro_input!(input as syn::DeriveInput); + let input = syn::parse_macro_input!(input as syn::DeriveInput); - let input_ident = &input.ident; - let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + let input_ident = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - let impl_ = match input.data { - syn::Data::Struct(struct_) => match struct_.fields { - syn::Fields::Named(named) => { - let fields = - named.named.iter().map(|i| &i.ident).map( - |i| quote::quote_spanned!(i.span() => .field(stringify!(#i), &self.#i) ), - ); + let impl_ = match input.data { + syn::Data::Struct(struct_) => match struct_.fields { + syn::Fields::Named(named) => { + let fields = + named.named.iter().map(|i| &i.ident).map( + |i| quote::quote_spanned!(i.span() => .field(stringify!(#i), &self.#i) ), + ); - quote::quote!( - fmt.debug_struct(stringify!(#input_ident)) - #( #fields )* - .finish() - ) - } - syn::Fields::Unnamed(unnamed) => { - let fields = unnamed - .unnamed - .iter() - .enumerate() - .map(|(i, _)| syn::Index::from(i)) - .map(|i| quote::quote_spanned!(i.span() => .field(&self.#i) )); + quote::quote!( + fmt.debug_struct(stringify!(#input_ident)) + #( #fields )* + .finish() + ) + }, + syn::Fields::Unnamed(unnamed) => { + let fields = unnamed + .unnamed + .iter() + .enumerate() + .map(|(i, _)| syn::Index::from(i)) + .map(|i| quote::quote_spanned!(i.span() => .field(&self.#i) )); - quote::quote!( - fmt.debug_tuple(stringify!(#input_ident)) - #( #fields )* - .finish() - ) - } - syn::Fields::Unit => quote::quote!(fmt.write_str(stringify!(#input_ident))), - }, - syn::Data::Enum(enum_) => { - let variants = enum_.variants.iter().map(|variant| { - let ident = &variant.ident; - let full_variant_str = format!("{}::{}", input_ident, ident); - match &variant.fields { - syn::Fields::Named(named) => { - let captured = named.named.iter().map(|i| &i.ident); - let debugged = captured.clone().map(|i| { - quote::quote_spanned!(i.span() => - .field(stringify!(#i), &#i) - ) - }); - quote::quote!( - Self::#ident { #( ref #captured, )* } => { - fmt.debug_struct(#full_variant_str) - #( #debugged )* - .finish() - } - ) - } - syn::Fields::Unnamed(unnamed) => { - let captured = unnamed - .unnamed - .iter() - .enumerate() - .map(|(i, f)| syn::Ident::new(&format!("_{}", i), f.span())); - let debugged = captured - .clone() - .map(|i| quote::quote_spanned!(i.span() => .field(&#i))); - quote::quote!( - Self::#ident ( #( ref #captured, )* ) => { - fmt.debug_tuple(#full_variant_str) - #( #debugged )* - .finish() - } - ) - } - syn::Fields::Unit => quote::quote!( - Self::#ident => fmt.write_str(#full_variant_str) - ), - } - }); + quote::quote!( + fmt.debug_tuple(stringify!(#input_ident)) + #( #fields )* + .finish() + ) + }, + syn::Fields::Unit => quote::quote!(fmt.write_str(stringify!(#input_ident))), + }, + syn::Data::Enum(enum_) => { + let variants = enum_.variants.iter().map(|variant| { + let ident = &variant.ident; + let full_variant_str = format!("{}::{}", input_ident, ident); + match &variant.fields { + syn::Fields::Named(named) => { + let captured = named.named.iter().map(|i| &i.ident); + let debugged = captured.clone().map(|i| { + quote::quote_spanned!(i.span() => + .field(stringify!(#i), &#i) + ) + }); + quote::quote!( + Self::#ident { #( ref #captured, )* } => { + fmt.debug_struct(#full_variant_str) + #( #debugged )* + .finish() + } + ) + }, + syn::Fields::Unnamed(unnamed) => { + let captured = unnamed + .unnamed + .iter() + .enumerate() + .map(|(i, f)| syn::Ident::new(&format!("_{}", i), f.span())); + let debugged = captured + .clone() + .map(|i| quote::quote_spanned!(i.span() => .field(&#i))); + quote::quote!( + Self::#ident ( #( ref #captured, )* ) => { + fmt.debug_tuple(#full_variant_str) + #( #debugged )* + .finish() + } + ) + }, + syn::Fields::Unit => quote::quote!( + Self::#ident => fmt.write_str(#full_variant_str) + ), + } + }); - quote::quote!(match *self { - #( #variants, )* - }) - } - syn::Data::Union(_) => { - let msg = "Union type not supported by `derive(DebugNoBound)`"; - return syn::Error::new(input.span(), msg).to_compile_error().into(); - } - }; + quote::quote!(match *self { + #( #variants, )* + }) + }, + syn::Data::Union(_) => { + let msg = "Union type not supported by `derive(DebugNoBound)`"; + return syn::Error::new(input.span(), msg).to_compile_error().into() + }, + }; - quote::quote!( - const _: () = { - #[automatically_derived] - impl #impl_generics ::core::fmt::Debug for #input_ident #ty_generics #where_clause { - fn fmt(&self, fmt: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - #impl_ - } - } - }; - ) - .into() + quote::quote!( + const _: () = { + #[automatically_derived] + impl #impl_generics ::core::fmt::Debug for #input_ident #ty_generics #where_clause { + fn fmt(&self, fmt: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + #impl_ + } + } + }; + ) + .into() } diff --git a/support/procedural-fork/src/no_bound/default.rs b/support/procedural-fork/src/no_bound/default.rs index cb054878d..0524247d2 100644 --- a/support/procedural-fork/src/no_bound/default.rs +++ b/support/procedural-fork/src/no_bound/default.rs @@ -21,150 +21,141 @@ use syn::{spanned::Spanned, Data, DeriveInput, Fields}; /// Derive Default but do not bound any generic. pub fn derive_default_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input = syn::parse_macro_input!(input as DeriveInput); - - let name = &input.ident; - - let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - - let impl_ = match input.data { - Data::Struct(struct_) => match struct_.fields { - Fields::Named(named) => { - let fields = named.named.iter().map(|field| &field.ident).map(|ident| { - quote_spanned! {ident.span() => - #ident: ::core::default::Default::default() - } - }); - - quote!(Self { #( #fields, )* }) - } - Fields::Unnamed(unnamed) => { - let fields = unnamed.unnamed.iter().map(|field| { - quote_spanned! {field.span()=> - ::core::default::Default::default() - } - }); - - quote!(Self( #( #fields, )* )) - } - Fields::Unit => { - quote!(Self) - } - }, - Data::Enum(enum_) => { - if enum_.variants.is_empty() { - return syn::Error::new_spanned(name, "cannot derive Default for an empty enum") - .to_compile_error() - .into(); - } - - // all #[default] attrs with the variant they're on; i.e. a var - let default_variants = enum_ - .variants - .into_iter() - .filter(|variant| { - variant - .attrs - .iter() - .any(|attr| attr.path().is_ident("default")) - }) - .collect::>(); - - match &*default_variants { - [] => return syn::Error::new( - name.clone().span(), - "no default declared, make a variant default by placing `#[default]` above it", - ) - .into_compile_error() - .into(), - // only one variant with the #[default] attribute set - [default_variant] => { - let variant_attrs = default_variant - .attrs - .iter() - .filter(|a| a.path().is_ident("default")) - .collect::>(); - - // check that there is only one #[default] attribute on the variant - if let [first_attr, second_attr, additional_attrs @ ..] = &*variant_attrs { - let mut err = - syn::Error::new(Span::call_site(), "multiple `#[default]` attributes"); - - err.combine(syn::Error::new_spanned( - first_attr, - "`#[default]` used here", - )); - - err.extend([second_attr].into_iter().chain(additional_attrs).map( - |variant| { - syn::Error::new_spanned(variant, "`#[default]` used again here") - }, - )); - - return err.into_compile_error().into(); - } - - let variant_ident = &default_variant.ident; - - let fully_qualified_variant_path = quote!(Self::#variant_ident); - - match &default_variant.fields { - Fields::Named(named) => { - let fields = - named.named.iter().map(|field| &field.ident).map(|ident| { - quote_spanned! {ident.span()=> - #ident: ::core::default::Default::default() - } - }); - - quote!(#fully_qualified_variant_path { #( #fields, )* }) - } - Fields::Unnamed(unnamed) => { - let fields = unnamed.unnamed.iter().map(|field| { - quote_spanned! {field.span()=> - ::core::default::Default::default() - } - }); - - quote!(#fully_qualified_variant_path( #( #fields, )* )) - } - Fields::Unit => fully_qualified_variant_path, - } - } - [first, additional @ ..] => { - let mut err = syn::Error::new(Span::call_site(), "multiple declared defaults"); - - err.combine(syn::Error::new_spanned(first, "first default")); - - err.extend( - additional - .iter() - .map(|variant| syn::Error::new_spanned(variant, "additional default")), - ); - - return err.into_compile_error().into(); - } - } - } - Data::Union(union_) => { - return syn::Error::new_spanned( - union_.union_token, - "Union type not supported by `derive(DefaultNoBound)`", - ) - .to_compile_error() - .into() - } - }; - - quote!( - const _: () = { - #[automatically_derived] - impl #impl_generics ::core::default::Default for #name #ty_generics #where_clause { - fn default() -> Self { - #impl_ - } - } - }; - ) - .into() + let input = syn::parse_macro_input!(input as DeriveInput); + + let name = &input.ident; + + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + + let impl_ = match input.data { + Data::Struct(struct_) => match struct_.fields { + Fields::Named(named) => { + let fields = named.named.iter().map(|field| &field.ident).map(|ident| { + quote_spanned! {ident.span() => + #ident: ::core::default::Default::default() + } + }); + + quote!(Self { #( #fields, )* }) + }, + Fields::Unnamed(unnamed) => { + let fields = unnamed.unnamed.iter().map(|field| { + quote_spanned! {field.span()=> + ::core::default::Default::default() + } + }); + + quote!(Self( #( #fields, )* )) + }, + Fields::Unit => { + quote!(Self) + }, + }, + Data::Enum(enum_) => { + if enum_.variants.is_empty() { + return syn::Error::new_spanned(name, "cannot derive Default for an empty enum") + .to_compile_error() + .into() + } + + // all #[default] attrs with the variant they're on; i.e. a var + let default_variants = enum_ + .variants + .into_iter() + .filter(|variant| variant.attrs.iter().any(|attr| attr.path().is_ident("default"))) + .collect::>(); + + match &*default_variants { + [] => return syn::Error::new( + name.clone().span(), + "no default declared, make a variant default by placing `#[default]` above it", + ) + .into_compile_error() + .into(), + // only one variant with the #[default] attribute set + [default_variant] => { + let variant_attrs = default_variant + .attrs + .iter() + .filter(|a| a.path().is_ident("default")) + .collect::>(); + + // check that there is only one #[default] attribute on the variant + if let [first_attr, second_attr, additional_attrs @ ..] = &*variant_attrs { + let mut err = + syn::Error::new(Span::call_site(), "multiple `#[default]` attributes"); + + err.combine(syn::Error::new_spanned(first_attr, "`#[default]` used here")); + + err.extend([second_attr].into_iter().chain(additional_attrs).map( + |variant| { + syn::Error::new_spanned(variant, "`#[default]` used again here") + }, + )); + + return err.into_compile_error().into() + } + + let variant_ident = &default_variant.ident; + + let fully_qualified_variant_path = quote!(Self::#variant_ident); + + match &default_variant.fields { + Fields::Named(named) => { + let fields = + named.named.iter().map(|field| &field.ident).map(|ident| { + quote_spanned! {ident.span()=> + #ident: ::core::default::Default::default() + } + }); + + quote!(#fully_qualified_variant_path { #( #fields, )* }) + }, + Fields::Unnamed(unnamed) => { + let fields = unnamed.unnamed.iter().map(|field| { + quote_spanned! {field.span()=> + ::core::default::Default::default() + } + }); + + quote!(#fully_qualified_variant_path( #( #fields, )* )) + }, + Fields::Unit => fully_qualified_variant_path, + } + }, + [first, additional @ ..] => { + let mut err = syn::Error::new(Span::call_site(), "multiple declared defaults"); + + err.combine(syn::Error::new_spanned(first, "first default")); + + err.extend( + additional + .into_iter() + .map(|variant| syn::Error::new_spanned(variant, "additional default")), + ); + + return err.into_compile_error().into() + }, + } + }, + Data::Union(union_) => + return syn::Error::new_spanned( + union_.union_token, + "Union type not supported by `derive(DefaultNoBound)`", + ) + .to_compile_error() + .into(), + }; + + quote!( + const _: () = { + #[automatically_derived] + impl #impl_generics ::core::default::Default for #name #ty_generics #where_clause { + fn default() -> Self { + #impl_ + } + } + }; + ) + .into() } diff --git a/support/procedural-fork/src/no_bound/ord.rs b/support/procedural-fork/src/no_bound/ord.rs index 20f30eb9d..b24d27c04 100644 --- a/support/procedural-fork/src/no_bound/ord.rs +++ b/support/procedural-fork/src/no_bound/ord.rs @@ -19,57 +19,57 @@ use syn::spanned::Spanned; /// Derive Ord but do not bound any generic. pub fn derive_ord_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input: syn::DeriveInput = match syn::parse(input) { - Ok(input) => input, - Err(e) => return e.to_compile_error().into(), - }; + let input: syn::DeriveInput = match syn::parse(input) { + Ok(input) => input, + Err(e) => return e.to_compile_error().into(), + }; - let name = &input.ident; - let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + let name = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - let impl_ = match input.data { - syn::Data::Struct(struct_) => match struct_.fields { - syn::Fields::Named(named) => { - let fields = named - .named - .iter() - .map(|i| &i.ident) - .map(|i| quote::quote_spanned!(i.span() => self.#i.cmp(&other.#i) )); + let impl_ = match input.data { + syn::Data::Struct(struct_) => match struct_.fields { + syn::Fields::Named(named) => { + let fields = named + .named + .iter() + .map(|i| &i.ident) + .map(|i| quote::quote_spanned!(i.span() => self.#i.cmp(&other.#i) )); - quote::quote!( core::cmp::Ordering::Equal #( .then_with(|| #fields) )* ) - } - syn::Fields::Unnamed(unnamed) => { - let fields = unnamed - .unnamed - .iter() - .enumerate() - .map(|(i, _)| syn::Index::from(i)) - .map(|i| quote::quote_spanned!(i.span() => self.#i.cmp(&other.#i) )); + quote::quote!( core::cmp::Ordering::Equal #( .then_with(|| #fields) )* ) + }, + syn::Fields::Unnamed(unnamed) => { + let fields = unnamed + .unnamed + .iter() + .enumerate() + .map(|(i, _)| syn::Index::from(i)) + .map(|i| quote::quote_spanned!(i.span() => self.#i.cmp(&other.#i) )); - quote::quote!( core::cmp::Ordering::Equal #( .then_with(|| #fields) )* ) - } - syn::Fields::Unit => { - quote::quote!(core::cmp::Ordering::Equal) - } - }, - syn::Data::Enum(_) => { - let msg = "Enum type not supported by `derive(OrdNoBound)`"; - return syn::Error::new(input.span(), msg).to_compile_error().into(); - } - syn::Data::Union(_) => { - let msg = "Union type not supported by `derive(OrdNoBound)`"; - return syn::Error::new(input.span(), msg).to_compile_error().into(); - } - }; + quote::quote!( core::cmp::Ordering::Equal #( .then_with(|| #fields) )* ) + }, + syn::Fields::Unit => { + quote::quote!(core::cmp::Ordering::Equal) + }, + }, + syn::Data::Enum(_) => { + let msg = "Enum type not supported by `derive(OrdNoBound)`"; + return syn::Error::new(input.span(), msg).to_compile_error().into() + }, + syn::Data::Union(_) => { + let msg = "Union type not supported by `derive(OrdNoBound)`"; + return syn::Error::new(input.span(), msg).to_compile_error().into() + }, + }; - quote::quote!( - const _: () = { - impl #impl_generics core::cmp::Ord for #name #ty_generics #where_clause { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - #impl_ - } - } - }; - ) - .into() + quote::quote!( + const _: () = { + impl #impl_generics core::cmp::Ord for #name #ty_generics #where_clause { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + #impl_ + } + } + }; + ) + .into() } diff --git a/support/procedural-fork/src/no_bound/partial_eq.rs b/support/procedural-fork/src/no_bound/partial_eq.rs index 8833f6e5f..a1be71a96 100644 --- a/support/procedural-fork/src/no_bound/partial_eq.rs +++ b/support/procedural-fork/src/no_bound/partial_eq.rs @@ -19,119 +19,119 @@ use syn::spanned::Spanned; /// Derive PartialEq but do not bound any generic. pub fn derive_partial_eq_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input = syn::parse_macro_input!(input as syn::DeriveInput); + let input = syn::parse_macro_input!(input as syn::DeriveInput); - let name = &input.ident; - let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + let name = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - let impl_ = match input.data { - syn::Data::Struct(struct_) => match struct_.fields { - syn::Fields::Named(named) => { - let fields = named - .named - .iter() - .map(|i| &i.ident) - .map(|i| quote::quote_spanned!(i.span() => self.#i == other.#i )); + let impl_ = match input.data { + syn::Data::Struct(struct_) => match struct_.fields { + syn::Fields::Named(named) => { + let fields = named + .named + .iter() + .map(|i| &i.ident) + .map(|i| quote::quote_spanned!(i.span() => self.#i == other.#i )); - quote::quote!( true #( && #fields )* ) - } - syn::Fields::Unnamed(unnamed) => { - let fields = unnamed - .unnamed - .iter() - .enumerate() - .map(|(i, _)| syn::Index::from(i)) - .map(|i| quote::quote_spanned!(i.span() => self.#i == other.#i )); + quote::quote!( true #( && #fields )* ) + }, + syn::Fields::Unnamed(unnamed) => { + let fields = unnamed + .unnamed + .iter() + .enumerate() + .map(|(i, _)| syn::Index::from(i)) + .map(|i| quote::quote_spanned!(i.span() => self.#i == other.#i )); - quote::quote!( true #( && #fields )* ) - } - syn::Fields::Unit => { - quote::quote!(true) - } - }, - syn::Data::Enum(enum_) => { - let variants = - enum_.variants.iter().map(|variant| { - let ident = &variant.ident; - match &variant.fields { - syn::Fields::Named(named) => { - let names = named.named.iter().map(|i| &i.ident); - let other_names = names.clone().enumerate().map(|(n, ident)| { - syn::Ident::new(&format!("_{}", n), ident.span()) - }); + quote::quote!( true #( && #fields )* ) + }, + syn::Fields::Unit => { + quote::quote!(true) + }, + }, + syn::Data::Enum(enum_) => { + let variants = + enum_.variants.iter().map(|variant| { + let ident = &variant.ident; + match &variant.fields { + syn::Fields::Named(named) => { + let names = named.named.iter().map(|i| &i.ident); + let other_names = names.clone().enumerate().map(|(n, ident)| { + syn::Ident::new(&format!("_{}", n), ident.span()) + }); - let capture = names.clone(); - let other_capture = names - .clone() - .zip(other_names.clone()) - .map(|(i, other_i)| quote::quote!(#i: #other_i)); - let eq = names.zip(other_names).map( - |(i, other_i)| quote::quote_spanned!(i.span() => #i == #other_i), - ); - quote::quote!( - ( - Self::#ident { #( #capture, )* }, - Self::#ident { #( #other_capture, )* }, - ) => true #( && #eq )* - ) - } - syn::Fields::Unnamed(unnamed) => { - let names = unnamed - .unnamed - .iter() - .enumerate() - .map(|(i, f)| syn::Ident::new(&format!("_{}", i), f.span())); - let other_names = - unnamed.unnamed.iter().enumerate().map(|(i, f)| { - syn::Ident::new(&format!("_{}_other", i), f.span()) - }); - let eq = names.clone().zip(other_names.clone()).map( - |(i, other_i)| quote::quote_spanned!(i.span() => #i == #other_i), - ); - quote::quote!( - ( - Self::#ident ( #( #names, )* ), - Self::#ident ( #( #other_names, )* ), - ) => true #( && #eq )* - ) - } - syn::Fields::Unit => quote::quote!( (Self::#ident, Self::#ident) => true ), - } - }); + let capture = names.clone(); + let other_capture = names + .clone() + .zip(other_names.clone()) + .map(|(i, other_i)| quote::quote!(#i: #other_i)); + let eq = names.zip(other_names).map( + |(i, other_i)| quote::quote_spanned!(i.span() => #i == #other_i), + ); + quote::quote!( + ( + Self::#ident { #( #capture, )* }, + Self::#ident { #( #other_capture, )* }, + ) => true #( && #eq )* + ) + }, + syn::Fields::Unnamed(unnamed) => { + let names = unnamed + .unnamed + .iter() + .enumerate() + .map(|(i, f)| syn::Ident::new(&format!("_{}", i), f.span())); + let other_names = + unnamed.unnamed.iter().enumerate().map(|(i, f)| { + syn::Ident::new(&format!("_{}_other", i), f.span()) + }); + let eq = names.clone().zip(other_names.clone()).map( + |(i, other_i)| quote::quote_spanned!(i.span() => #i == #other_i), + ); + quote::quote!( + ( + Self::#ident ( #( #names, )* ), + Self::#ident ( #( #other_names, )* ), + ) => true #( && #eq )* + ) + }, + syn::Fields::Unit => quote::quote!( (Self::#ident, Self::#ident) => true ), + } + }); - let mut different_variants = vec![]; - for (i, i_variant) in enum_.variants.iter().enumerate() { - for (j, j_variant) in enum_.variants.iter().enumerate() { - if i != j { - let i_ident = &i_variant.ident; - let j_ident = &j_variant.ident; - different_variants.push(quote::quote!( - (Self::#i_ident { .. }, Self::#j_ident { .. }) => false - )) - } - } - } + let mut different_variants = vec![]; + for (i, i_variant) in enum_.variants.iter().enumerate() { + for (j, j_variant) in enum_.variants.iter().enumerate() { + if i != j { + let i_ident = &i_variant.ident; + let j_ident = &j_variant.ident; + different_variants.push(quote::quote!( + (Self::#i_ident { .. }, Self::#j_ident { .. }) => false + )) + } + } + } - quote::quote!( match (self, other) { - #( #variants, )* - #( #different_variants, )* - }) - } - syn::Data::Union(_) => { - let msg = "Union type not supported by `derive(PartialEqNoBound)`"; - return syn::Error::new(input.span(), msg).to_compile_error().into(); - } - }; + quote::quote!( match (self, other) { + #( #variants, )* + #( #different_variants, )* + }) + }, + syn::Data::Union(_) => { + let msg = "Union type not supported by `derive(PartialEqNoBound)`"; + return syn::Error::new(input.span(), msg).to_compile_error().into() + }, + }; - quote::quote!( - const _: () = { - #[automatically_derived] - impl #impl_generics ::core::cmp::PartialEq for #name #ty_generics #where_clause { - fn eq(&self, other: &Self) -> bool { - #impl_ - } - } - }; - ) - .into() + quote::quote!( + const _: () = { + #[automatically_derived] + impl #impl_generics ::core::cmp::PartialEq for #name #ty_generics #where_clause { + fn eq(&self, other: &Self) -> bool { + #impl_ + } + } + }; + ) + .into() } diff --git a/support/procedural-fork/src/no_bound/partial_ord.rs b/support/procedural-fork/src/no_bound/partial_ord.rs index c73199d4e..86aa42be9 100644 --- a/support/procedural-fork/src/no_bound/partial_ord.rs +++ b/support/procedural-fork/src/no_bound/partial_ord.rs @@ -19,72 +19,71 @@ use syn::spanned::Spanned; /// Derive PartialOrd but do not bound any generic. pub fn derive_partial_ord_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input: syn::DeriveInput = match syn::parse(input) { - Ok(input) => input, - Err(e) => return e.to_compile_error().into(), - }; + let input: syn::DeriveInput = match syn::parse(input) { + Ok(input) => input, + Err(e) => return e.to_compile_error().into(), + }; - let name = &input.ident; - let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + let name = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - let impl_ = - match input.data { - syn::Data::Struct(struct_) => { - match struct_.fields { - syn::Fields::Named(named) => { - let fields = named.named.iter().map(|i| &i.ident).map( - |i| quote::quote_spanned!(i.span() => self.#i.partial_cmp(&other.#i)), - ); + let impl_ = match input.data { + syn::Data::Struct(struct_) => + match struct_.fields { + syn::Fields::Named(named) => { + let fields = + named.named.iter().map(|i| &i.ident).map( + |i| quote::quote_spanned!(i.span() => self.#i.partial_cmp(&other.#i)), + ); - quote::quote!( - Some(core::cmp::Ordering::Equal) - #( - .and_then(|order| { - let next_order = #fields?; - Some(order.then(next_order)) - }) - )* - ) - } - syn::Fields::Unnamed(unnamed) => { - let fields = + quote::quote!( + Some(core::cmp::Ordering::Equal) + #( + .and_then(|order| { + let next_order = #fields?; + Some(order.then(next_order)) + }) + )* + ) + }, + syn::Fields::Unnamed(unnamed) => { + let fields = unnamed.unnamed.iter().enumerate().map(|(i, _)| syn::Index::from(i)).map( |i| quote::quote_spanned!(i.span() => self.#i.partial_cmp(&other.#i)), ); - quote::quote!( - Some(core::cmp::Ordering::Equal) - #( - .and_then(|order| { - let next_order = #fields?; - Some(order.then(next_order)) - }) - )* - ) - } - syn::Fields::Unit => { - quote::quote!(Some(core::cmp::Ordering::Equal)) - } - } - } - syn::Data::Enum(_) => { - let msg = "Enum type not supported by `derive(PartialOrdNoBound)`"; - return syn::Error::new(input.span(), msg).to_compile_error().into(); - } - syn::Data::Union(_) => { - let msg = "Union type not supported by `derive(PartialOrdNoBound)`"; - return syn::Error::new(input.span(), msg).to_compile_error().into(); - } - }; + quote::quote!( + Some(core::cmp::Ordering::Equal) + #( + .and_then(|order| { + let next_order = #fields?; + Some(order.then(next_order)) + }) + )* + ) + }, + syn::Fields::Unit => { + quote::quote!(Some(core::cmp::Ordering::Equal)) + }, + }, + syn::Data::Enum(_) => { + let msg = "Enum type not supported by `derive(PartialOrdNoBound)`"; + return syn::Error::new(input.span(), msg).to_compile_error().into() + }, + syn::Data::Union(_) => { + let msg = "Union type not supported by `derive(PartialOrdNoBound)`"; + return syn::Error::new(input.span(), msg).to_compile_error().into() + }, + }; - quote::quote!( - const _: () = { - impl #impl_generics core::cmp::PartialOrd for #name #ty_generics #where_clause { - fn partial_cmp(&self, other: &Self) -> Option { - #impl_ - } - } - }; - ) - .into() + quote::quote!( + const _: () = { + impl #impl_generics core::cmp::PartialOrd for #name #ty_generics #where_clause { + fn partial_cmp(&self, other: &Self) -> Option { + #impl_ + } + } + }; + ) + .into() } diff --git a/support/procedural-fork/src/pallet/expand/call.rs b/support/procedural-fork/src/pallet/expand/call.rs index a39e81fd1..f395872c8 100644 --- a/support/procedural-fork/src/pallet/expand/call.rs +++ b/support/procedural-fork/src/pallet/expand/call.rs @@ -16,12 +16,12 @@ // limitations under the License. use crate::{ - pallet::{ - expand::warnings::{weight_constant_warning, weight_witness_warning}, - parse::call::CallWeightDef, - Def, - }, - COUNTER, + pallet::{ + expand::warnings::{weight_constant_warning, weight_witness_warning}, + parse::call::CallWeightDef, + Def, + }, + COUNTER, }; use proc_macro2::TokenStream as TokenStream2; use proc_macro_warning::Warning; @@ -32,56 +32,45 @@ use syn::spanned::Spanned; /// * Generate enum call and implement various trait on it. /// * Implement Callable and call_function on `Pallet` pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream { - let (span, where_clause, methods, docs) = match def.call.as_ref() { - Some(call) => { - let span = call.attr_span; - let where_clause = call.where_clause.clone(); - let methods = call.methods.clone(); - let docs = call.docs.clone(); - - (span, where_clause, methods, docs) - } - None => ( - def.item.span(), - def.config.where_clause.clone(), - Vec::new(), - Vec::new(), - ), - }; - let frame_support = &def.frame_support; - let frame_system = &def.frame_system; - let type_impl_gen = &def.type_impl_generics(span); - let type_decl_bounded_gen = &def.type_decl_bounded_generics(span); - let type_use_gen = &def.type_use_generics(span); - let call_ident = syn::Ident::new("Call", span); - let pallet_ident = &def.pallet_struct.pallet; - - let fn_name = methods - .iter() - .map(|method| &method.name) - .collect::>(); - let call_index = methods - .iter() - .map(|method| method.call_index) - .collect::>(); - let new_call_variant_fn_name = fn_name - .iter() - .map(|fn_name| quote::format_ident!("new_call_variant_{}", fn_name)) - .collect::>(); - - let new_call_variant_doc = fn_name - .iter() - .map(|fn_name| format!("Create a call with the variant `{}`.", fn_name)) - .collect::>(); - - let mut call_index_warnings = Vec::new(); - // Emit a warning for each call that is missing `call_index` when not in dev-mode. - for method in &methods { - if method.explicit_call_index || def.dev_mode { - continue; - } - - let warning = Warning::new_deprecated("ImplicitCallIndex") + let (span, where_clause, methods, docs) = match def.call.as_ref() { + Some(call) => { + let span = call.attr_span; + let where_clause = call.where_clause.clone(); + let methods = call.methods.clone(); + let docs = call.docs.clone(); + + (span, where_clause, methods, docs) + }, + None => (def.item.span(), def.config.where_clause.clone(), Vec::new(), Vec::new()), + }; + let frame_support = &def.frame_support; + let frame_system = &def.frame_system; + let type_impl_gen = &def.type_impl_generics(span); + let type_decl_bounded_gen = &def.type_decl_bounded_generics(span); + let type_use_gen = &def.type_use_generics(span); + let call_ident = syn::Ident::new("Call", span); + let pallet_ident = &def.pallet_struct.pallet; + + let fn_name = methods.iter().map(|method| &method.name).collect::>(); + let call_index = methods.iter().map(|method| method.call_index).collect::>(); + let new_call_variant_fn_name = fn_name + .iter() + .map(|fn_name| quote::format_ident!("new_call_variant_{}", fn_name)) + .collect::>(); + + let new_call_variant_doc = fn_name + .iter() + .map(|fn_name| format!("Create a call with the variant `{}`.", fn_name)) + .collect::>(); + + let mut call_index_warnings = Vec::new(); + // Emit a warning for each call that is missing `call_index` when not in dev-mode. + for method in &methods { + if method.explicit_call_index || def.dev_mode { + continue + } + + let warning = Warning::new_deprecated("ImplicitCallIndex") .index(call_index_warnings.len()) .old("use implicit call indices") .new("ensure that all calls have a `pallet::call_index` attribute or put the pallet into `dev` mode") @@ -91,408 +80,373 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream { ]) .span(method.name.span()) .build_or_panic(); - call_index_warnings.push(warning); - } - - let mut fn_weight = Vec::::new(); - let mut weight_warnings = Vec::new(); - for method in &methods { - match &method.weight { - CallWeightDef::DevModeDefault => fn_weight.push(syn::parse_quote!(0)), - CallWeightDef::Immediate(e) => { - weight_constant_warning(e, def.dev_mode, &mut weight_warnings); - weight_witness_warning(method, def.dev_mode, &mut weight_warnings); - - fn_weight.push(e.into_token_stream()); - } - CallWeightDef::Inherited => { - let pallet_weight = def - .call - .as_ref() - .expect("we have methods; we have calls; qed") - .inherited_call_weight - .as_ref() - .expect("the parser prevents this"); - - // Expand `<::WeightInfo>::call_name()`. - let t = &pallet_weight.typename; - let n = &method.name; - fn_weight.push(quote!({ < #t > :: #n () })); - } - } - } - debug_assert_eq!(fn_weight.len(), methods.len()); - - let fn_doc = methods - .iter() - .map(|method| &method.docs) - .collect::>(); - - let args_name = methods - .iter() - .map(|method| { - method - .args - .iter() - .map(|(_, name, _)| name.clone()) - .collect::>() - }) - .collect::>(); - - let args_name_stripped = methods - .iter() - .map(|method| { - method - .args - .iter() - .map(|(_, name, _)| { - syn::Ident::new(name.to_string().trim_start_matches('_'), name.span()) - }) - .collect::>() - }) - .collect::>(); - - let make_args_name_pattern = |ref_tok| { - args_name - .iter() - .zip(args_name_stripped.iter()) - .map(|(args_name, args_name_stripped)| { - args_name - .iter() - .zip(args_name_stripped) - .map(|(args_name, args_name_stripped)| { - if args_name == args_name_stripped { - quote::quote!( #ref_tok #args_name ) - } else { - quote::quote!( #args_name_stripped: #ref_tok #args_name ) - } - }) - .collect::>() - }) - .collect::>() - }; - - let args_name_pattern = make_args_name_pattern(None); - let args_name_pattern_ref = make_args_name_pattern(Some(quote::quote!(ref))); - - let args_type = methods - .iter() - .map(|method| { - method - .args - .iter() - .map(|(_, _, type_)| type_.clone()) - .collect::>() - }) - .collect::>(); - - let args_compact_attr = methods.iter().map(|method| { - method - .args - .iter() - .map(|(is_compact, _, type_)| { - if *is_compact { - quote::quote_spanned!(type_.span() => #[codec(compact)] ) - } else { - quote::quote!() - } - }) - .collect::>() - }); - - let default_docs = [syn::parse_quote!( - r"Contains a variant per dispatchable extrinsic that this pallet has." - )]; - let docs = if docs.is_empty() { - &default_docs[..] - } else { - &docs[..] - }; - - let maybe_compile_error = if def.call.is_none() { - quote::quote! { - compile_error!(concat!( - "`", - stringify!($pallet_name), - "` does not have #[pallet::call] defined, perhaps you should remove `Call` from \ - construct_runtime?", - )); - } - } else { - proc_macro2::TokenStream::new() - }; - - let count = COUNTER.with(|counter| counter.borrow_mut().inc()); - let macro_ident = syn::Ident::new(&format!("__is_call_part_defined_{}", count), span); - - let capture_docs = if cfg!(feature = "no-metadata-docs") { - "never" - } else { - "always" - }; - - // Wrap all calls inside of storage layers - if let Some(syn::Item::Impl(item_impl)) = def - .call - .as_ref() - .map(|c| &mut def.item.content.as_mut().expect("Checked by def parser").1[c.index]) - { - item_impl.items.iter_mut().for_each(|i| { - if let syn::ImplItem::Fn(method) = i { - let block = &method.block; - method.block = syn::parse_quote! {{ - // We execute all dispatchable in a new storage layer, allowing them - // to return an error at any point, and undoing any storage changes. - #frame_support::storage::with_storage_layer(|| #block) - }}; - } - }); - } - - // Extracts #[allow] attributes, necessary so that we don't run into compiler warnings - let maybe_allow_attrs = methods - .iter() - .map(|method| { - method - .attrs - .iter() - .find(|attr| attr.path().is_ident("allow")) - .map_or(proc_macro2::TokenStream::new(), |attr| { - attr.to_token_stream() - }) - }) - .collect::>(); - - let cfg_attrs = methods - .iter() - .map(|method| { - let attrs = method - .cfg_attrs - .iter() - .map(|attr| attr.to_token_stream()) - .collect::>(); - quote::quote!( #( #attrs )* ) - }) - .collect::>(); - - let feeless_check = methods - .iter() - .map(|method| &method.feeless_check) - .collect::>(); - let feeless_check_result = - feeless_check - .iter() - .zip(args_name.iter()) - .map(|(feeless_check, arg_name)| { - if let Some(feeless_check) = feeless_check { - quote::quote!(#feeless_check(origin, #( #arg_name, )*)) - } else { - quote::quote!(false) - } - }); - - quote::quote_spanned!(span => - #[doc(hidden)] - mod warnings { - #( - #call_index_warnings - )* - #( - #weight_warnings - )* - } - - #[allow(unused_imports)] - #[doc(hidden)] - pub mod __substrate_call_check { - #[macro_export] - #[doc(hidden)] - macro_rules! #macro_ident { - ($pallet_name:ident) => { - #maybe_compile_error - }; - } - - #[doc(hidden)] - pub use #macro_ident as is_call_part_defined; - } - - #( #[doc = #docs] )* - #[derive( - #frame_support::RuntimeDebugNoBound, - #frame_support::CloneNoBound, - #frame_support::EqNoBound, - #frame_support::PartialEqNoBound, - #frame_support::__private::codec::Encode, - #frame_support::__private::codec::Decode, - #frame_support::__private::scale_info::TypeInfo, - )] - #[codec(encode_bound())] - #[codec(decode_bound())] - #[scale_info(skip_type_params(#type_use_gen), capture_docs = #capture_docs)] - #[allow(non_camel_case_types)] - pub enum #call_ident<#type_decl_bounded_gen> #where_clause { - #[doc(hidden)] - #[codec(skip)] - __Ignore( - ::core::marker::PhantomData<(#type_use_gen,)>, - #frame_support::Never, - ), - #( - #cfg_attrs - #( #[doc = #fn_doc] )* - #[codec(index = #call_index)] - #fn_name { - #( - #[allow(missing_docs)] - #args_compact_attr #args_name_stripped: #args_type - ),* - }, - )* - } - - impl<#type_impl_gen> #call_ident<#type_use_gen> #where_clause { - #( - #cfg_attrs - #[doc = #new_call_variant_doc] - pub fn #new_call_variant_fn_name( - #( #args_name_stripped: #args_type ),* - ) -> Self { - Self::#fn_name { - #( #args_name_stripped ),* - } - } - )* - } - - impl<#type_impl_gen> #frame_support::dispatch::GetDispatchInfo - for #call_ident<#type_use_gen> - #where_clause - { - fn get_dispatch_info(&self) -> #frame_support::dispatch::DispatchInfo { - match *self { - #( - #cfg_attrs - Self::#fn_name { #( #args_name_pattern_ref, )* } => { - let __pallet_base_weight = #fn_weight; - - let __pallet_weight = < - dyn #frame_support::dispatch::WeighData<( #( & #args_type, )* )> - >::weigh_data(&__pallet_base_weight, ( #( #args_name, )* )); - - let __pallet_class = < - dyn #frame_support::dispatch::ClassifyDispatch< - ( #( & #args_type, )* ) - > - >::classify_dispatch(&__pallet_base_weight, ( #( #args_name, )* )); - - let __pallet_pays_fee = < - dyn #frame_support::dispatch::PaysFee<( #( & #args_type, )* )> - >::pays_fee(&__pallet_base_weight, ( #( #args_name, )* )); - - #frame_support::dispatch::DispatchInfo { - weight: __pallet_weight, - class: __pallet_class, - pays_fee: __pallet_pays_fee, - } - }, - )* - Self::__Ignore(_, _) => unreachable!("__Ignore cannot be used"), - } - } - } - - impl<#type_impl_gen> #frame_support::dispatch::CheckIfFeeless for #call_ident<#type_use_gen> - #where_clause - { - type Origin = #frame_system::pallet_prelude::OriginFor; - #[allow(unused_variables)] - fn is_feeless(&self, origin: &Self::Origin) -> bool { - match *self { - #( - #cfg_attrs - Self::#fn_name { #( #args_name_pattern_ref, )* } => { - #feeless_check_result - }, - )* - Self::__Ignore(_, _) => unreachable!("__Ignore cannot be used"), - } - } - } - - impl<#type_impl_gen> #frame_support::traits::GetCallName for #call_ident<#type_use_gen> - #where_clause - { - fn get_call_name(&self) -> &'static str { - match *self { - #( #cfg_attrs Self::#fn_name { .. } => stringify!(#fn_name), )* - Self::__Ignore(_, _) => unreachable!("__PhantomItem cannot be used."), - } - } - - fn get_call_names() -> &'static [&'static str] { - &[ #( #cfg_attrs stringify!(#fn_name), )* ] - } - } - - impl<#type_impl_gen> #frame_support::traits::GetCallIndex for #call_ident<#type_use_gen> - #where_clause - { - fn get_call_index(&self) -> u8 { - match *self { - #( #cfg_attrs Self::#fn_name { .. } => #call_index, )* - Self::__Ignore(_, _) => unreachable!("__PhantomItem cannot be used."), - } - } - - fn get_call_indices() -> &'static [u8] { - &[ #( #cfg_attrs #call_index, )* ] - } - } - - impl<#type_impl_gen> #frame_support::traits::UnfilteredDispatchable - for #call_ident<#type_use_gen> - #where_clause - { - type RuntimeOrigin = #frame_system::pallet_prelude::OriginFor; - fn dispatch_bypass_filter( - self, - origin: Self::RuntimeOrigin - ) -> #frame_support::dispatch::DispatchResultWithPostInfo { - #frame_support::dispatch_context::run_in_context(|| { - match self { - #( - #cfg_attrs - Self::#fn_name { #( #args_name_pattern, )* } => { - #frame_support::__private::sp_tracing::enter_span!( - #frame_support::__private::sp_tracing::trace_span!(stringify!(#fn_name)) - ); - #maybe_allow_attrs - <#pallet_ident<#type_use_gen>>::#fn_name(origin, #( #args_name, )* ) - .map(Into::into).map_err(Into::into) - }, - )* - Self::__Ignore(_, _) => { - let _ = origin; // Use origin for empty Call enum - unreachable!("__PhantomItem cannot be used."); - }, - } - }) - } - } - - impl<#type_impl_gen> #frame_support::dispatch::Callable for #pallet_ident<#type_use_gen> - #where_clause - { - type RuntimeCall = #call_ident<#type_use_gen>; - } - - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #where_clause { - #[allow(dead_code)] - #[doc(hidden)] - pub fn call_functions() -> #frame_support::__private::metadata_ir::PalletCallMetadataIR { - #frame_support::__private::scale_info::meta_type::<#call_ident<#type_use_gen>>().into() - } - } - ) + call_index_warnings.push(warning); + } + + let mut fn_weight = Vec::::new(); + let mut weight_warnings = Vec::new(); + for method in &methods { + match &method.weight { + CallWeightDef::DevModeDefault => fn_weight.push(syn::parse_quote!(0)), + CallWeightDef::Immediate(e) => { + weight_constant_warning(e, def.dev_mode, &mut weight_warnings); + weight_witness_warning(method, def.dev_mode, &mut weight_warnings); + + fn_weight.push(e.into_token_stream()); + }, + CallWeightDef::Inherited => { + let pallet_weight = def + .call + .as_ref() + .expect("we have methods; we have calls; qed") + .inherited_call_weight + .as_ref() + .expect("the parser prevents this"); + + // Expand `<::WeightInfo>::call_name()`. + let t = &pallet_weight.typename; + let n = &method.name; + fn_weight.push(quote!({ < #t > :: #n () })); + }, + } + } + debug_assert_eq!(fn_weight.len(), methods.len()); + + let fn_doc = methods.iter().map(|method| &method.docs).collect::>(); + + let args_name = methods + .iter() + .map(|method| method.args.iter().map(|(_, name, _)| name.clone()).collect::>()) + .collect::>(); + + let args_name_stripped = methods + .iter() + .map(|method| { + method + .args + .iter() + .map(|(_, name, _)| { + syn::Ident::new(name.to_string().trim_start_matches('_'), name.span()) + }) + .collect::>() + }) + .collect::>(); + + let make_args_name_pattern = |ref_tok| { + args_name + .iter() + .zip(args_name_stripped.iter()) + .map(|(args_name, args_name_stripped)| { + args_name + .iter() + .zip(args_name_stripped) + .map(|(args_name, args_name_stripped)| { + if args_name == args_name_stripped { + quote::quote!( #ref_tok #args_name ) + } else { + quote::quote!( #args_name_stripped: #ref_tok #args_name ) + } + }) + .collect::>() + }) + .collect::>() + }; + + let args_name_pattern = make_args_name_pattern(None); + let args_name_pattern_ref = make_args_name_pattern(Some(quote::quote!(ref))); + + let args_type = methods + .iter() + .map(|method| method.args.iter().map(|(_, _, type_)| type_.clone()).collect::>()) + .collect::>(); + + let args_compact_attr = methods.iter().map(|method| { + method + .args + .iter() + .map(|(is_compact, _, type_)| { + if *is_compact { + quote::quote_spanned!(type_.span() => #[codec(compact)] ) + } else { + quote::quote!() + } + }) + .collect::>() + }); + + let default_docs = + [syn::parse_quote!(r"Contains a variant per dispatchable extrinsic that this pallet has.")]; + let docs = if docs.is_empty() { &default_docs[..] } else { &docs[..] }; + + let maybe_compile_error = if def.call.is_none() { + quote::quote! { + compile_error!(concat!( + "`", + stringify!($pallet_name), + "` does not have #[pallet::call] defined, perhaps you should remove `Call` from \ + construct_runtime?", + )); + } + } else { + proc_macro2::TokenStream::new() + }; + + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + let macro_ident = syn::Ident::new(&format!("__is_call_part_defined_{}", count), span); + + let capture_docs = if cfg!(feature = "no-metadata-docs") { "never" } else { "always" }; + + // Wrap all calls inside of storage layers + if let Some(syn::Item::Impl(item_impl)) = def + .call + .as_ref() + .map(|c| &mut def.item.content.as_mut().expect("Checked by def parser").1[c.index]) + { + item_impl.items.iter_mut().for_each(|i| { + if let syn::ImplItem::Fn(method) = i { + let block = &method.block; + method.block = syn::parse_quote! {{ + // We execute all dispatchable in a new storage layer, allowing them + // to return an error at any point, and undoing any storage changes. + #frame_support::storage::with_storage_layer(|| #block) + }}; + } + }); + } + + // Extracts #[allow] attributes, necessary so that we don't run into compiler warnings + let maybe_allow_attrs = methods + .iter() + .map(|method| { + method + .attrs + .iter() + .find(|attr| attr.path().is_ident("allow")) + .map_or(proc_macro2::TokenStream::new(), |attr| attr.to_token_stream()) + }) + .collect::>(); + + let cfg_attrs = methods + .iter() + .map(|method| { + let attrs = + method.cfg_attrs.iter().map(|attr| attr.to_token_stream()).collect::>(); + quote::quote!( #( #attrs )* ) + }) + .collect::>(); + + let feeless_check = methods.iter().map(|method| &method.feeless_check).collect::>(); + let feeless_check_result = + feeless_check.iter().zip(args_name.iter()).map(|(feeless_check, arg_name)| { + if let Some(feeless_check) = feeless_check { + quote::quote!(#feeless_check(origin, #( #arg_name, )*)) + } else { + quote::quote!(false) + } + }); + + quote::quote_spanned!(span => + #[doc(hidden)] + mod warnings { + #( + #call_index_warnings + )* + #( + #weight_warnings + )* + } + + #[allow(unused_imports)] + #[doc(hidden)] + pub mod __substrate_call_check { + #[macro_export] + #[doc(hidden)] + macro_rules! #macro_ident { + ($pallet_name:ident) => { + #maybe_compile_error + }; + } + + #[doc(hidden)] + pub use #macro_ident as is_call_part_defined; + } + + #( #[doc = #docs] )* + #[derive( + #frame_support::RuntimeDebugNoBound, + #frame_support::CloneNoBound, + #frame_support::EqNoBound, + #frame_support::PartialEqNoBound, + #frame_support::__private::codec::Encode, + #frame_support::__private::codec::Decode, + #frame_support::__private::scale_info::TypeInfo, + )] + #[codec(encode_bound())] + #[codec(decode_bound())] + #[scale_info(skip_type_params(#type_use_gen), capture_docs = #capture_docs)] + #[allow(non_camel_case_types)] + pub enum #call_ident<#type_decl_bounded_gen> #where_clause { + #[doc(hidden)] + #[codec(skip)] + __Ignore( + ::core::marker::PhantomData<(#type_use_gen,)>, + #frame_support::Never, + ), + #( + #cfg_attrs + #( #[doc = #fn_doc] )* + #[codec(index = #call_index)] + #fn_name { + #( + #[allow(missing_docs)] + #args_compact_attr #args_name_stripped: #args_type + ),* + }, + )* + } + + impl<#type_impl_gen> #call_ident<#type_use_gen> #where_clause { + #( + #cfg_attrs + #[doc = #new_call_variant_doc] + pub fn #new_call_variant_fn_name( + #( #args_name_stripped: #args_type ),* + ) -> Self { + Self::#fn_name { + #( #args_name_stripped ),* + } + } + )* + } + + impl<#type_impl_gen> #frame_support::dispatch::GetDispatchInfo + for #call_ident<#type_use_gen> + #where_clause + { + fn get_dispatch_info(&self) -> #frame_support::dispatch::DispatchInfo { + match *self { + #( + #cfg_attrs + Self::#fn_name { #( #args_name_pattern_ref, )* } => { + let __pallet_base_weight = #fn_weight; + + let __pallet_weight = < + dyn #frame_support::dispatch::WeighData<( #( & #args_type, )* )> + >::weigh_data(&__pallet_base_weight, ( #( #args_name, )* )); + + let __pallet_class = < + dyn #frame_support::dispatch::ClassifyDispatch< + ( #( & #args_type, )* ) + > + >::classify_dispatch(&__pallet_base_weight, ( #( #args_name, )* )); + + let __pallet_pays_fee = < + dyn #frame_support::dispatch::PaysFee<( #( & #args_type, )* )> + >::pays_fee(&__pallet_base_weight, ( #( #args_name, )* )); + + #frame_support::dispatch::DispatchInfo { + weight: __pallet_weight, + class: __pallet_class, + pays_fee: __pallet_pays_fee, + } + }, + )* + Self::__Ignore(_, _) => unreachable!("__Ignore cannot be used"), + } + } + } + + impl<#type_impl_gen> #frame_support::dispatch::CheckIfFeeless for #call_ident<#type_use_gen> + #where_clause + { + type Origin = #frame_system::pallet_prelude::OriginFor; + #[allow(unused_variables)] + fn is_feeless(&self, origin: &Self::Origin) -> bool { + match *self { + #( + #cfg_attrs + Self::#fn_name { #( #args_name_pattern_ref, )* } => { + #feeless_check_result + }, + )* + Self::__Ignore(_, _) => unreachable!("__Ignore cannot be used"), + } + } + } + + impl<#type_impl_gen> #frame_support::traits::GetCallName for #call_ident<#type_use_gen> + #where_clause + { + fn get_call_name(&self) -> &'static str { + match *self { + #( #cfg_attrs Self::#fn_name { .. } => stringify!(#fn_name), )* + Self::__Ignore(_, _) => unreachable!("__PhantomItem cannot be used."), + } + } + + fn get_call_names() -> &'static [&'static str] { + &[ #( #cfg_attrs stringify!(#fn_name), )* ] + } + } + + impl<#type_impl_gen> #frame_support::traits::GetCallIndex for #call_ident<#type_use_gen> + #where_clause + { + fn get_call_index(&self) -> u8 { + match *self { + #( #cfg_attrs Self::#fn_name { .. } => #call_index, )* + Self::__Ignore(_, _) => unreachable!("__PhantomItem cannot be used."), + } + } + + fn get_call_indices() -> &'static [u8] { + &[ #( #cfg_attrs #call_index, )* ] + } + } + + impl<#type_impl_gen> #frame_support::traits::UnfilteredDispatchable + for #call_ident<#type_use_gen> + #where_clause + { + type RuntimeOrigin = #frame_system::pallet_prelude::OriginFor; + fn dispatch_bypass_filter( + self, + origin: Self::RuntimeOrigin + ) -> #frame_support::dispatch::DispatchResultWithPostInfo { + #frame_support::dispatch_context::run_in_context(|| { + match self { + #( + #cfg_attrs + Self::#fn_name { #( #args_name_pattern, )* } => { + #frame_support::__private::sp_tracing::enter_span!( + #frame_support::__private::sp_tracing::trace_span!(stringify!(#fn_name)) + ); + #maybe_allow_attrs + <#pallet_ident<#type_use_gen>>::#fn_name(origin, #( #args_name, )* ) + .map(Into::into).map_err(Into::into) + }, + )* + Self::__Ignore(_, _) => { + let _ = origin; // Use origin for empty Call enum + unreachable!("__PhantomItem cannot be used."); + }, + } + }) + } + } + + impl<#type_impl_gen> #frame_support::dispatch::Callable for #pallet_ident<#type_use_gen> + #where_clause + { + type RuntimeCall = #call_ident<#type_use_gen>; + } + + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #where_clause { + #[allow(dead_code)] + #[doc(hidden)] + pub fn call_functions() -> #frame_support::__private::metadata_ir::PalletCallMetadataIR { + #frame_support::__private::scale_info::meta_type::<#call_ident<#type_use_gen>>().into() + } + } + ) } diff --git a/support/procedural-fork/src/pallet/expand/composite.rs b/support/procedural-fork/src/pallet/expand/composite.rs index 49c0ad675..d449afe8f 100644 --- a/support/procedural-fork/src/pallet/expand/composite.rs +++ b/support/procedural-fork/src/pallet/expand/composite.rs @@ -20,21 +20,21 @@ use proc_macro2::TokenStream; /// Expands `composite_enum` and adds the `VariantCount` implementation for it. pub fn expand_composites(def: &mut Def) -> TokenStream { - let mut expand = quote::quote!(); - let frame_support = &def.frame_support; + let mut expand = quote::quote!(); + let frame_support = &def.frame_support; - for composite in &def.composites { - let name = &composite.ident; - let (impl_generics, ty_generics, where_clause) = composite.generics.split_for_impl(); - let variants_count = composite.variant_count; + for composite in &def.composites { + let name = &composite.ident; + let (impl_generics, ty_generics, where_clause) = composite.generics.split_for_impl(); + let variants_count = composite.variant_count; - // add `VariantCount` implementation for `composite_enum` - expand.extend(quote::quote_spanned!(composite.attr_span => + // add `VariantCount` implementation for `composite_enum` + expand.extend(quote::quote_spanned!(composite.attr_span => impl #impl_generics #frame_support::traits::VariantCount for #name #ty_generics #where_clause { const VARIANT_COUNT: u32 = #variants_count; } )); - } + } - expand + expand } diff --git a/support/procedural-fork/src/pallet/expand/config.rs b/support/procedural-fork/src/pallet/expand/config.rs index 55ac72537..5cf4035a8 100644 --- a/support/procedural-fork/src/pallet/expand/config.rs +++ b/support/procedural-fork/src/pallet/expand/config.rs @@ -23,20 +23,20 @@ use syn::{parse_quote, Item}; /// /// * Generate default rust doc pub fn expand_config(def: &mut Def) -> TokenStream { - let config = &def.config; - let config_item = { - let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[config.index]; - if let Item::Trait(item) = item { - item - } else { - unreachable!("Checked by config parser") - } - }; + let config = &def.config; + let config_item = { + let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[config.index]; + if let Item::Trait(item) = item { + item + } else { + unreachable!("Checked by config parser") + } + }; - config_item.attrs.insert( - 0, - parse_quote!( - #[doc = r" + config_item.attrs.insert( + 0, + parse_quote!( + #[doc = r" Configuration trait of this pallet. The main purpose of this trait is to act as an interface between this pallet and the runtime in @@ -44,54 +44,54 @@ which it is embedded in. A type, function, or constant in this trait is essentia configured by the runtime that includes this pallet. Consequently, a runtime that wants to include this pallet must implement this trait." - ] - ), - ); + ] + ), + ); - // we only emit `DefaultConfig` if there are trait items, so an empty `DefaultConfig` is - // impossible consequently. - match &config.default_sub_trait { - Some(default_sub_trait) if !default_sub_trait.items.is_empty() => { - let trait_items = &default_sub_trait - .items - .iter() - .map(|item| { - if item.1 { - if let syn::TraitItem::Type(item) = item.0.clone() { - let mut item = item.clone(); - item.bounds.clear(); - syn::TraitItem::Type(item) - } else { - item.0.clone() - } - } else { - item.0.clone() - } - }) - .collect::>(); + // we only emit `DefaultConfig` if there are trait items, so an empty `DefaultConfig` is + // impossible consequently. + match &config.default_sub_trait { + Some(default_sub_trait) if default_sub_trait.items.len() > 0 => { + let trait_items = &default_sub_trait + .items + .iter() + .map(|item| { + if item.1 { + if let syn::TraitItem::Type(item) = item.0.clone() { + let mut item = item.clone(); + item.bounds.clear(); + syn::TraitItem::Type(item) + } else { + item.0.clone() + } + } else { + item.0.clone() + } + }) + .collect::>(); - let type_param_bounds = if default_sub_trait.has_system { - let system = &def.frame_system; - quote::quote!(: #system::DefaultConfig) - } else { - quote::quote!() - }; + let type_param_bounds = if default_sub_trait.has_system { + let system = &def.frame_system; + quote::quote!(: #system::DefaultConfig) + } else { + quote::quote!() + }; - quote!( - /// Based on [`Config`]. Auto-generated by - /// [`#[pallet::config(with_default)]`](`frame_support::pallet_macros::config`). - /// Can be used in tandem with - /// [`#[register_default_config]`](`frame_support::register_default_config`) and - /// [`#[derive_impl]`](`frame_support::derive_impl`) to derive test config traits - /// based on existing pallet config traits in a safe and developer-friendly way. - /// - /// See [here](`frame_support::pallet_macros::config`) for more information and caveats about - /// the auto-generated `DefaultConfig` trait and how it is generated. - pub trait DefaultConfig #type_param_bounds { - #(#trait_items)* - } - ) - } - _ => Default::default(), - } + quote!( + /// Based on [`Config`]. Auto-generated by + /// [`#[pallet::config(with_default)]`](`frame_support::pallet_macros::config`). + /// Can be used in tandem with + /// [`#[register_default_config]`](`frame_support::register_default_config`) and + /// [`#[derive_impl]`](`frame_support::derive_impl`) to derive test config traits + /// based on existing pallet config traits in a safe and developer-friendly way. + /// + /// See [here](`frame_support::pallet_macros::config`) for more information and caveats about + /// the auto-generated `DefaultConfig` trait and how it is generated. + pub trait DefaultConfig #type_param_bounds { + #(#trait_items)* + } + ) + }, + _ => Default::default(), + } } diff --git a/support/procedural-fork/src/pallet/expand/constants.rs b/support/procedural-fork/src/pallet/expand/constants.rs index 5153ccf49..57fa8b7f3 100644 --- a/support/procedural-fork/src/pallet/expand/constants.rs +++ b/support/procedural-fork/src/pallet/expand/constants.rs @@ -18,99 +18,91 @@ use crate::pallet::Def; struct ConstDef { - /// Name of the associated type. - pub ident: syn::Ident, - /// The type in Get, e.g. `u32` in `type Foo: Get;`, but `Self` is replaced by `T` - pub type_: syn::Type, - /// The doc associated - pub doc: Vec, - /// default_byte implementation - pub default_byte_impl: proc_macro2::TokenStream, - /// Constant name for Metadata (optional) - pub metadata_name: Option, + /// Name of the associated type. + pub ident: syn::Ident, + /// The type in Get, e.g. `u32` in `type Foo: Get;`, but `Self` is replaced by `T` + pub type_: syn::Type, + /// The doc associated + pub doc: Vec, + /// default_byte implementation + pub default_byte_impl: proc_macro2::TokenStream, + /// Constant name for Metadata (optional) + pub metadata_name: Option, } /// /// * Impl fn module_constant_metadata for pallet. pub fn expand_constants(def: &mut Def) -> proc_macro2::TokenStream { - let frame_support = &def.frame_support; - let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site()); - let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site()); - let pallet_ident = &def.pallet_struct.pallet; - let trait_use_gen = &def.trait_use_generics(proc_macro2::Span::call_site()); - - let mut where_clauses = vec![&def.config.where_clause]; - where_clauses.extend(def.extra_constants.iter().map(|d| &d.where_clause)); - let completed_where_clause = super::merge_where_clauses(&where_clauses); - - let config_consts = def.config.consts_metadata.iter().map(|const_| { - let ident = &const_.ident; - let const_type = &const_.type_; - - ConstDef { - ident: const_.ident.clone(), - type_: const_.type_.clone(), - doc: const_.doc.clone(), - default_byte_impl: quote::quote!( - let value = <::#ident as - #frame_support::traits::Get<#const_type>>::get(); - #frame_support::__private::codec::Encode::encode(&value) - ), - metadata_name: None, - } - }); - - let extra_consts = def - .extra_constants - .iter() - .flat_map(|d| &d.extra_constants) - .map(|const_| { - let ident = &const_.ident; - - ConstDef { - ident: const_.ident.clone(), - type_: const_.type_.clone(), - doc: const_.doc.clone(), - default_byte_impl: quote::quote!( - let value = >::#ident(); - #frame_support::__private::codec::Encode::encode(&value) - ), - metadata_name: const_.metadata_name.clone(), - } - }); - - let consts = config_consts.chain(extra_consts).map(|const_| { - let const_type = &const_.type_; - let ident_str = format!("{}", const_.metadata_name.unwrap_or(const_.ident)); - - let no_docs = vec![]; - let doc = if cfg!(feature = "no-metadata-docs") { - &no_docs - } else { - &const_.doc - }; - - let default_byte_impl = &const_.default_byte_impl; - - quote::quote!({ - #frame_support::__private::metadata_ir::PalletConstantMetadataIR { - name: #ident_str, - ty: #frame_support::__private::scale_info::meta_type::<#const_type>(), - value: { #default_byte_impl }, - docs: #frame_support::__private::sp_std::vec![ #( #doc ),* ], - } - }) - }); - - quote::quote!( - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause{ - - #[doc(hidden)] - pub fn pallet_constants_metadata() - -> #frame_support::__private::sp_std::vec::Vec<#frame_support::__private::metadata_ir::PalletConstantMetadataIR> - { - #frame_support::__private::sp_std::vec![ #( #consts ),* ] - } - } - ) + let frame_support = &def.frame_support; + let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site()); + let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site()); + let pallet_ident = &def.pallet_struct.pallet; + let trait_use_gen = &def.trait_use_generics(proc_macro2::Span::call_site()); + + let mut where_clauses = vec![&def.config.where_clause]; + where_clauses.extend(def.extra_constants.iter().map(|d| &d.where_clause)); + let completed_where_clause = super::merge_where_clauses(&where_clauses); + + let config_consts = def.config.consts_metadata.iter().map(|const_| { + let ident = &const_.ident; + let const_type = &const_.type_; + + ConstDef { + ident: const_.ident.clone(), + type_: const_.type_.clone(), + doc: const_.doc.clone(), + default_byte_impl: quote::quote!( + let value = <::#ident as + #frame_support::traits::Get<#const_type>>::get(); + #frame_support::__private::codec::Encode::encode(&value) + ), + metadata_name: None, + } + }); + + let extra_consts = def.extra_constants.iter().flat_map(|d| &d.extra_constants).map(|const_| { + let ident = &const_.ident; + + ConstDef { + ident: const_.ident.clone(), + type_: const_.type_.clone(), + doc: const_.doc.clone(), + default_byte_impl: quote::quote!( + let value = >::#ident(); + #frame_support::__private::codec::Encode::encode(&value) + ), + metadata_name: const_.metadata_name.clone(), + } + }); + + let consts = config_consts.chain(extra_consts).map(|const_| { + let const_type = &const_.type_; + let ident_str = format!("{}", const_.metadata_name.unwrap_or(const_.ident)); + + let no_docs = vec![]; + let doc = if cfg!(feature = "no-metadata-docs") { &no_docs } else { &const_.doc }; + + let default_byte_impl = &const_.default_byte_impl; + + quote::quote!({ + #frame_support::__private::metadata_ir::PalletConstantMetadataIR { + name: #ident_str, + ty: #frame_support::__private::scale_info::meta_type::<#const_type>(), + value: { #default_byte_impl }, + docs: #frame_support::__private::sp_std::vec![ #( #doc ),* ], + } + }) + }); + + quote::quote!( + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause{ + + #[doc(hidden)] + pub fn pallet_constants_metadata() + -> #frame_support::__private::sp_std::vec::Vec<#frame_support::__private::metadata_ir::PalletConstantMetadataIR> + { + #frame_support::__private::sp_std::vec![ #( #consts ),* ] + } + } + ) } diff --git a/support/procedural-fork/src/pallet/expand/doc_only.rs b/support/procedural-fork/src/pallet/expand/doc_only.rs index 3e60e9a9b..621a051ac 100644 --- a/support/procedural-fork/src/pallet/expand/doc_only.rs +++ b/support/procedural-fork/src/pallet/expand/doc_only.rs @@ -20,84 +20,84 @@ use proc_macro2::Span; use crate::pallet::Def; pub fn expand_doc_only(def: &mut Def) -> proc_macro2::TokenStream { - let dispatchables = if let Some(call_def) = &def.call { - let type_impl_generics = def.type_impl_generics(Span::call_site()); - call_def - .methods - .iter() - .map(|method| { - let name = &method.name; - let args = &method - .args - .iter() - .map(|(_, arg_name, arg_type)| quote::quote!( #arg_name: #arg_type, )) - .collect::(); - let docs = &method.docs; + let dispatchables = if let Some(call_def) = &def.call { + let type_impl_generics = def.type_impl_generics(Span::call_site()); + call_def + .methods + .iter() + .map(|method| { + let name = &method.name; + let args = &method + .args + .iter() + .map(|(_, arg_name, arg_type)| quote::quote!( #arg_name: #arg_type, )) + .collect::(); + let docs = &method.docs; - let real = format!(" [`Pallet::{}`].", name); - quote::quote!( - #( #[doc = #docs] )* - /// - /// # Warning: Doc-Only - /// - /// This function is an automatically generated, and is doc-only, uncallable - /// stub. See the real version in - #[ doc = #real ] - pub fn #name<#type_impl_generics>(#args) { unreachable!(); } - ) - }) - .collect::() - } else { - quote::quote!() - }; + let real = format!(" [`Pallet::{}`].", name); + quote::quote!( + #( #[doc = #docs] )* + /// + /// # Warning: Doc-Only + /// + /// This function is an automatically generated, and is doc-only, uncallable + /// stub. See the real version in + #[ doc = #real ] + pub fn #name<#type_impl_generics>(#args) { unreachable!(); } + ) + }) + .collect::() + } else { + quote::quote!() + }; - let storage_types = def - .storages - .iter() - .map(|storage| { - let storage_name = &storage.ident; - let storage_type_docs = &storage.docs; - let real = format!("[`pallet::{}`].", storage_name); - quote::quote!( - #( #[doc = #storage_type_docs] )* - /// - /// # Warning: Doc-Only - /// - /// This type is automatically generated, and is doc-only. See the real version in - #[ doc = #real ] - pub struct #storage_name(); - ) - }) - .collect::(); + let storage_types = def + .storages + .iter() + .map(|storage| { + let storage_name = &storage.ident; + let storage_type_docs = &storage.docs; + let real = format!("[`pallet::{}`].", storage_name); + quote::quote!( + #( #[doc = #storage_type_docs] )* + /// + /// # Warning: Doc-Only + /// + /// This type is automatically generated, and is doc-only. See the real version in + #[ doc = #real ] + pub struct #storage_name(); + ) + }) + .collect::(); - quote::quote!( - /// Auto-generated docs-only module listing all (public and private) defined storage types - /// for this pallet. - /// - /// # Warning: Doc-Only - /// - /// Members of this module cannot be used directly and are only provided for documentation - /// purposes. - /// - /// To see the actual storage type, find a struct with the same name at the root of the - /// pallet, in the list of [*Type Definitions*](../index.html#types). - #[cfg(doc)] - pub mod storage_types { - use super::*; - #storage_types - } + quote::quote!( + /// Auto-generated docs-only module listing all (public and private) defined storage types + /// for this pallet. + /// + /// # Warning: Doc-Only + /// + /// Members of this module cannot be used directly and are only provided for documentation + /// purposes. + /// + /// To see the actual storage type, find a struct with the same name at the root of the + /// pallet, in the list of [*Type Definitions*](../index.html#types). + #[cfg(doc)] + pub mod storage_types { + use super::*; + #storage_types + } - /// Auto-generated docs-only module listing all defined dispatchables for this pallet. - /// - /// # Warning: Doc-Only - /// - /// Members of this module cannot be used directly and are only provided for documentation - /// purposes. To see the real version of each dispatchable, look for them in [`Pallet`] or - /// [`Call`]. - #[cfg(doc)] - pub mod dispatchables { - use super::*; - #dispatchables - } - ) + /// Auto-generated docs-only module listing all defined dispatchables for this pallet. + /// + /// # Warning: Doc-Only + /// + /// Members of this module cannot be used directly and are only provided for documentation + /// purposes. To see the real version of each dispatchable, look for them in [`Pallet`] or + /// [`Call`]. + #[cfg(doc)] + pub mod dispatchables { + use super::*; + #dispatchables + } + ) } diff --git a/support/procedural-fork/src/pallet/expand/documentation.rs b/support/procedural-fork/src/pallet/expand/documentation.rs index 42891dab6..ec19f889a 100644 --- a/support/procedural-fork/src/pallet/expand/documentation.rs +++ b/support/procedural-fork/src/pallet/expand/documentation.rs @@ -20,20 +20,20 @@ use proc_macro2::TokenStream; use quote::ToTokens; use syn::{spanned::Spanned, Attribute, Lit, LitStr}; -const DOC: &str = "doc"; -const PALLET_DOC: &str = "pallet_doc"; +const DOC: &'static str = "doc"; +const PALLET_DOC: &'static str = "pallet_doc"; /// Get the documentation file path from the `pallet_doc` attribute. /// /// Supported format: /// `#[pallet_doc(PATH)]`: The path of the file from which the documentation is loaded fn parse_pallet_doc_value(attr: &Attribute) -> syn::Result { - let lit: syn::LitStr = attr.parse_args().map_err(|_| { + let lit: syn::LitStr = attr.parse_args().map_err(|_| { let msg = "The `pallet_doc` received an unsupported argument. Supported format: `pallet_doc(\"PATH\")`"; syn::Error::new(attr.span(), msg) })?; - Ok(DocMetaValue::Path(lit)) + Ok(DocMetaValue::Path(lit)) } /// Get the value from the `doc` comment attribute: @@ -42,49 +42,46 @@ fn parse_pallet_doc_value(attr: &Attribute) -> syn::Result { /// - `#[doc = "A doc string"]`: Documentation as a string literal /// - `#[doc = include_str!(PATH)]`: Documentation obtained from a path fn parse_doc_value(attr: &Attribute) -> syn::Result> { - if !attr.path().is_ident(DOC) { - return Ok(None); - } - - let meta = attr.meta.require_name_value()?; - - match &meta.value { - syn::Expr::Lit(lit) => Ok(Some(DocMetaValue::Lit(lit.lit.clone()))), - syn::Expr::Macro(mac) if mac.mac.path.is_ident("include_str") => { - Ok(Some(DocMetaValue::Path(mac.mac.parse_body()?))) - } - _ => Err(syn::Error::new( - attr.span(), - "Expected `= \"docs\"` or `= include_str!(\"PATH\")`", - )), - } + if !attr.path().is_ident(DOC) { + return Ok(None) + } + + let meta = attr.meta.require_name_value()?; + + match &meta.value { + syn::Expr::Lit(lit) => Ok(Some(DocMetaValue::Lit(lit.lit.clone()))), + syn::Expr::Macro(mac) if mac.mac.path.is_ident("include_str") => + Ok(Some(DocMetaValue::Path(mac.mac.parse_body()?))), + _ => + Err(syn::Error::new(attr.span(), "Expected `= \"docs\"` or `= include_str!(\"PATH\")`")), + } } /// Supported documentation tokens. #[derive(Debug)] enum DocMetaValue { - /// Documentation with string literals. - /// - /// `#[doc = "Lit"]` - Lit(Lit), - /// Documentation with `include_str!` macro. - /// - /// The string literal represents the file `PATH`. - /// - /// `#[doc = include_str!(PATH)]` - Path(LitStr), + /// Documentation with string literals. + /// + /// `#[doc = "Lit"]` + Lit(Lit), + /// Documentation with `include_str!` macro. + /// + /// The string literal represents the file `PATH`. + /// + /// `#[doc = include_str!(PATH)]` + Path(LitStr), } impl ToTokens for DocMetaValue { - fn to_tokens(&self, tokens: &mut TokenStream) { - match self { - DocMetaValue::Lit(lit) => lit.to_tokens(tokens), - DocMetaValue::Path(path_lit) => { - let decl = quote::quote!(include_str!(#path_lit)); - tokens.extend(decl) - } - } - } + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + DocMetaValue::Lit(lit) => lit.to_tokens(tokens), + DocMetaValue::Path(path_lit) => { + let decl = quote::quote!(include_str!(#path_lit)); + tokens.extend(decl) + }, + } + } } /// Extract the documentation from the given pallet definition @@ -113,63 +110,63 @@ impl ToTokens for DocMetaValue { /// Unlike the `doc` attribute, the documentation provided to the `proc_macro` attribute is /// not added to the pallet. pub fn expand_documentation(def: &mut Def) -> proc_macro2::TokenStream { - let frame_support = &def.frame_support; - let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site()); - let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site()); - let pallet_ident = &def.pallet_struct.pallet; - let where_clauses = &def.config.where_clause; - - // TODO: Use [drain_filter](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.drain_filter) when it is stable. - - // The `pallet_doc` attributes are excluded from the generation of the pallet, - // but they are included in the runtime metadata. - let mut pallet_docs = Vec::with_capacity(def.item.attrs.len()); - let mut index = 0; - while index < def.item.attrs.len() { - let attr = &def.item.attrs[index]; - if attr.path().get_ident().map_or(false, |i| *i == PALLET_DOC) { - pallet_docs.push(def.item.attrs.remove(index)); - // Do not increment the index, we have just removed the - // element from the attributes. - continue; - } - - index += 1; - } - - // Capture the `#[doc = include_str!("../README.md")]` and `#[doc = "Documentation"]`. - let docs = match def - .item - .attrs - .iter() - .filter_map(|v| parse_doc_value(v).transpose()) - .collect::>>() - { - Ok(r) => r, - Err(err) => return err.into_compile_error(), - }; - - // Capture the `#[pallet_doc("../README.md")]`. - let pallet_docs = match pallet_docs - .into_iter() - .map(|attr| parse_pallet_doc_value(&attr)) - .collect::>>() - { - Ok(docs) => docs, - Err(err) => return err.into_compile_error(), - }; - - let docs = docs.iter().chain(pallet_docs.iter()); - - quote::quote!( - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #where_clauses{ - - #[doc(hidden)] - pub fn pallet_documentation_metadata() - -> #frame_support::__private::sp_std::vec::Vec<&'static str> - { - #frame_support::__private::sp_std::vec![ #( #docs ),* ] - } - } - ) + let frame_support = &def.frame_support; + let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site()); + let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site()); + let pallet_ident = &def.pallet_struct.pallet; + let where_clauses = &def.config.where_clause; + + // TODO: Use [drain_filter](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.drain_filter) when it is stable. + + // The `pallet_doc` attributes are excluded from the generation of the pallet, + // but they are included in the runtime metadata. + let mut pallet_docs = Vec::with_capacity(def.item.attrs.len()); + let mut index = 0; + while index < def.item.attrs.len() { + let attr = &def.item.attrs[index]; + if attr.path().get_ident().map_or(false, |i| *i == PALLET_DOC) { + pallet_docs.push(def.item.attrs.remove(index)); + // Do not increment the index, we have just removed the + // element from the attributes. + continue + } + + index += 1; + } + + // Capture the `#[doc = include_str!("../README.md")]` and `#[doc = "Documentation"]`. + let docs = match def + .item + .attrs + .iter() + .filter_map(|v| parse_doc_value(v).transpose()) + .collect::>>() + { + Ok(r) => r, + Err(err) => return err.into_compile_error(), + }; + + // Capture the `#[pallet_doc("../README.md")]`. + let pallet_docs = match pallet_docs + .into_iter() + .map(|attr| parse_pallet_doc_value(&attr)) + .collect::>>() + { + Ok(docs) => docs, + Err(err) => return err.into_compile_error(), + }; + + let docs = docs.iter().chain(pallet_docs.iter()); + + quote::quote!( + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #where_clauses{ + + #[doc(hidden)] + pub fn pallet_documentation_metadata() + -> #frame_support::__private::sp_std::vec::Vec<&'static str> + { + #frame_support::__private::sp_std::vec![ #( #docs ),* ] + } + } + ) } diff --git a/support/procedural-fork/src/pallet/expand/error.rs b/support/procedural-fork/src/pallet/expand/error.rs index e2c3f680c..72fb6e923 100644 --- a/support/procedural-fork/src/pallet/expand/error.rs +++ b/support/procedural-fork/src/pallet/expand/error.rs @@ -16,11 +16,11 @@ // limitations under the License. use crate::{ - pallet::{ - parse::error::{VariantDef, VariantField}, - Def, - }, - COUNTER, + pallet::{ + parse::error::{VariantDef, VariantField}, + Def, + }, + COUNTER, }; use frame_support_procedural_tools::get_doc_literals; use quote::ToTokens; @@ -29,49 +29,49 @@ use syn::spanned::Spanned; /// /// * impl various trait on Error pub fn expand_error(def: &mut Def) -> proc_macro2::TokenStream { - let count = COUNTER.with(|counter| counter.borrow_mut().inc()); - let error_token_unique_id = - syn::Ident::new(&format!("__tt_error_token_{}", count), def.item.span()); - - let frame_support = &def.frame_support; - let frame_system = &def.frame_system; - let config_where_clause = &def.config.where_clause; - - let error = if let Some(error) = &def.error { - error - } else { - return quote::quote! { - #[macro_export] - #[doc(hidden)] - macro_rules! #error_token_unique_id { - { - $caller:tt - your_tt_return = [{ $my_tt_return:path }] - } => { - $my_tt_return! { - $caller - } - }; - } - - pub use #error_token_unique_id as tt_error_token; - }; - }; - - let error_ident = &error.error; - let type_impl_gen = &def.type_impl_generics(error.attr_span); - let type_use_gen = &def.type_use_generics(error.attr_span); - - let phantom_variant: syn::Variant = syn::parse_quote!( - #[doc(hidden)] - #[codec(skip)] - __Ignore( - #frame_support::__private::sp_std::marker::PhantomData<(#type_use_gen)>, - #frame_support::Never, - ) - ); - - let as_str_matches = error.variants.iter().map( + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + let error_token_unique_id = + syn::Ident::new(&format!("__tt_error_token_{}", count), def.item.span()); + + let frame_support = &def.frame_support; + let frame_system = &def.frame_system; + let config_where_clause = &def.config.where_clause; + + let error = if let Some(error) = &def.error { + error + } else { + return quote::quote! { + #[macro_export] + #[doc(hidden)] + macro_rules! #error_token_unique_id { + { + $caller:tt + your_tt_return = [{ $my_tt_return:path }] + } => { + $my_tt_return! { + $caller + } + }; + } + + pub use #error_token_unique_id as tt_error_token; + } + }; + + let error_ident = &error.error; + let type_impl_gen = &def.type_impl_generics(error.attr_span); + let type_use_gen = &def.type_use_generics(error.attr_span); + + let phantom_variant: syn::Variant = syn::parse_quote!( + #[doc(hidden)] + #[codec(skip)] + __Ignore( + #frame_support::__private::sp_std::marker::PhantomData<(#type_use_gen)>, + #frame_support::Never, + ) + ); + + let as_str_matches = error.variants.iter().map( |VariantDef { ident: variant, field: field_ty, docs: _, cfg_attrs }| { let variant_str = variant.to_string(); let cfg_attrs = cfg_attrs.iter().map(|attr| attr.to_token_stream()); @@ -89,107 +89,103 @@ pub fn expand_error(def: &mut Def) -> proc_macro2::TokenStream { }, ); - let error_item = { - let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[error.index]; - if let syn::Item::Enum(item) = item { - item - } else { - unreachable!("Checked by error parser") - } - }; - - error_item.variants.insert(0, phantom_variant); - - let capture_docs = if cfg!(feature = "no-metadata-docs") { - "never" - } else { - "always" - }; - - // derive TypeInfo for error metadata - error_item.attrs.push(syn::parse_quote! { - #[derive( - #frame_support::__private::codec::Encode, - #frame_support::__private::codec::Decode, - #frame_support::__private::scale_info::TypeInfo, - #frame_support::PalletError, - )] - }); - error_item.attrs.push(syn::parse_quote!( - #[scale_info(skip_type_params(#type_use_gen), capture_docs = #capture_docs)] - )); - - if get_doc_literals(&error_item.attrs).is_empty() { - error_item.attrs.push(syn::parse_quote!( - #[doc = "The `Error` enum of this pallet."] - )); - } - - quote::quote_spanned!(error.attr_span => - impl<#type_impl_gen> #frame_support::__private::sp_std::fmt::Debug for #error_ident<#type_use_gen> - #config_where_clause - { - fn fmt(&self, f: &mut #frame_support::__private::sp_std::fmt::Formatter<'_>) - -> #frame_support::__private::sp_std::fmt::Result - { - f.write_str(self.as_str()) - } - } - - impl<#type_impl_gen> #error_ident<#type_use_gen> #config_where_clause { - #[doc(hidden)] - pub fn as_str(&self) -> &'static str { - match &self { - Self::__Ignore(_, _) => unreachable!("`__Ignore` can never be constructed"), - #( #as_str_matches )* - } - } - } - - impl<#type_impl_gen> From<#error_ident<#type_use_gen>> for &'static str - #config_where_clause - { - fn from(err: #error_ident<#type_use_gen>) -> &'static str { - err.as_str() - } - } - - impl<#type_impl_gen> From<#error_ident<#type_use_gen>> - for #frame_support::sp_runtime::DispatchError - #config_where_clause - { - fn from(err: #error_ident<#type_use_gen>) -> Self { - use #frame_support::__private::codec::Encode; - let index = < - ::PalletInfo - as #frame_support::traits::PalletInfo - >::index::>() - .expect("Every active module has an index in the runtime; qed") as u8; - let mut encoded = err.encode(); - encoded.resize(#frame_support::MAX_MODULE_ERROR_ENCODED_SIZE, 0); - - #frame_support::sp_runtime::DispatchError::Module(#frame_support::sp_runtime::ModuleError { - index, - error: TryInto::try_into(encoded).expect("encoded error is resized to be equal to the maximum encoded error size; qed"), - message: Some(err.as_str()), - }) - } - } - - #[macro_export] - #[doc(hidden)] - macro_rules! #error_token_unique_id { - { - $caller:tt - your_tt_return = [{ $my_tt_return:path }] - } => { - $my_tt_return! { - $caller - error = [{ #error_ident }] - } - }; - } - - pub use #error_token_unique_id as tt_error_token; - ) + let error_item = { + let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[error.index]; + if let syn::Item::Enum(item) = item { + item + } else { + unreachable!("Checked by error parser") + } + }; + + error_item.variants.insert(0, phantom_variant); + + let capture_docs = if cfg!(feature = "no-metadata-docs") { "never" } else { "always" }; + + // derive TypeInfo for error metadata + error_item.attrs.push(syn::parse_quote! { + #[derive( + #frame_support::__private::codec::Encode, + #frame_support::__private::codec::Decode, + #frame_support::__private::scale_info::TypeInfo, + #frame_support::PalletError, + )] + }); + error_item.attrs.push(syn::parse_quote!( + #[scale_info(skip_type_params(#type_use_gen), capture_docs = #capture_docs)] + )); + + if get_doc_literals(&error_item.attrs).is_empty() { + error_item.attrs.push(syn::parse_quote!( + #[doc = "The `Error` enum of this pallet."] + )); + } + + quote::quote_spanned!(error.attr_span => + impl<#type_impl_gen> #frame_support::__private::sp_std::fmt::Debug for #error_ident<#type_use_gen> + #config_where_clause + { + fn fmt(&self, f: &mut #frame_support::__private::sp_std::fmt::Formatter<'_>) + -> #frame_support::__private::sp_std::fmt::Result + { + f.write_str(self.as_str()) + } + } + + impl<#type_impl_gen> #error_ident<#type_use_gen> #config_where_clause { + #[doc(hidden)] + pub fn as_str(&self) -> &'static str { + match &self { + Self::__Ignore(_, _) => unreachable!("`__Ignore` can never be constructed"), + #( #as_str_matches )* + } + } + } + + impl<#type_impl_gen> From<#error_ident<#type_use_gen>> for &'static str + #config_where_clause + { + fn from(err: #error_ident<#type_use_gen>) -> &'static str { + err.as_str() + } + } + + impl<#type_impl_gen> From<#error_ident<#type_use_gen>> + for #frame_support::sp_runtime::DispatchError + #config_where_clause + { + fn from(err: #error_ident<#type_use_gen>) -> Self { + use #frame_support::__private::codec::Encode; + let index = < + ::PalletInfo + as #frame_support::traits::PalletInfo + >::index::>() + .expect("Every active module has an index in the runtime; qed") as u8; + let mut encoded = err.encode(); + encoded.resize(#frame_support::MAX_MODULE_ERROR_ENCODED_SIZE, 0); + + #frame_support::sp_runtime::DispatchError::Module(#frame_support::sp_runtime::ModuleError { + index, + error: TryInto::try_into(encoded).expect("encoded error is resized to be equal to the maximum encoded error size; qed"), + message: Some(err.as_str()), + }) + } + } + + #[macro_export] + #[doc(hidden)] + macro_rules! #error_token_unique_id { + { + $caller:tt + your_tt_return = [{ $my_tt_return:path }] + } => { + $my_tt_return! { + $caller + error = [{ #error_ident }] + } + }; + } + + pub use #error_token_unique_id as tt_error_token; + ) } diff --git a/support/procedural-fork/src/pallet/expand/event.rs b/support/procedural-fork/src/pallet/expand/event.rs index 931dcd95a..655fc5507 100644 --- a/support/procedural-fork/src/pallet/expand/event.rs +++ b/support/procedural-fork/src/pallet/expand/event.rs @@ -16,8 +16,8 @@ // limitations under the License. use crate::{ - pallet::{parse::event::PalletEventDepositAttr, Def}, - COUNTER, + pallet::{parse::event::PalletEventDepositAttr, Def}, + COUNTER, }; use frame_support_procedural_tools::get_doc_literals; use syn::{spanned::Spanned, Ident}; @@ -27,159 +27,148 @@ use syn::{spanned::Spanned, Ident}; /// * Impl various trait on Event including metadata /// * if deposit_event is defined, implement deposit_event on module. pub fn expand_event(def: &mut Def) -> proc_macro2::TokenStream { - let count = COUNTER.with(|counter| counter.borrow_mut().inc()); - - let (event, macro_ident) = if let Some(event) = &def.event { - let ident = Ident::new( - &format!("__is_event_part_defined_{}", count), - event.attr_span, - ); - (event, ident) - } else { - let macro_ident = Ident::new( - &format!("__is_event_part_defined_{}", count), - def.item.span(), - ); - - return quote::quote! { - #[doc(hidden)] - pub mod __substrate_event_check { - #[macro_export] - #[doc(hidden)] - macro_rules! #macro_ident { - ($pallet_name:ident) => { - compile_error!(concat!( - "`", - stringify!($pallet_name), - "` does not have #[pallet::event] defined, perhaps you should \ - remove `Event` from construct_runtime?", - )); - } - } - - #[doc(hidden)] - pub use #macro_ident as is_event_part_defined; - } - }; - }; - - let event_where_clause = &event.where_clause; - - // NOTE: actually event where clause must be a subset of config where clause because of - // `type RuntimeEvent: From>`. But we merge either way for potential better error - // message - let completed_where_clause = - super::merge_where_clauses(&[&event.where_clause, &def.config.where_clause]); - - let event_ident = &event.event; - let frame_system = &def.frame_system; - let frame_support = &def.frame_support; - let event_use_gen = &event.gen_kind.type_use_gen(event.attr_span); - let event_impl_gen = &event.gen_kind.type_impl_gen(event.attr_span); - - let event_item = { - let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[event.index]; - if let syn::Item::Enum(item) = item { - item - } else { - unreachable!("Checked by event parser") - } - }; - - // Phantom data is added for generic event. - if event.gen_kind.is_generic() { - let variant = syn::parse_quote!( - #[doc(hidden)] - #[codec(skip)] - __Ignore( - ::core::marker::PhantomData<(#event_use_gen)>, - #frame_support::Never, - ) - ); - - // Push ignore variant at the end. - event_item.variants.push(variant); - } - - if get_doc_literals(&event_item.attrs).is_empty() { - event_item - .attrs - .push(syn::parse_quote!(#[doc = "The `Event` enum of this pallet"])); - } - - // derive some traits because system event require Clone, FullCodec, Eq, PartialEq and Debug - event_item.attrs.push(syn::parse_quote!( - #[derive( - #frame_support::CloneNoBound, - #frame_support::EqNoBound, - #frame_support::PartialEqNoBound, - #frame_support::RuntimeDebugNoBound, - #frame_support::__private::codec::Encode, - #frame_support::__private::codec::Decode, - #frame_support::__private::scale_info::TypeInfo, - )] - )); - - let capture_docs = if cfg!(feature = "no-metadata-docs") { - "never" - } else { - "always" - }; - - // skip requirement for type params to implement `TypeInfo`, and set docs capture - event_item.attrs.push(syn::parse_quote!( - #[scale_info(skip_type_params(#event_use_gen), capture_docs = #capture_docs)] - )); - - let deposit_event = if let Some(deposit_event) = &event.deposit_event { - let event_use_gen = &event.gen_kind.type_use_gen(event.attr_span); - let trait_use_gen = &def.trait_use_generics(event.attr_span); - let type_impl_gen = &def.type_impl_generics(event.attr_span); - let type_use_gen = &def.type_use_generics(event.attr_span); - let pallet_ident = &def.pallet_struct.pallet; - - let PalletEventDepositAttr { - fn_vis, fn_span, .. - } = deposit_event; - - quote::quote_spanned!(*fn_span => - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { - #fn_vis fn deposit_event(event: Event<#event_use_gen>) { - let event = < - ::RuntimeEvent as - From> - >::from(event); - - let event = < - ::RuntimeEvent as - Into<::RuntimeEvent> - >::into(event); - - <#frame_system::Pallet>::deposit_event(event) - } - } - ) - } else { - Default::default() - }; - - quote::quote_spanned!(event.attr_span => - #[doc(hidden)] - pub mod __substrate_event_check { - #[macro_export] - #[doc(hidden)] - macro_rules! #macro_ident { - ($pallet_name:ident) => {}; - } - - #[doc(hidden)] - pub use #macro_ident as is_event_part_defined; - } - - #deposit_event - - impl<#event_impl_gen> From<#event_ident<#event_use_gen>> for () #event_where_clause { - fn from(_: #event_ident<#event_use_gen>) {} - } - ) + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + + let (event, macro_ident) = if let Some(event) = &def.event { + let ident = Ident::new(&format!("__is_event_part_defined_{}", count), event.attr_span); + (event, ident) + } else { + let macro_ident = + Ident::new(&format!("__is_event_part_defined_{}", count), def.item.span()); + + return quote::quote! { + #[doc(hidden)] + pub mod __substrate_event_check { + #[macro_export] + #[doc(hidden)] + macro_rules! #macro_ident { + ($pallet_name:ident) => { + compile_error!(concat!( + "`", + stringify!($pallet_name), + "` does not have #[pallet::event] defined, perhaps you should \ + remove `Event` from construct_runtime?", + )); + } + } + + #[doc(hidden)] + pub use #macro_ident as is_event_part_defined; + } + } + }; + + let event_where_clause = &event.where_clause; + + // NOTE: actually event where clause must be a subset of config where clause because of + // `type RuntimeEvent: From>`. But we merge either way for potential better error + // message + let completed_where_clause = + super::merge_where_clauses(&[&event.where_clause, &def.config.where_clause]); + + let event_ident = &event.event; + let frame_system = &def.frame_system; + let frame_support = &def.frame_support; + let event_use_gen = &event.gen_kind.type_use_gen(event.attr_span); + let event_impl_gen = &event.gen_kind.type_impl_gen(event.attr_span); + + let event_item = { + let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[event.index]; + if let syn::Item::Enum(item) = item { + item + } else { + unreachable!("Checked by event parser") + } + }; + + // Phantom data is added for generic event. + if event.gen_kind.is_generic() { + let variant = syn::parse_quote!( + #[doc(hidden)] + #[codec(skip)] + __Ignore( + ::core::marker::PhantomData<(#event_use_gen)>, + #frame_support::Never, + ) + ); + + // Push ignore variant at the end. + event_item.variants.push(variant); + } + + if get_doc_literals(&event_item.attrs).is_empty() { + event_item + .attrs + .push(syn::parse_quote!(#[doc = "The `Event` enum of this pallet"])); + } + + // derive some traits because system event require Clone, FullCodec, Eq, PartialEq and Debug + event_item.attrs.push(syn::parse_quote!( + #[derive( + #frame_support::CloneNoBound, + #frame_support::EqNoBound, + #frame_support::PartialEqNoBound, + #frame_support::RuntimeDebugNoBound, + #frame_support::__private::codec::Encode, + #frame_support::__private::codec::Decode, + #frame_support::__private::scale_info::TypeInfo, + )] + )); + + let capture_docs = if cfg!(feature = "no-metadata-docs") { "never" } else { "always" }; + + // skip requirement for type params to implement `TypeInfo`, and set docs capture + event_item.attrs.push(syn::parse_quote!( + #[scale_info(skip_type_params(#event_use_gen), capture_docs = #capture_docs)] + )); + + let deposit_event = if let Some(deposit_event) = &event.deposit_event { + let event_use_gen = &event.gen_kind.type_use_gen(event.attr_span); + let trait_use_gen = &def.trait_use_generics(event.attr_span); + let type_impl_gen = &def.type_impl_generics(event.attr_span); + let type_use_gen = &def.type_use_generics(event.attr_span); + let pallet_ident = &def.pallet_struct.pallet; + + let PalletEventDepositAttr { fn_vis, fn_span, .. } = deposit_event; + + quote::quote_spanned!(*fn_span => + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { + #fn_vis fn deposit_event(event: Event<#event_use_gen>) { + let event = < + ::RuntimeEvent as + From> + >::from(event); + + let event = < + ::RuntimeEvent as + Into<::RuntimeEvent> + >::into(event); + + <#frame_system::Pallet>::deposit_event(event) + } + } + ) + } else { + Default::default() + }; + + quote::quote_spanned!(event.attr_span => + #[doc(hidden)] + pub mod __substrate_event_check { + #[macro_export] + #[doc(hidden)] + macro_rules! #macro_ident { + ($pallet_name:ident) => {}; + } + + #[doc(hidden)] + pub use #macro_ident as is_event_part_defined; + } + + #deposit_event + + impl<#event_impl_gen> From<#event_ident<#event_use_gen>> for () #event_where_clause { + fn from(_: #event_ident<#event_use_gen>) {} + } + ) } diff --git a/support/procedural-fork/src/pallet/expand/genesis_build.rs b/support/procedural-fork/src/pallet/expand/genesis_build.rs index c6089550d..248e83469 100644 --- a/support/procedural-fork/src/pallet/expand/genesis_build.rs +++ b/support/procedural-fork/src/pallet/expand/genesis_build.rs @@ -20,34 +20,30 @@ use crate::pallet::Def; /// /// * implement the trait `sp_runtime::BuildStorage` pub fn expand_genesis_build(def: &mut Def) -> proc_macro2::TokenStream { - let genesis_config = if let Some(genesis_config) = &def.genesis_config { - genesis_config - } else { - return Default::default(); - }; - let genesis_build = def.genesis_build.as_ref().expect("Checked by def parser"); + let genesis_config = if let Some(genesis_config) = &def.genesis_config { + genesis_config + } else { + return Default::default() + }; + let genesis_build = def.genesis_build.as_ref().expect("Checked by def parser"); - let frame_support = &def.frame_support; - let type_impl_gen = &genesis_config - .gen_kind - .type_impl_gen(genesis_build.attr_span); - let gen_cfg_ident = &genesis_config.genesis_config; - let gen_cfg_use_gen = &genesis_config - .gen_kind - .type_use_gen(genesis_build.attr_span); + let frame_support = &def.frame_support; + let type_impl_gen = &genesis_config.gen_kind.type_impl_gen(genesis_build.attr_span); + let gen_cfg_ident = &genesis_config.genesis_config; + let gen_cfg_use_gen = &genesis_config.gen_kind.type_use_gen(genesis_build.attr_span); - let where_clause = &genesis_build.where_clause; + let where_clause = &genesis_build.where_clause; - quote::quote_spanned!(genesis_build.attr_span => - #[cfg(feature = "std")] - impl<#type_impl_gen> #frame_support::sp_runtime::BuildStorage for #gen_cfg_ident<#gen_cfg_use_gen> #where_clause - { - fn assimilate_storage(&self, storage: &mut #frame_support::sp_runtime::Storage) -> std::result::Result<(), std::string::String> { - #frame_support::__private::BasicExternalities::execute_with_storage(storage, || { - self.build(); - Ok(()) - }) - } - } - ) + quote::quote_spanned!(genesis_build.attr_span => + #[cfg(feature = "std")] + impl<#type_impl_gen> #frame_support::sp_runtime::BuildStorage for #gen_cfg_ident<#gen_cfg_use_gen> #where_clause + { + fn assimilate_storage(&self, storage: &mut #frame_support::sp_runtime::Storage) -> std::result::Result<(), std::string::String> { + #frame_support::__private::BasicExternalities::execute_with_storage(storage, || { + self.build(); + Ok(()) + }) + } + } + ) } diff --git a/support/procedural-fork/src/pallet/expand/genesis_config.rs b/support/procedural-fork/src/pallet/expand/genesis_config.rs index e171e2468..31d519ef2 100644 --- a/support/procedural-fork/src/pallet/expand/genesis_config.rs +++ b/support/procedural-fork/src/pallet/expand/genesis_config.rs @@ -23,130 +23,125 @@ use syn::{spanned::Spanned, Ident}; /// /// * add various derive trait on GenesisConfig struct. pub fn expand_genesis_config(def: &mut Def) -> proc_macro2::TokenStream { - let count = COUNTER.with(|counter| counter.borrow_mut().inc()); - - let (genesis_config, def_macro_ident, std_macro_ident) = if let Some(genesis_config) = - &def.genesis_config - { - let def_macro_ident = Ident::new( - &format!("__is_genesis_config_defined_{}", count), - genesis_config.genesis_config.span(), - ); - - let std_macro_ident = Ident::new( - &format!("__is_std_macro_defined_for_genesis_{}", count), - genesis_config.genesis_config.span(), - ); - - (genesis_config, def_macro_ident, std_macro_ident) - } else { - let def_macro_ident = Ident::new( - &format!("__is_genesis_config_defined_{}", count), - def.item.span(), - ); - - let std_macro_ident = Ident::new( - &format!("__is_std_enabled_for_genesis_{}", count), - def.item.span(), - ); - - return quote::quote! { - #[doc(hidden)] - pub mod __substrate_genesis_config_check { - #[macro_export] - #[doc(hidden)] - macro_rules! #def_macro_ident { - ($pallet_name:ident) => { - compile_error!(concat!( - "`", - stringify!($pallet_name), - "` does not have #[pallet::genesis_config] defined, perhaps you should \ - remove `Config` from construct_runtime?", - )); - } - } - - #[macro_export] - #[doc(hidden)] - macro_rules! #std_macro_ident { - ($pallet_name:ident, $pallet_path:expr) => {}; - } - - #[doc(hidden)] - pub use #def_macro_ident as is_genesis_config_defined; - #[doc(hidden)] - pub use #std_macro_ident as is_std_enabled_for_genesis; - } - }; - }; - - let frame_support = &def.frame_support; - - let genesis_config_item = - &mut def.item.content.as_mut().expect("Checked by def parser").1[genesis_config.index]; - - let serde_crate = format!("{}::__private::serde", frame_support.to_token_stream()); - - match genesis_config_item { - syn::Item::Enum(syn::ItemEnum { attrs, .. }) - | syn::Item::Struct(syn::ItemStruct { attrs, .. }) - | syn::Item::Type(syn::ItemType { attrs, .. }) => { - if get_doc_literals(attrs).is_empty() { - attrs.push(syn::parse_quote!( - #[doc = r" + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + + let (genesis_config, def_macro_ident, std_macro_ident) = + if let Some(genesis_config) = &def.genesis_config { + let def_macro_ident = Ident::new( + &format!("__is_genesis_config_defined_{}", count), + genesis_config.genesis_config.span(), + ); + + let std_macro_ident = Ident::new( + &format!("__is_std_macro_defined_for_genesis_{}", count), + genesis_config.genesis_config.span(), + ); + + (genesis_config, def_macro_ident, std_macro_ident) + } else { + let def_macro_ident = + Ident::new(&format!("__is_genesis_config_defined_{}", count), def.item.span()); + + let std_macro_ident = + Ident::new(&format!("__is_std_enabled_for_genesis_{}", count), def.item.span()); + + return quote::quote! { + #[doc(hidden)] + pub mod __substrate_genesis_config_check { + #[macro_export] + #[doc(hidden)] + macro_rules! #def_macro_ident { + ($pallet_name:ident) => { + compile_error!(concat!( + "`", + stringify!($pallet_name), + "` does not have #[pallet::genesis_config] defined, perhaps you should \ + remove `Config` from construct_runtime?", + )); + } + } + + #[macro_export] + #[doc(hidden)] + macro_rules! #std_macro_ident { + ($pallet_name:ident, $pallet_path:expr) => {}; + } + + #[doc(hidden)] + pub use #def_macro_ident as is_genesis_config_defined; + #[doc(hidden)] + pub use #std_macro_ident as is_std_enabled_for_genesis; + } + } + }; + + let frame_support = &def.frame_support; + + let genesis_config_item = + &mut def.item.content.as_mut().expect("Checked by def parser").1[genesis_config.index]; + + let serde_crate = format!("{}::__private::serde", frame_support.to_token_stream()); + + match genesis_config_item { + syn::Item::Enum(syn::ItemEnum { attrs, .. }) | + syn::Item::Struct(syn::ItemStruct { attrs, .. }) | + syn::Item::Type(syn::ItemType { attrs, .. }) => { + if get_doc_literals(attrs).is_empty() { + attrs.push(syn::parse_quote!( + #[doc = r" Can be used to configure the [genesis state](https://docs.substrate.io/build/genesis-configuration/) of this pallet. "] - )); - } - attrs.push(syn::parse_quote!( - #[derive(#frame_support::Serialize, #frame_support::Deserialize)] - )); - attrs.push(syn::parse_quote!( #[serde(rename_all = "camelCase")] )); - attrs.push(syn::parse_quote!( #[serde(deny_unknown_fields)] )); - attrs.push(syn::parse_quote!( #[serde(bound(serialize = ""))] )); - attrs.push(syn::parse_quote!( #[serde(bound(deserialize = ""))] )); - attrs.push(syn::parse_quote!( #[serde(crate = #serde_crate)] )); - } - _ => unreachable!("Checked by genesis_config parser"), - } - - quote::quote! { - #[doc(hidden)] - pub mod __substrate_genesis_config_check { - #[macro_export] - #[doc(hidden)] - macro_rules! #def_macro_ident { - ($pallet_name:ident) => {}; - } - - #[cfg(not(feature = "std"))] - #[macro_export] - #[doc(hidden)] - macro_rules! #std_macro_ident { - ($pallet_name:ident, $pallet_path:expr) => { - compile_error!(concat!( - "`", - stringify!($pallet_name), - "` does not have the std feature enabled, this will cause the `", - $pallet_path, - "::GenesisConfig` type to not implement serde traits." - )); - }; - } - - #[cfg(feature = "std")] - #[macro_export] - #[doc(hidden)] - macro_rules! #std_macro_ident { - ($pallet_name:ident, $pallet_path:expr) => {}; - } - - #[doc(hidden)] - pub use #def_macro_ident as is_genesis_config_defined; - #[doc(hidden)] - pub use #std_macro_ident as is_std_enabled_for_genesis; - } - } + )); + } + attrs.push(syn::parse_quote!( + #[derive(#frame_support::Serialize, #frame_support::Deserialize)] + )); + attrs.push(syn::parse_quote!( #[serde(rename_all = "camelCase")] )); + attrs.push(syn::parse_quote!( #[serde(deny_unknown_fields)] )); + attrs.push(syn::parse_quote!( #[serde(bound(serialize = ""))] )); + attrs.push(syn::parse_quote!( #[serde(bound(deserialize = ""))] )); + attrs.push(syn::parse_quote!( #[serde(crate = #serde_crate)] )); + }, + _ => unreachable!("Checked by genesis_config parser"), + } + + quote::quote! { + #[doc(hidden)] + pub mod __substrate_genesis_config_check { + #[macro_export] + #[doc(hidden)] + macro_rules! #def_macro_ident { + ($pallet_name:ident) => {}; + } + + #[cfg(not(feature = "std"))] + #[macro_export] + #[doc(hidden)] + macro_rules! #std_macro_ident { + ($pallet_name:ident, $pallet_path:expr) => { + compile_error!(concat!( + "`", + stringify!($pallet_name), + "` does not have the std feature enabled, this will cause the `", + $pallet_path, + "::GenesisConfig` type to not implement serde traits." + )); + }; + } + + #[cfg(feature = "std")] + #[macro_export] + #[doc(hidden)] + macro_rules! #std_macro_ident { + ($pallet_name:ident, $pallet_path:expr) => {}; + } + + #[doc(hidden)] + pub use #def_macro_ident as is_genesis_config_defined; + #[doc(hidden)] + pub use #std_macro_ident as is_std_enabled_for_genesis; + } + } } diff --git a/support/procedural-fork/src/pallet/expand/hooks.rs b/support/procedural-fork/src/pallet/expand/hooks.rs index 6967f4c08..3623b5952 100644 --- a/support/procedural-fork/src/pallet/expand/hooks.rs +++ b/support/procedural-fork/src/pallet/expand/hooks.rs @@ -19,326 +19,322 @@ use crate::pallet::Def; /// * implement the individual traits using the Hooks trait pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { - let (where_clause, span, has_runtime_upgrade) = match def.hooks.as_ref() { - Some(hooks) => { - let where_clause = hooks.where_clause.clone(); - let span = hooks.attr_span; - let has_runtime_upgrade = hooks.has_runtime_upgrade; - (where_clause, span, has_runtime_upgrade) - } - None => ( - def.config.where_clause.clone(), - def.pallet_struct.attr_span, - false, - ), - }; + let (where_clause, span, has_runtime_upgrade) = match def.hooks.as_ref() { + Some(hooks) => { + let where_clause = hooks.where_clause.clone(); + let span = hooks.attr_span; + let has_runtime_upgrade = hooks.has_runtime_upgrade; + (where_clause, span, has_runtime_upgrade) + }, + None => (def.config.where_clause.clone(), def.pallet_struct.attr_span, false), + }; - let frame_support = &def.frame_support; - let type_impl_gen = &def.type_impl_generics(span); - let type_use_gen = &def.type_use_generics(span); - let pallet_ident = &def.pallet_struct.pallet; - let frame_system = &def.frame_system; - let pallet_name = quote::quote! { - < - ::PalletInfo - as - #frame_support::traits::PalletInfo - >::name::().unwrap_or("") - }; + let frame_support = &def.frame_support; + let type_impl_gen = &def.type_impl_generics(span); + let type_use_gen = &def.type_use_generics(span); + let pallet_ident = &def.pallet_struct.pallet; + let frame_system = &def.frame_system; + let pallet_name = quote::quote! { + < + ::PalletInfo + as + #frame_support::traits::PalletInfo + >::name::().unwrap_or("") + }; - let initialize_on_chain_storage_version = if let Some(in_code_version) = - &def.pallet_struct.storage_version - { - quote::quote! { - #frame_support::__private::log::info!( - target: #frame_support::LOG_TARGET, - "🐥 New pallet {:?} detected in the runtime. Initializing the on-chain storage version to match the storage version defined in the pallet: {:?}", - #pallet_name, - #in_code_version - ); - #in_code_version.put::(); - } - } else { - quote::quote! { - let default_version = #frame_support::traits::StorageVersion::new(0); - #frame_support::__private::log::info!( - target: #frame_support::LOG_TARGET, - "🐥 New pallet {:?} detected in the runtime. The pallet has no defined storage version, so the on-chain version is being initialized to {:?}.", - #pallet_name, - default_version - ); - default_version.put::(); - } - }; + let initialize_on_chain_storage_version = if let Some(in_code_version) = + &def.pallet_struct.storage_version + { + quote::quote! { + #frame_support::__private::log::info!( + target: #frame_support::LOG_TARGET, + "🐥 New pallet {:?} detected in the runtime. Initializing the on-chain storage version to match the storage version defined in the pallet: {:?}", + #pallet_name, + #in_code_version + ); + #in_code_version.put::(); + } + } else { + quote::quote! { + let default_version = #frame_support::traits::StorageVersion::new(0); + #frame_support::__private::log::info!( + target: #frame_support::LOG_TARGET, + "🐥 New pallet {:?} detected in the runtime. The pallet has no defined storage version, so the on-chain version is being initialized to {:?}.", + #pallet_name, + default_version + ); + default_version.put::(); + } + }; - let log_runtime_upgrade = if has_runtime_upgrade { - // a migration is defined here. - quote::quote! { - #frame_support::__private::log::info!( - target: #frame_support::LOG_TARGET, - "⚠️ {} declares internal migrations (which *might* execute). \ - On-chain `{:?}` vs in-code storage version `{:?}`", - #pallet_name, - ::on_chain_storage_version(), - ::in_code_storage_version(), - ); - } - } else { - // default. - quote::quote! { - #frame_support::__private::log::debug!( - target: #frame_support::LOG_TARGET, - "✅ no migration for {}", - #pallet_name, - ); - } - }; + let log_runtime_upgrade = if has_runtime_upgrade { + // a migration is defined here. + quote::quote! { + #frame_support::__private::log::info!( + target: #frame_support::LOG_TARGET, + "⚠️ {} declares internal migrations (which *might* execute). \ + On-chain `{:?}` vs in-code storage version `{:?}`", + #pallet_name, + ::on_chain_storage_version(), + ::in_code_storage_version(), + ); + } + } else { + // default. + quote::quote! { + #frame_support::__private::log::debug!( + target: #frame_support::LOG_TARGET, + "✅ no migration for {}", + #pallet_name, + ); + } + }; - let hooks_impl = if def.hooks.is_none() { - let frame_system = &def.frame_system; - quote::quote! { - impl<#type_impl_gen> - #frame_support::traits::Hooks<#frame_system::pallet_prelude::BlockNumberFor::> - for #pallet_ident<#type_use_gen> #where_clause {} - } - } else { - proc_macro2::TokenStream::new() - }; + let hooks_impl = if def.hooks.is_none() { + let frame_system = &def.frame_system; + quote::quote! { + impl<#type_impl_gen> + #frame_support::traits::Hooks<#frame_system::pallet_prelude::BlockNumberFor::> + for #pallet_ident<#type_use_gen> #where_clause {} + } + } else { + proc_macro2::TokenStream::new() + }; - // If a storage version is set, we should ensure that the storage version on chain matches the - // in-code storage version. This assumes that `Executive` is running custom migrations before - // the pallets are called. - let post_storage_version_check = if def.pallet_struct.storage_version.is_some() { - quote::quote! { - let on_chain_version = ::on_chain_storage_version(); - let in_code_version = ::in_code_storage_version(); + // If a storage version is set, we should ensure that the storage version on chain matches the + // in-code storage version. This assumes that `Executive` is running custom migrations before + // the pallets are called. + let post_storage_version_check = if def.pallet_struct.storage_version.is_some() { + quote::quote! { + let on_chain_version = ::on_chain_storage_version(); + let in_code_version = ::in_code_storage_version(); - if on_chain_version != in_code_version { - #frame_support::__private::log::error!( - target: #frame_support::LOG_TARGET, - "{}: On chain storage version {:?} doesn't match in-code storage version {:?}.", - #pallet_name, - on_chain_version, - in_code_version, - ); + if on_chain_version != in_code_version { + #frame_support::__private::log::error!( + target: #frame_support::LOG_TARGET, + "{}: On chain storage version {:?} doesn't match in-code storage version {:?}.", + #pallet_name, + on_chain_version, + in_code_version, + ); - return Err("On chain and in-code storage version do not match. Missing runtime upgrade?".into()); - } - } - } else { - quote::quote! { - let on_chain_version = ::on_chain_storage_version(); + return Err("On chain and in-code storage version do not match. Missing runtime upgrade?".into()); + } + } + } else { + quote::quote! { + let on_chain_version = ::on_chain_storage_version(); - if on_chain_version != #frame_support::traits::StorageVersion::new(0) { - #frame_support::__private::log::error!( - target: #frame_support::LOG_TARGET, - "{}: On chain storage version {:?} is set to non zero, \ - while the pallet is missing the `#[pallet::storage_version(VERSION)]` attribute.", - #pallet_name, - on_chain_version, - ); + if on_chain_version != #frame_support::traits::StorageVersion::new(0) { + #frame_support::__private::log::error!( + target: #frame_support::LOG_TARGET, + "{}: On chain storage version {:?} is set to non zero, \ + while the pallet is missing the `#[pallet::storage_version(VERSION)]` attribute.", + #pallet_name, + on_chain_version, + ); - return Err("On chain storage version set, while the pallet doesn't \ - have the `#[pallet::storage_version(VERSION)]` attribute.".into()); - } - } - }; + return Err("On chain storage version set, while the pallet doesn't \ + have the `#[pallet::storage_version(VERSION)]` attribute.".into()); + } + } + }; - quote::quote_spanned!(span => - #hooks_impl + quote::quote_spanned!(span => + #hooks_impl - impl<#type_impl_gen> - #frame_support::traits::OnFinalize<#frame_system::pallet_prelude::BlockNumberFor::> - for #pallet_ident<#type_use_gen> #where_clause - { - fn on_finalize(n: #frame_system::pallet_prelude::BlockNumberFor::) { - #frame_support::__private::sp_tracing::enter_span!( - #frame_support::__private::sp_tracing::trace_span!("on_finalize") - ); - < - Self as #frame_support::traits::Hooks< - #frame_system::pallet_prelude::BlockNumberFor:: - > - >::on_finalize(n) - } - } + impl<#type_impl_gen> + #frame_support::traits::OnFinalize<#frame_system::pallet_prelude::BlockNumberFor::> + for #pallet_ident<#type_use_gen> #where_clause + { + fn on_finalize(n: #frame_system::pallet_prelude::BlockNumberFor::) { + #frame_support::__private::sp_tracing::enter_span!( + #frame_support::__private::sp_tracing::trace_span!("on_finalize") + ); + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::on_finalize(n) + } + } - impl<#type_impl_gen> - #frame_support::traits::OnIdle<#frame_system::pallet_prelude::BlockNumberFor::> - for #pallet_ident<#type_use_gen> #where_clause - { - fn on_idle( - n: #frame_system::pallet_prelude::BlockNumberFor::, - remaining_weight: #frame_support::weights::Weight - ) -> #frame_support::weights::Weight { - < - Self as #frame_support::traits::Hooks< - #frame_system::pallet_prelude::BlockNumberFor:: - > - >::on_idle(n, remaining_weight) - } - } + impl<#type_impl_gen> + #frame_support::traits::OnIdle<#frame_system::pallet_prelude::BlockNumberFor::> + for #pallet_ident<#type_use_gen> #where_clause + { + fn on_idle( + n: #frame_system::pallet_prelude::BlockNumberFor::, + remaining_weight: #frame_support::weights::Weight + ) -> #frame_support::weights::Weight { + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::on_idle(n, remaining_weight) + } + } - impl<#type_impl_gen> - #frame_support::traits::OnPoll<#frame_system::pallet_prelude::BlockNumberFor::> - for #pallet_ident<#type_use_gen> #where_clause - { - fn on_poll( - n: #frame_system::pallet_prelude::BlockNumberFor::, - weight: &mut #frame_support::weights::WeightMeter - ) { - < - Self as #frame_support::traits::Hooks< - #frame_system::pallet_prelude::BlockNumberFor:: - > - >::on_poll(n, weight); - } - } + impl<#type_impl_gen> + #frame_support::traits::OnPoll<#frame_system::pallet_prelude::BlockNumberFor::> + for #pallet_ident<#type_use_gen> #where_clause + { + fn on_poll( + n: #frame_system::pallet_prelude::BlockNumberFor::, + weight: &mut #frame_support::weights::WeightMeter + ) { + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::on_poll(n, weight); + } + } - impl<#type_impl_gen> - #frame_support::traits::OnInitialize<#frame_system::pallet_prelude::BlockNumberFor::> - for #pallet_ident<#type_use_gen> #where_clause - { - fn on_initialize( - n: #frame_system::pallet_prelude::BlockNumberFor:: - ) -> #frame_support::weights::Weight { - #frame_support::__private::sp_tracing::enter_span!( - #frame_support::__private::sp_tracing::trace_span!("on_initialize") - ); - < - Self as #frame_support::traits::Hooks< - #frame_system::pallet_prelude::BlockNumberFor:: - > - >::on_initialize(n) - } - } + impl<#type_impl_gen> + #frame_support::traits::OnInitialize<#frame_system::pallet_prelude::BlockNumberFor::> + for #pallet_ident<#type_use_gen> #where_clause + { + fn on_initialize( + n: #frame_system::pallet_prelude::BlockNumberFor:: + ) -> #frame_support::weights::Weight { + #frame_support::__private::sp_tracing::enter_span!( + #frame_support::__private::sp_tracing::trace_span!("on_initialize") + ); + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::on_initialize(n) + } + } - impl<#type_impl_gen> - #frame_support::traits::BeforeAllRuntimeMigrations - for #pallet_ident<#type_use_gen> #where_clause - { - fn before_all_runtime_migrations() -> #frame_support::weights::Weight { - use #frame_support::traits::{Get, PalletInfoAccess}; - use #frame_support::__private::hashing::twox_128; - use #frame_support::storage::unhashed::contains_prefixed_key; - #frame_support::__private::sp_tracing::enter_span!( - #frame_support::__private::sp_tracing::trace_span!("before_all") - ); + impl<#type_impl_gen> + #frame_support::traits::BeforeAllRuntimeMigrations + for #pallet_ident<#type_use_gen> #where_clause + { + fn before_all_runtime_migrations() -> #frame_support::weights::Weight { + use #frame_support::traits::{Get, PalletInfoAccess}; + use #frame_support::__private::hashing::twox_128; + use #frame_support::storage::unhashed::contains_prefixed_key; + #frame_support::__private::sp_tracing::enter_span!( + #frame_support::__private::sp_tracing::trace_span!("before_all") + ); - // Check if the pallet has any keys set, including the storage version. If there are - // no keys set, the pallet was just added to the runtime and needs to have its - // version initialized. - let pallet_hashed_prefix = ::name_hash(); - let exists = contains_prefixed_key(&pallet_hashed_prefix); - if !exists { - #initialize_on_chain_storage_version - ::DbWeight::get().reads_writes(1, 1) - } else { - ::DbWeight::get().reads(1) - } - } - } + // Check if the pallet has any keys set, including the storage version. If there are + // no keys set, the pallet was just added to the runtime and needs to have its + // version initialized. + let pallet_hashed_prefix = ::name_hash(); + let exists = contains_prefixed_key(&pallet_hashed_prefix); + if !exists { + #initialize_on_chain_storage_version + ::DbWeight::get().reads_writes(1, 1) + } else { + ::DbWeight::get().reads(1) + } + } + } - impl<#type_impl_gen> - #frame_support::traits::OnRuntimeUpgrade - for #pallet_ident<#type_use_gen> #where_clause - { - fn on_runtime_upgrade() -> #frame_support::weights::Weight { - #frame_support::__private::sp_tracing::enter_span!( - #frame_support::__private::sp_tracing::trace_span!("on_runtime_update") - ); + impl<#type_impl_gen> + #frame_support::traits::OnRuntimeUpgrade + for #pallet_ident<#type_use_gen> #where_clause + { + fn on_runtime_upgrade() -> #frame_support::weights::Weight { + #frame_support::__private::sp_tracing::enter_span!( + #frame_support::__private::sp_tracing::trace_span!("on_runtime_update") + ); - // log info about the upgrade. - #log_runtime_upgrade + // log info about the upgrade. + #log_runtime_upgrade - < - Self as #frame_support::traits::Hooks< - #frame_system::pallet_prelude::BlockNumberFor:: - > - >::on_runtime_upgrade() - } + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::on_runtime_upgrade() + } - #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result<#frame_support::__private::sp_std::vec::Vec, #frame_support::sp_runtime::TryRuntimeError> { - < - Self - as - #frame_support::traits::Hooks<#frame_system::pallet_prelude::BlockNumberFor::> - >::pre_upgrade() - } + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result<#frame_support::__private::sp_std::vec::Vec, #frame_support::sp_runtime::TryRuntimeError> { + < + Self + as + #frame_support::traits::Hooks<#frame_system::pallet_prelude::BlockNumberFor::> + >::pre_upgrade() + } - #[cfg(feature = "try-runtime")] - fn post_upgrade(state: #frame_support::__private::sp_std::vec::Vec) -> Result<(), #frame_support::sp_runtime::TryRuntimeError> { - #post_storage_version_check + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: #frame_support::__private::sp_std::vec::Vec) -> Result<(), #frame_support::sp_runtime::TryRuntimeError> { + #post_storage_version_check - < - Self - as - #frame_support::traits::Hooks<#frame_system::pallet_prelude::BlockNumberFor::> - >::post_upgrade(state) - } - } + < + Self + as + #frame_support::traits::Hooks<#frame_system::pallet_prelude::BlockNumberFor::> + >::post_upgrade(state) + } + } - impl<#type_impl_gen> - #frame_support::traits::OffchainWorker<#frame_system::pallet_prelude::BlockNumberFor::> - for #pallet_ident<#type_use_gen> #where_clause - { - fn offchain_worker(n: #frame_system::pallet_prelude::BlockNumberFor::) { - < - Self as #frame_support::traits::Hooks< - #frame_system::pallet_prelude::BlockNumberFor:: - > - >::offchain_worker(n) - } - } + impl<#type_impl_gen> + #frame_support::traits::OffchainWorker<#frame_system::pallet_prelude::BlockNumberFor::> + for #pallet_ident<#type_use_gen> #where_clause + { + fn offchain_worker(n: #frame_system::pallet_prelude::BlockNumberFor::) { + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::offchain_worker(n) + } + } - // Integrity tests are only required for when `std` is enabled. - #frame_support::std_enabled! { - impl<#type_impl_gen> - #frame_support::traits::IntegrityTest - for #pallet_ident<#type_use_gen> #where_clause - { - fn integrity_test() { - #frame_support::__private::sp_io::TestExternalities::default().execute_with(|| { - < - Self as #frame_support::traits::Hooks< - #frame_system::pallet_prelude::BlockNumberFor:: - > - >::integrity_test() - }); - } - } - } + // Integrity tests are only required for when `std` is enabled. + #frame_support::std_enabled! { + impl<#type_impl_gen> + #frame_support::traits::IntegrityTest + for #pallet_ident<#type_use_gen> #where_clause + { + fn integrity_test() { + #frame_support::__private::sp_io::TestExternalities::default().execute_with(|| { + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::integrity_test() + }); + } + } + } - #[cfg(feature = "try-runtime")] - impl<#type_impl_gen> - #frame_support::traits::TryState<#frame_system::pallet_prelude::BlockNumberFor::> - for #pallet_ident<#type_use_gen> #where_clause - { - fn try_state( - n: #frame_system::pallet_prelude::BlockNumberFor::, - _s: #frame_support::traits::TryStateSelect - ) -> Result<(), #frame_support::sp_runtime::TryRuntimeError> { - #frame_support::__private::log::info!( - target: #frame_support::LOG_TARGET, - "🩺 Running {:?} try-state checks", - #pallet_name, - ); - < - Self as #frame_support::traits::Hooks< - #frame_system::pallet_prelude::BlockNumberFor:: - > - >::try_state(n).map_err(|err| { - #frame_support::__private::log::error!( - target: #frame_support::LOG_TARGET, - "❌ {:?} try_state checks failed: {:?}", - #pallet_name, - err - ); + #[cfg(feature = "try-runtime")] + impl<#type_impl_gen> + #frame_support::traits::TryState<#frame_system::pallet_prelude::BlockNumberFor::> + for #pallet_ident<#type_use_gen> #where_clause + { + fn try_state( + n: #frame_system::pallet_prelude::BlockNumberFor::, + _s: #frame_support::traits::TryStateSelect + ) -> Result<(), #frame_support::sp_runtime::TryRuntimeError> { + #frame_support::__private::log::info!( + target: #frame_support::LOG_TARGET, + "🩺 Running {:?} try-state checks", + #pallet_name, + ); + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::try_state(n).map_err(|err| { + #frame_support::__private::log::error!( + target: #frame_support::LOG_TARGET, + "❌ {:?} try_state checks failed: {:?}", + #pallet_name, + err + ); - err - }) - } - } - ) + err + }) + } + } + ) } diff --git a/support/procedural-fork/src/pallet/expand/inherent.rs b/support/procedural-fork/src/pallet/expand/inherent.rs index 0a80d672a..182d79f5b 100644 --- a/support/procedural-fork/src/pallet/expand/inherent.rs +++ b/support/procedural-fork/src/pallet/expand/inherent.rs @@ -21,38 +21,35 @@ use quote::quote; use syn::{spanned::Spanned, Ident}; pub fn expand_inherents(def: &mut Def) -> TokenStream { - let count = COUNTER.with(|counter| counter.borrow_mut().inc()); - let macro_ident = Ident::new( - &format!("__is_inherent_part_defined_{}", count), - def.item.span(), - ); + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + let macro_ident = Ident::new(&format!("__is_inherent_part_defined_{}", count), def.item.span()); - let maybe_compile_error = if def.inherent.is_none() { - quote! { - compile_error!(concat!( - "`", - stringify!($pallet_name), - "` does not have #[pallet::inherent] defined, perhaps you should \ - remove `Inherent` from construct_runtime?", - )); - } - } else { - TokenStream::new() - }; + let maybe_compile_error = if def.inherent.is_none() { + quote! { + compile_error!(concat!( + "`", + stringify!($pallet_name), + "` does not have #[pallet::inherent] defined, perhaps you should \ + remove `Inherent` from construct_runtime?", + )); + } + } else { + TokenStream::new() + }; - quote! { - #[doc(hidden)] - pub mod __substrate_inherent_check { - #[macro_export] - #[doc(hidden)] - macro_rules! #macro_ident { - ($pallet_name:ident) => { - #maybe_compile_error - } - } + quote! { + #[doc(hidden)] + pub mod __substrate_inherent_check { + #[macro_export] + #[doc(hidden)] + macro_rules! #macro_ident { + ($pallet_name:ident) => { + #maybe_compile_error + } + } - #[doc(hidden)] - pub use #macro_ident as is_inherent_part_defined; - } - } + #[doc(hidden)] + pub use #macro_ident as is_inherent_part_defined; + } + } } diff --git a/support/procedural-fork/src/pallet/expand/instances.rs b/support/procedural-fork/src/pallet/expand/instances.rs index 12423409c..b6dfa7e6d 100644 --- a/support/procedural-fork/src/pallet/expand/instances.rs +++ b/support/procedural-fork/src/pallet/expand/instances.rs @@ -22,22 +22,22 @@ use proc_macro2::Span; /// * Provide inherent instance to be used by construct_runtime /// * Provide Instance1 ..= Instance16 for instantiable pallet pub fn expand_instances(def: &mut Def) -> proc_macro2::TokenStream { - let frame_support = &def.frame_support; - let inherent_ident = syn::Ident::new(crate::INHERENT_INSTANCE_NAME, Span::call_site()); - let instances = if def.config.has_instance { - (1..=NUMBER_OF_INSTANCE) - .map(|i| syn::Ident::new(&format!("Instance{}", i), Span::call_site())) - .collect() - } else { - vec![] - }; + let frame_support = &def.frame_support; + let inherent_ident = syn::Ident::new(crate::INHERENT_INSTANCE_NAME, Span::call_site()); + let instances = if def.config.has_instance { + (1..=NUMBER_OF_INSTANCE) + .map(|i| syn::Ident::new(&format!("Instance{}", i), Span::call_site())) + .collect() + } else { + vec![] + }; - quote::quote!( - /// Hidden instance generated to be internally used when module is used without - /// instance. - #[doc(hidden)] - pub type #inherent_ident = (); + quote::quote!( + /// Hidden instance generated to be internally used when module is used without + /// instance. + #[doc(hidden)] + pub type #inherent_ident = (); - #( pub use #frame_support::instances::#instances; )* - ) + #( pub use #frame_support::instances::#instances; )* + ) } diff --git a/support/procedural-fork/src/pallet/expand/mod.rs b/support/procedural-fork/src/pallet/expand/mod.rs index ff4423f85..067839c28 100644 --- a/support/procedural-fork/src/pallet/expand/mod.rs +++ b/support/procedural-fork/src/pallet/expand/mod.rs @@ -42,12 +42,12 @@ use quote::ToTokens; /// Merge where clause together, `where` token span is taken from the first not none one. pub fn merge_where_clauses(clauses: &[&Option]) -> Option { - let mut clauses = clauses.iter().filter_map(|f| f.as_ref()); - let mut res = clauses.next()?.clone(); - for other in clauses { - res.predicates.extend(other.predicates.iter().cloned()) - } - Some(res) + let mut clauses = clauses.iter().filter_map(|f| f.as_ref()); + let mut res = clauses.next()?.clone(); + for other in clauses { + res.predicates.extend(other.predicates.iter().cloned()) + } + Some(res) } /// Expand definition, in particular: @@ -55,32 +55,32 @@ pub fn merge_where_clauses(clauses: &[&Option]) -> Option proc_macro2::TokenStream { - // Remove the `pallet_doc` attribute first. - let metadata_docs = documentation::expand_documentation(&mut def); - let constants = constants::expand_constants(&mut def); - let pallet_struct = pallet_struct::expand_pallet_struct(&mut def); - let config = config::expand_config(&mut def); - let call = call::expand_call(&mut def); - let tasks = tasks::expand_tasks(&mut def); - let error = error::expand_error(&mut def); - let event = event::expand_event(&mut def); - let storages = storage::expand_storages(&mut def); - let inherents = inherent::expand_inherents(&mut def); - let instances = instances::expand_instances(&mut def); - let hooks = hooks::expand_hooks(&mut def); - let genesis_build = genesis_build::expand_genesis_build(&mut def); - let genesis_config = genesis_config::expand_genesis_config(&mut def); - let type_values = type_value::expand_type_values(&mut def); - let origins = origin::expand_origins(&mut def); - let validate_unsigned = validate_unsigned::expand_validate_unsigned(&mut def); - let tt_default_parts = tt_default_parts::expand_tt_default_parts(&mut def); - let doc_only = doc_only::expand_doc_only(&mut def); - let composites = composite::expand_composites(&mut def); + // Remove the `pallet_doc` attribute first. + let metadata_docs = documentation::expand_documentation(&mut def); + let constants = constants::expand_constants(&mut def); + let pallet_struct = pallet_struct::expand_pallet_struct(&mut def); + let config = config::expand_config(&mut def); + let call = call::expand_call(&mut def); + let tasks = tasks::expand_tasks(&mut def); + let error = error::expand_error(&mut def); + let event = event::expand_event(&mut def); + let storages = storage::expand_storages(&mut def); + let inherents = inherent::expand_inherents(&mut def); + let instances = instances::expand_instances(&mut def); + let hooks = hooks::expand_hooks(&mut def); + let genesis_build = genesis_build::expand_genesis_build(&mut def); + let genesis_config = genesis_config::expand_genesis_config(&mut def); + let type_values = type_value::expand_type_values(&mut def); + let origins = origin::expand_origins(&mut def); + let validate_unsigned = validate_unsigned::expand_validate_unsigned(&mut def); + let tt_default_parts = tt_default_parts::expand_tt_default_parts(&mut def); + let doc_only = doc_only::expand_doc_only(&mut def); + let composites = composite::expand_composites(&mut def); - def.item.attrs.insert( - 0, - syn::parse_quote!( - #[doc = r"The `pallet` module in each FRAME pallet hosts the most important items needed + def.item.attrs.insert( + 0, + syn::parse_quote!( + #[doc = r"The `pallet` module in each FRAME pallet hosts the most important items needed to construct this pallet. The main components of this pallet are: @@ -93,38 +93,38 @@ storage item. Otherwise, all storage items are listed among [*Type Definitions*] - [`Config`], which contains the configuration trait of this pallet. - [`Event`] and [`Error`], which are listed among the [*Enums*](#enums). "] - ), - ); + ), + ); - let new_items = quote::quote!( - #metadata_docs - #constants - #pallet_struct - #config - #call - #tasks - #error - #event - #storages - #inherents - #instances - #hooks - #genesis_build - #genesis_config - #type_values - #origins - #validate_unsigned - #tt_default_parts - #doc_only - #composites - ); + let new_items = quote::quote!( + #metadata_docs + #constants + #pallet_struct + #config + #call + #tasks + #error + #event + #storages + #inherents + #instances + #hooks + #genesis_build + #genesis_config + #type_values + #origins + #validate_unsigned + #tt_default_parts + #doc_only + #composites + ); - def.item - .content - .as_mut() - .expect("This is checked by parsing") - .1 - .push(syn::Item::Verbatim(new_items)); + def.item + .content + .as_mut() + .expect("This is checked by parsing") + .1 + .push(syn::Item::Verbatim(new_items)); - def.item.into_token_stream() + def.item.into_token_stream() } diff --git a/support/procedural-fork/src/pallet/expand/origin.rs b/support/procedural-fork/src/pallet/expand/origin.rs index 167445ad6..55865b424 100644 --- a/support/procedural-fork/src/pallet/expand/origin.rs +++ b/support/procedural-fork/src/pallet/expand/origin.rs @@ -21,38 +21,35 @@ use quote::quote; use syn::{spanned::Spanned, Ident}; pub fn expand_origins(def: &mut Def) -> TokenStream { - let count = COUNTER.with(|counter| counter.borrow_mut().inc()); - let macro_ident = Ident::new( - &format!("__is_origin_part_defined_{}", count), - def.item.span(), - ); + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + let macro_ident = Ident::new(&format!("__is_origin_part_defined_{}", count), def.item.span()); - let maybe_compile_error = if def.origin.is_none() { - quote! { - compile_error!(concat!( - "`", - stringify!($pallet_name), - "` does not have #[pallet::origin] defined, perhaps you should \ - remove `Origin` from construct_runtime?", - )); - } - } else { - TokenStream::new() - }; + let maybe_compile_error = if def.origin.is_none() { + quote! { + compile_error!(concat!( + "`", + stringify!($pallet_name), + "` does not have #[pallet::origin] defined, perhaps you should \ + remove `Origin` from construct_runtime?", + )); + } + } else { + TokenStream::new() + }; - quote! { - #[doc(hidden)] - pub mod __substrate_origin_check { - #[macro_export] - #[doc(hidden)] - macro_rules! #macro_ident { - ($pallet_name:ident) => { - #maybe_compile_error - } - } + quote! { + #[doc(hidden)] + pub mod __substrate_origin_check { + #[macro_export] + #[doc(hidden)] + macro_rules! #macro_ident { + ($pallet_name:ident) => { + #maybe_compile_error + } + } - #[doc(hidden)] - pub use #macro_ident as is_origin_part_defined; - } - } + #[doc(hidden)] + pub use #macro_ident as is_origin_part_defined; + } + } } diff --git a/support/procedural-fork/src/pallet/expand/pallet_struct.rs b/support/procedural-fork/src/pallet/expand/pallet_struct.rs index c5def65ed..7cdf6bde9 100644 --- a/support/procedural-fork/src/pallet/expand/pallet_struct.rs +++ b/support/procedural-fork/src/pallet/expand/pallet_struct.rs @@ -28,275 +28,263 @@ use frame_support_procedural_tools::get_doc_literals; /// * implementation of `PalletInfoAccess` information /// * implementation of `StorageInfoTrait` on Pallet pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream { - let frame_support = &def.frame_support; - let frame_system = &def.frame_system; - let type_impl_gen = &def.type_impl_generics(def.pallet_struct.attr_span); - let type_use_gen = &def.type_use_generics(def.pallet_struct.attr_span); - let type_decl_gen = &def.type_decl_generics(def.pallet_struct.attr_span); - let pallet_ident = &def.pallet_struct.pallet; - let config_where_clause = &def.config.where_clause; - - let mut storages_where_clauses = vec![&def.config.where_clause]; - storages_where_clauses.extend(def.storages.iter().map(|storage| &storage.where_clause)); - let storages_where_clauses = merge_where_clauses(&storages_where_clauses); - - let pallet_item = { - let pallet_module_items = &mut def.item.content.as_mut().expect("Checked by def").1; - let item = &mut pallet_module_items[def.pallet_struct.index]; - if let syn::Item::Struct(item) = item { - item - } else { - unreachable!("Checked by pallet struct parser") - } - }; - - // If the first field type is `_` then we replace with `PhantomData` - if let Some(field) = pallet_item.fields.iter_mut().next() { - if field.ty == syn::parse_quote!(_) { - field.ty = syn::parse_quote!( - #frame_support::__private::sp_std::marker::PhantomData<(#type_use_gen)> - ); - } - } - - if get_doc_literals(&pallet_item.attrs).is_empty() { - pallet_item.attrs.push(syn::parse_quote!( - #[doc = r" + let frame_support = &def.frame_support; + let frame_system = &def.frame_system; + let type_impl_gen = &def.type_impl_generics(def.pallet_struct.attr_span); + let type_use_gen = &def.type_use_generics(def.pallet_struct.attr_span); + let type_decl_gen = &def.type_decl_generics(def.pallet_struct.attr_span); + let pallet_ident = &def.pallet_struct.pallet; + let config_where_clause = &def.config.where_clause; + + let mut storages_where_clauses = vec![&def.config.where_clause]; + storages_where_clauses.extend(def.storages.iter().map(|storage| &storage.where_clause)); + let storages_where_clauses = merge_where_clauses(&storages_where_clauses); + + let pallet_item = { + let pallet_module_items = &mut def.item.content.as_mut().expect("Checked by def").1; + let item = &mut pallet_module_items[def.pallet_struct.index]; + if let syn::Item::Struct(item) = item { + item + } else { + unreachable!("Checked by pallet struct parser") + } + }; + + // If the first field type is `_` then we replace with `PhantomData` + if let Some(field) = pallet_item.fields.iter_mut().next() { + if field.ty == syn::parse_quote!(_) { + field.ty = syn::parse_quote!( + #frame_support::__private::sp_std::marker::PhantomData<(#type_use_gen)> + ); + } + } + + if get_doc_literals(&pallet_item.attrs).is_empty() { + pallet_item.attrs.push(syn::parse_quote!( + #[doc = r" The `Pallet` struct, the main type that implements traits and standalone functions within the pallet. "] - )); - } - - pallet_item.attrs.push(syn::parse_quote!( - #[derive( - #frame_support::CloneNoBound, - #frame_support::EqNoBound, - #frame_support::PartialEqNoBound, - #frame_support::RuntimeDebugNoBound, - )] - )); - - let pallet_error_metadata = if let Some(error_def) = &def.error { - let error_ident = &error_def.error; - quote::quote_spanned!(def.pallet_struct.attr_span => - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #config_where_clause { - #[doc(hidden)] - pub fn error_metadata() -> Option<#frame_support::__private::metadata_ir::PalletErrorMetadataIR> { - Some(#frame_support::__private::metadata_ir::PalletErrorMetadataIR { - ty: #frame_support::__private::scale_info::meta_type::<#error_ident<#type_use_gen>>() - }) - } - } - ) - } else { - quote::quote_spanned!(def.pallet_struct.attr_span => - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #config_where_clause { - #[doc(hidden)] - pub fn error_metadata() -> Option<#frame_support::__private::metadata_ir::PalletErrorMetadataIR> { - None - } - } - ) - }; - - let storage_info_span = def - .pallet_struct - .without_storage_info - .unwrap_or(def.pallet_struct.attr_span); - - let storage_names = &def - .storages - .iter() - .map(|storage| &storage.ident) - .collect::>(); - let storage_cfg_attrs = &def - .storages - .iter() - .map(|storage| &storage.cfg_attrs) - .collect::>(); - - // Depending on the flag `without_storage_info` and the storage attribute `unbounded`, we use - // partial or full storage info from storage. - let storage_info_traits = &def - .storages - .iter() - .map(|storage| { - if storage.unbounded || def.pallet_struct.without_storage_info.is_some() { - quote::quote_spanned!(storage_info_span => PartialStorageInfoTrait) - } else { - quote::quote_spanned!(storage_info_span => StorageInfoTrait) - } - }) - .collect::>(); - - let storage_info_methods = &def - .storages - .iter() - .map(|storage| { - if storage.unbounded || def.pallet_struct.without_storage_info.is_some() { - quote::quote_spanned!(storage_info_span => partial_storage_info) - } else { - quote::quote_spanned!(storage_info_span => storage_info) - } - }) - .collect::>(); - - let storage_info = quote::quote_spanned!(storage_info_span => - impl<#type_impl_gen> #frame_support::traits::StorageInfoTrait - for #pallet_ident<#type_use_gen> - #storages_where_clauses - { - fn storage_info() - -> #frame_support::__private::sp_std::vec::Vec<#frame_support::traits::StorageInfo> - { - #[allow(unused_mut)] - let mut res = #frame_support::__private::sp_std::vec![]; - - #( - #(#storage_cfg_attrs)* - { - let mut storage_info = < - #storage_names<#type_use_gen> - as #frame_support::traits::#storage_info_traits - >::#storage_info_methods(); - res.append(&mut storage_info); - } - )* - - res - } - } - ); - - let (storage_version, in_code_storage_version_ty) = - if let Some(v) = def.pallet_struct.storage_version.as_ref() { - ( - quote::quote! { #v }, - quote::quote! { #frame_support::traits::StorageVersion }, - ) - } else { - ( - quote::quote! { core::default::Default::default() }, - quote::quote! { #frame_support::traits::NoStorageVersionSet }, - ) - }; - - let whitelisted_storage_idents: Vec = def - .storages - .iter() - .filter_map(|s| s.whitelisted.then_some(s.ident.clone())) - .collect(); - - let whitelisted_storage_keys_impl = quote::quote![ - use #frame_support::traits::{StorageInfoTrait, TrackedStorageKey, WhitelistedStorageKeys}; - impl<#type_impl_gen> WhitelistedStorageKeys for #pallet_ident<#type_use_gen> #storages_where_clauses { - fn whitelisted_storage_keys() -> #frame_support::__private::sp_std::vec::Vec { - use #frame_support::__private::sp_std::vec; - vec![#( - TrackedStorageKey::new(#whitelisted_storage_idents::<#type_use_gen>::hashed_key().to_vec()) - ),*] - } - } - ]; - - quote::quote_spanned!(def.pallet_struct.attr_span => - #pallet_error_metadata - - /// Type alias to `Pallet`, to be used by `construct_runtime`. - /// - /// Generated by `pallet` attribute macro. - #[deprecated(note = "use `Pallet` instead")] - #[allow(dead_code)] - pub type Module<#type_decl_gen> = #pallet_ident<#type_use_gen>; - - // Implement `GetStorageVersion` for `Pallet` - impl<#type_impl_gen> #frame_support::traits::GetStorageVersion - for #pallet_ident<#type_use_gen> - #config_where_clause - { - type InCodeStorageVersion = #in_code_storage_version_ty; - - fn in_code_storage_version() -> Self::InCodeStorageVersion { - #storage_version - } - - fn on_chain_storage_version() -> #frame_support::traits::StorageVersion { - #frame_support::traits::StorageVersion::get::() - } - } - - // Implement `OnGenesis` for `Pallet` - impl<#type_impl_gen> #frame_support::traits::OnGenesis - for #pallet_ident<#type_use_gen> - #config_where_clause - { - fn on_genesis() { - let storage_version: #frame_support::traits::StorageVersion = #storage_version; - storage_version.put::(); - } - } - - // Implement `PalletInfoAccess` for `Pallet` - impl<#type_impl_gen> #frame_support::traits::PalletInfoAccess - for #pallet_ident<#type_use_gen> - #config_where_clause - { - fn index() -> usize { - < - ::PalletInfo as #frame_support::traits::PalletInfo - >::index::() - .expect("Pallet is part of the runtime because pallet `Config` trait is \ - implemented by the runtime") - } - - fn name() -> &'static str { - < - ::PalletInfo as #frame_support::traits::PalletInfo - >::name::() - .expect("Pallet is part of the runtime because pallet `Config` trait is \ - implemented by the runtime") - } - - fn name_hash() -> [u8; 16] { - < - ::PalletInfo as #frame_support::traits::PalletInfo - >::name_hash::() - .expect("Pallet is part of the runtime because pallet `Config` trait is \ - implemented by the runtime") - } - - fn module_name() -> &'static str { - < - ::PalletInfo as #frame_support::traits::PalletInfo - >::module_name::() - .expect("Pallet is part of the runtime because pallet `Config` trait is \ - implemented by the runtime") - } - - fn crate_version() -> #frame_support::traits::CrateVersion { - #frame_support::crate_to_crate_version!() - } - } - - impl<#type_impl_gen> #frame_support::traits::PalletsInfoAccess - for #pallet_ident<#type_use_gen> - #config_where_clause - { - fn count() -> usize { 1 } - fn infos() -> #frame_support::__private::sp_std::vec::Vec<#frame_support::traits::PalletInfoData> { - use #frame_support::traits::PalletInfoAccess; - let item = #frame_support::traits::PalletInfoData { - index: Self::index(), - name: Self::name(), - module_name: Self::module_name(), - crate_version: Self::crate_version(), - }; - #frame_support::__private::sp_std::vec![item] - } - } - - #storage_info - #whitelisted_storage_keys_impl - ) + )); + } + + pallet_item.attrs.push(syn::parse_quote!( + #[derive( + #frame_support::CloneNoBound, + #frame_support::EqNoBound, + #frame_support::PartialEqNoBound, + #frame_support::RuntimeDebugNoBound, + )] + )); + + let pallet_error_metadata = if let Some(error_def) = &def.error { + let error_ident = &error_def.error; + quote::quote_spanned!(def.pallet_struct.attr_span => + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #config_where_clause { + #[doc(hidden)] + pub fn error_metadata() -> Option<#frame_support::__private::metadata_ir::PalletErrorMetadataIR> { + Some(#frame_support::__private::metadata_ir::PalletErrorMetadataIR { + ty: #frame_support::__private::scale_info::meta_type::<#error_ident<#type_use_gen>>() + }) + } + } + ) + } else { + quote::quote_spanned!(def.pallet_struct.attr_span => + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #config_where_clause { + #[doc(hidden)] + pub fn error_metadata() -> Option<#frame_support::__private::metadata_ir::PalletErrorMetadataIR> { + None + } + } + ) + }; + + let storage_info_span = + def.pallet_struct.without_storage_info.unwrap_or(def.pallet_struct.attr_span); + + let storage_names = &def.storages.iter().map(|storage| &storage.ident).collect::>(); + let storage_cfg_attrs = + &def.storages.iter().map(|storage| &storage.cfg_attrs).collect::>(); + + // Depending on the flag `without_storage_info` and the storage attribute `unbounded`, we use + // partial or full storage info from storage. + let storage_info_traits = &def + .storages + .iter() + .map(|storage| { + if storage.unbounded || def.pallet_struct.without_storage_info.is_some() { + quote::quote_spanned!(storage_info_span => PartialStorageInfoTrait) + } else { + quote::quote_spanned!(storage_info_span => StorageInfoTrait) + } + }) + .collect::>(); + + let storage_info_methods = &def + .storages + .iter() + .map(|storage| { + if storage.unbounded || def.pallet_struct.without_storage_info.is_some() { + quote::quote_spanned!(storage_info_span => partial_storage_info) + } else { + quote::quote_spanned!(storage_info_span => storage_info) + } + }) + .collect::>(); + + let storage_info = quote::quote_spanned!(storage_info_span => + impl<#type_impl_gen> #frame_support::traits::StorageInfoTrait + for #pallet_ident<#type_use_gen> + #storages_where_clauses + { + fn storage_info() + -> #frame_support::__private::sp_std::vec::Vec<#frame_support::traits::StorageInfo> + { + #[allow(unused_mut)] + let mut res = #frame_support::__private::sp_std::vec![]; + + #( + #(#storage_cfg_attrs)* + { + let mut storage_info = < + #storage_names<#type_use_gen> + as #frame_support::traits::#storage_info_traits + >::#storage_info_methods(); + res.append(&mut storage_info); + } + )* + + res + } + } + ); + + let (storage_version, in_code_storage_version_ty) = + if let Some(v) = def.pallet_struct.storage_version.as_ref() { + (quote::quote! { #v }, quote::quote! { #frame_support::traits::StorageVersion }) + } else { + ( + quote::quote! { core::default::Default::default() }, + quote::quote! { #frame_support::traits::NoStorageVersionSet }, + ) + }; + + let whitelisted_storage_idents: Vec = def + .storages + .iter() + .filter_map(|s| s.whitelisted.then_some(s.ident.clone())) + .collect(); + + let whitelisted_storage_keys_impl = quote::quote![ + use #frame_support::traits::{StorageInfoTrait, TrackedStorageKey, WhitelistedStorageKeys}; + impl<#type_impl_gen> WhitelistedStorageKeys for #pallet_ident<#type_use_gen> #storages_where_clauses { + fn whitelisted_storage_keys() -> #frame_support::__private::sp_std::vec::Vec { + use #frame_support::__private::sp_std::vec; + vec![#( + TrackedStorageKey::new(#whitelisted_storage_idents::<#type_use_gen>::hashed_key().to_vec()) + ),*] + } + } + ]; + + quote::quote_spanned!(def.pallet_struct.attr_span => + #pallet_error_metadata + + /// Type alias to `Pallet`, to be used by `construct_runtime`. + /// + /// Generated by `pallet` attribute macro. + #[deprecated(note = "use `Pallet` instead")] + #[allow(dead_code)] + pub type Module<#type_decl_gen> = #pallet_ident<#type_use_gen>; + + // Implement `GetStorageVersion` for `Pallet` + impl<#type_impl_gen> #frame_support::traits::GetStorageVersion + for #pallet_ident<#type_use_gen> + #config_where_clause + { + type InCodeStorageVersion = #in_code_storage_version_ty; + + fn in_code_storage_version() -> Self::InCodeStorageVersion { + #storage_version + } + + fn on_chain_storage_version() -> #frame_support::traits::StorageVersion { + #frame_support::traits::StorageVersion::get::() + } + } + + // Implement `OnGenesis` for `Pallet` + impl<#type_impl_gen> #frame_support::traits::OnGenesis + for #pallet_ident<#type_use_gen> + #config_where_clause + { + fn on_genesis() { + let storage_version: #frame_support::traits::StorageVersion = #storage_version; + storage_version.put::(); + } + } + + // Implement `PalletInfoAccess` for `Pallet` + impl<#type_impl_gen> #frame_support::traits::PalletInfoAccess + for #pallet_ident<#type_use_gen> + #config_where_clause + { + fn index() -> usize { + < + ::PalletInfo as #frame_support::traits::PalletInfo + >::index::() + .expect("Pallet is part of the runtime because pallet `Config` trait is \ + implemented by the runtime") + } + + fn name() -> &'static str { + < + ::PalletInfo as #frame_support::traits::PalletInfo + >::name::() + .expect("Pallet is part of the runtime because pallet `Config` trait is \ + implemented by the runtime") + } + + fn name_hash() -> [u8; 16] { + < + ::PalletInfo as #frame_support::traits::PalletInfo + >::name_hash::() + .expect("Pallet is part of the runtime because pallet `Config` trait is \ + implemented by the runtime") + } + + fn module_name() -> &'static str { + < + ::PalletInfo as #frame_support::traits::PalletInfo + >::module_name::() + .expect("Pallet is part of the runtime because pallet `Config` trait is \ + implemented by the runtime") + } + + fn crate_version() -> #frame_support::traits::CrateVersion { + #frame_support::crate_to_crate_version!() + } + } + + impl<#type_impl_gen> #frame_support::traits::PalletsInfoAccess + for #pallet_ident<#type_use_gen> + #config_where_clause + { + fn count() -> usize { 1 } + fn infos() -> #frame_support::__private::sp_std::vec::Vec<#frame_support::traits::PalletInfoData> { + use #frame_support::traits::PalletInfoAccess; + let item = #frame_support::traits::PalletInfoData { + index: Self::index(), + name: Self::name(), + module_name: Self::module_name(), + crate_version: Self::crate_version(), + }; + #frame_support::__private::sp_std::vec![item] + } + } + + #storage_info + #whitelisted_storage_keys_impl + ) } diff --git a/support/procedural-fork/src/pallet/expand/storage.rs b/support/procedural-fork/src/pallet/expand/storage.rs index 32752dc52..937b068cf 100644 --- a/support/procedural-fork/src/pallet/expand/storage.rs +++ b/support/procedural-fork/src/pallet/expand/storage.rs @@ -16,14 +16,14 @@ // limitations under the License. use crate::{ - counter_prefix, - pallet::{ - parse::{ - helper::two128_str, - storage::{Metadata, QueryKind, StorageDef, StorageGenerics}, - }, - Def, - }, + counter_prefix, + pallet::{ + parse::{ + helper::two128_str, + storage::{Metadata, QueryKind, StorageDef, StorageGenerics}, + }, + Def, + }, }; use quote::ToTokens; use std::{collections::HashMap, ops::IndexMut}; @@ -32,76 +32,73 @@ use syn::spanned::Spanned; /// Generate the prefix_ident related to the storage. /// prefix_ident is used for the prefix struct to be given to storage as first generic param. fn prefix_ident(storage: &StorageDef) -> syn::Ident { - let storage_ident = &storage.ident; - syn::Ident::new( - &format!("_GeneratedPrefixForStorage{}", storage_ident), - storage_ident.span(), - ) + let storage_ident = &storage.ident; + syn::Ident::new(&format!("_GeneratedPrefixForStorage{}", storage_ident), storage_ident.span()) } /// Generate the counter_prefix_ident related to the storage. /// counter_prefix_ident is used for the prefix struct to be given to counted storage map. fn counter_prefix_ident(storage_ident: &syn::Ident) -> syn::Ident { - syn::Ident::new( - &format!("_GeneratedCounterPrefixForStorage{}", storage_ident), - storage_ident.span(), - ) + syn::Ident::new( + &format!("_GeneratedCounterPrefixForStorage{}", storage_ident), + storage_ident.span(), + ) } /// Check for duplicated storage prefixes. This step is necessary since users can specify an /// alternative storage prefix using the #[pallet::storage_prefix] syntax, and we need to ensure /// that the prefix specified by the user is not a duplicate of an existing one. fn check_prefix_duplicates( - storage_def: &StorageDef, - // A hashmap of all already used prefix and their associated error if duplication - used_prefixes: &mut HashMap, + storage_def: &StorageDef, + // A hashmap of all already used prefix and their associated error if duplication + used_prefixes: &mut HashMap, ) -> syn::Result<()> { - let prefix = storage_def.prefix(); - let dup_err = syn::Error::new( - storage_def.prefix_span(), - format!("Duplicate storage prefixes found for `{}`", prefix), - ); - - if let Some(other_dup_err) = used_prefixes.insert(prefix.clone(), dup_err.clone()) { - let mut err = dup_err; - err.combine(other_dup_err); - return Err(err); - } - - if let Metadata::CountedMap { .. } = storage_def.metadata { - let counter_prefix = counter_prefix(&prefix); - let counter_dup_err = syn::Error::new( - storage_def.prefix_span(), - format!( - "Duplicate storage prefixes found for `{}`, used for counter associated to \ + let prefix = storage_def.prefix(); + let dup_err = syn::Error::new( + storage_def.prefix_span(), + format!("Duplicate storage prefixes found for `{}`", prefix), + ); + + if let Some(other_dup_err) = used_prefixes.insert(prefix.clone(), dup_err.clone()) { + let mut err = dup_err; + err.combine(other_dup_err); + return Err(err) + } + + if let Metadata::CountedMap { .. } = storage_def.metadata { + let counter_prefix = counter_prefix(&prefix); + let counter_dup_err = syn::Error::new( + storage_def.prefix_span(), + format!( + "Duplicate storage prefixes found for `{}`, used for counter associated to \ counted storage map", - counter_prefix, - ), - ); - - if let Some(other_dup_err) = used_prefixes.insert(counter_prefix, counter_dup_err.clone()) { - let mut err = counter_dup_err; - err.combine(other_dup_err); - return Err(err); - } - } - - Ok(()) + counter_prefix, + ), + ); + + if let Some(other_dup_err) = used_prefixes.insert(counter_prefix, counter_dup_err.clone()) { + let mut err = counter_dup_err; + err.combine(other_dup_err); + return Err(err) + } + } + + Ok(()) } pub struct ResultOnEmptyStructMetadata { - /// The Rust ident that is going to be used as the name of the OnEmpty struct. - pub name: syn::Ident, - /// The path to the error type being returned by the ResultQuery. - pub error_path: syn::Path, - /// The visibility of the OnEmpty struct. - pub visibility: syn::Visibility, - /// The type of the storage item. - pub value_ty: syn::Type, - /// The name of the pallet error enum variant that is going to be returned. - pub variant_name: syn::Ident, - /// The span used to report compilation errors about the OnEmpty struct. - pub span: proc_macro2::Span, + /// The Rust ident that is going to be used as the name of the OnEmpty struct. + pub name: syn::Ident, + /// The path to the error type being returned by the ResultQuery. + pub error_path: syn::Path, + /// The visibility of the OnEmpty struct. + pub visibility: syn::Visibility, + /// The type of the storage item. + pub value_ty: syn::Type, + /// The name of the pallet error enum variant that is going to be returned. + pub variant_name: syn::Ident, + /// The span used to report compilation errors about the OnEmpty struct. + pub span: proc_macro2::Span, } /// @@ -109,305 +106,277 @@ pub struct ResultOnEmptyStructMetadata { /// * if generics are named: reorder the generic, remove their name, and add the missing ones. /// * Add `#[allow(type_alias_bounds)]` pub fn process_generics(def: &mut Def) -> syn::Result> { - let frame_support = &def.frame_support; - let mut on_empty_struct_metadata = Vec::new(); - - for storage_def in def.storages.iter_mut() { - let item = &mut def.item.content.as_mut().expect("Checked by def").1[storage_def.index]; - - let typ_item = match item { - syn::Item::Type(t) => t, - _ => unreachable!("Checked by def"), - }; - - typ_item - .attrs - .push(syn::parse_quote!(#[allow(type_alias_bounds)])); - - let typ_path = match &mut *typ_item.ty { - syn::Type::Path(p) => p, - _ => unreachable!("Checked by def"), - }; - - let args = match &mut typ_path.path.segments[0].arguments { - syn::PathArguments::AngleBracketed(args) => args, - _ => unreachable!("Checked by def"), - }; - - let prefix_ident = prefix_ident(storage_def); - let type_use_gen = if def.config.has_instance { - quote::quote_spanned!(storage_def.attr_span => T, I) - } else { - quote::quote_spanned!(storage_def.attr_span => T) - }; - - let default_query_kind: syn::Type = - syn::parse_quote!(#frame_support::storage::types::OptionQuery); - let mut default_on_empty = |value_ty: syn::Type| -> syn::Type { - if let Some(QueryKind::ResultQuery(error_path, variant_name)) = - storage_def.query_kind.as_ref() - { - let on_empty_ident = - quote::format_ident!("__Frame_Internal_Get{}Result", storage_def.ident); - on_empty_struct_metadata.push(ResultOnEmptyStructMetadata { - name: on_empty_ident.clone(), - visibility: storage_def.vis.clone(), - value_ty, - error_path: error_path.clone(), - variant_name: variant_name.clone(), - span: storage_def.attr_span, - }); - return syn::parse_quote!(#on_empty_ident); - } - syn::parse_quote!(#frame_support::traits::GetDefault) - }; - let default_max_values: syn::Type = syn::parse_quote!(#frame_support::traits::GetDefault); - - let set_result_query_type_parameter = |query_type: &mut syn::Type| -> syn::Result<()> { - if let Some(QueryKind::ResultQuery(error_path, _)) = storage_def.query_kind.as_ref() { - if let syn::Type::Path(syn::TypePath { - path: syn::Path { segments, .. }, - .. - }) = query_type - { - if let Some(seg) = segments.last_mut() { - if let syn::PathArguments::AngleBracketed( - syn::AngleBracketedGenericArguments { args, .. }, - ) = &mut seg.arguments - { - args.clear(); - args.push(syn::GenericArgument::Type(syn::parse_quote!(#error_path))); - } - } - } else { - let msg = format!( - "Invalid pallet::storage, unexpected type for query, expected ResultQuery \ + let frame_support = &def.frame_support; + let mut on_empty_struct_metadata = Vec::new(); + + for storage_def in def.storages.iter_mut() { + let item = &mut def.item.content.as_mut().expect("Checked by def").1[storage_def.index]; + + let typ_item = match item { + syn::Item::Type(t) => t, + _ => unreachable!("Checked by def"), + }; + + typ_item.attrs.push(syn::parse_quote!(#[allow(type_alias_bounds)])); + + let typ_path = match &mut *typ_item.ty { + syn::Type::Path(p) => p, + _ => unreachable!("Checked by def"), + }; + + let args = match &mut typ_path.path.segments[0].arguments { + syn::PathArguments::AngleBracketed(args) => args, + _ => unreachable!("Checked by def"), + }; + + let prefix_ident = prefix_ident(storage_def); + let type_use_gen = if def.config.has_instance { + quote::quote_spanned!(storage_def.attr_span => T, I) + } else { + quote::quote_spanned!(storage_def.attr_span => T) + }; + + let default_query_kind: syn::Type = + syn::parse_quote!(#frame_support::storage::types::OptionQuery); + let mut default_on_empty = |value_ty: syn::Type| -> syn::Type { + if let Some(QueryKind::ResultQuery(error_path, variant_name)) = + storage_def.query_kind.as_ref() + { + let on_empty_ident = + quote::format_ident!("__Frame_Internal_Get{}Result", storage_def.ident); + on_empty_struct_metadata.push(ResultOnEmptyStructMetadata { + name: on_empty_ident.clone(), + visibility: storage_def.vis.clone(), + value_ty, + error_path: error_path.clone(), + variant_name: variant_name.clone(), + span: storage_def.attr_span, + }); + return syn::parse_quote!(#on_empty_ident) + } + syn::parse_quote!(#frame_support::traits::GetDefault) + }; + let default_max_values: syn::Type = syn::parse_quote!(#frame_support::traits::GetDefault); + + let set_result_query_type_parameter = |query_type: &mut syn::Type| -> syn::Result<()> { + if let Some(QueryKind::ResultQuery(error_path, _)) = storage_def.query_kind.as_ref() { + if let syn::Type::Path(syn::TypePath { path: syn::Path { segments, .. }, .. }) = + query_type + { + if let Some(seg) = segments.last_mut() { + if let syn::PathArguments::AngleBracketed( + syn::AngleBracketedGenericArguments { args, .. }, + ) = &mut seg.arguments + { + args.clear(); + args.push(syn::GenericArgument::Type(syn::parse_quote!(#error_path))); + } + } + } else { + let msg = format!( + "Invalid pallet::storage, unexpected type for query, expected ResultQuery \ with 1 type parameter, found `{}`", - query_type.to_token_stream() - ); - return Err(syn::Error::new(query_type.span(), msg)); - } - } - Ok(()) - }; - - if let Some(named_generics) = storage_def.named_generics.clone() { - args.args.clear(); - args.args - .push(syn::parse_quote!( #prefix_ident<#type_use_gen> )); - match named_generics { - StorageGenerics::Value { - value, - query_kind, - on_empty, - } => { - args.args.push(syn::GenericArgument::Type(value.clone())); - let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone()); - set_result_query_type_parameter(&mut query_kind)?; - args.args.push(syn::GenericArgument::Type(query_kind)); - let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value)); - args.args.push(syn::GenericArgument::Type(on_empty)); - } - StorageGenerics::Map { - hasher, - key, - value, - query_kind, - on_empty, - max_values, - } - | StorageGenerics::CountedMap { - hasher, - key, - value, - query_kind, - on_empty, - max_values, - } => { - args.args.push(syn::GenericArgument::Type(hasher)); - args.args.push(syn::GenericArgument::Type(key)); - args.args.push(syn::GenericArgument::Type(value.clone())); - let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone()); - set_result_query_type_parameter(&mut query_kind)?; - args.args.push(syn::GenericArgument::Type(query_kind)); - let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value)); - args.args.push(syn::GenericArgument::Type(on_empty)); - let max_values = max_values.unwrap_or_else(|| default_max_values.clone()); - args.args.push(syn::GenericArgument::Type(max_values)); - } - StorageGenerics::DoubleMap { - hasher1, - key1, - hasher2, - key2, - value, - query_kind, - on_empty, - max_values, - } => { - args.args.push(syn::GenericArgument::Type(hasher1)); - args.args.push(syn::GenericArgument::Type(key1)); - args.args.push(syn::GenericArgument::Type(hasher2)); - args.args.push(syn::GenericArgument::Type(key2)); - args.args.push(syn::GenericArgument::Type(value.clone())); - let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone()); - set_result_query_type_parameter(&mut query_kind)?; - args.args.push(syn::GenericArgument::Type(query_kind)); - let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value)); - args.args.push(syn::GenericArgument::Type(on_empty)); - let max_values = max_values.unwrap_or_else(|| default_max_values.clone()); - args.args.push(syn::GenericArgument::Type(max_values)); - } - StorageGenerics::NMap { - keygen, - value, - query_kind, - on_empty, - max_values, - } - | StorageGenerics::CountedNMap { - keygen, - value, - query_kind, - on_empty, - max_values, - } => { - args.args.push(syn::GenericArgument::Type(keygen)); - args.args.push(syn::GenericArgument::Type(value.clone())); - let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone()); - set_result_query_type_parameter(&mut query_kind)?; - args.args.push(syn::GenericArgument::Type(query_kind)); - let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value)); - args.args.push(syn::GenericArgument::Type(on_empty)); - let max_values = max_values.unwrap_or_else(|| default_max_values.clone()); - args.args.push(syn::GenericArgument::Type(max_values)); - } - } - } else { - args.args[0] = syn::parse_quote!( #prefix_ident<#type_use_gen> ); - - let (value_idx, query_idx, on_empty_idx) = match storage_def.metadata { - Metadata::Value { .. } => (1, 2, 3), - Metadata::NMap { .. } | Metadata::CountedNMap { .. } => (2, 3, 4), - Metadata::Map { .. } | Metadata::CountedMap { .. } => (3, 4, 5), - Metadata::DoubleMap { .. } => (5, 6, 7), - }; - - if storage_def.use_default_hasher { - let hasher_indices: Vec = match storage_def.metadata { - Metadata::Map { .. } | Metadata::CountedMap { .. } => vec![1], - Metadata::DoubleMap { .. } => vec![1, 3], - _ => vec![], - }; - for hasher_idx in hasher_indices { - args.args[hasher_idx] = syn::GenericArgument::Type( - syn::parse_quote!(#frame_support::Blake2_128Concat), - ); - } - } - - if query_idx < args.args.len() { - if let syn::GenericArgument::Type(query_kind) = args.args.index_mut(query_idx) { - set_result_query_type_parameter(query_kind)?; - } - } else if let Some(QueryKind::ResultQuery(error_path, _)) = - storage_def.query_kind.as_ref() - { - args.args - .push(syn::GenericArgument::Type(syn::parse_quote!(#error_path))) - } - - // Here, we only need to check if OnEmpty is *not* specified, and if so, then we have to - // generate a default OnEmpty struct for it. - if on_empty_idx >= args.args.len() - && matches!( - storage_def.query_kind.as_ref(), - Some(QueryKind::ResultQuery(_, _)) - ) - { - let value_ty = match args.args[value_idx].clone() { - syn::GenericArgument::Type(ty) => ty, - _ => unreachable!(), - }; - let on_empty = default_on_empty(value_ty); - args.args.push(syn::GenericArgument::Type(on_empty)); - } - } - } - - Ok(on_empty_struct_metadata) + query_type.to_token_stream().to_string() + ); + return Err(syn::Error::new(query_type.span(), msg)) + } + } + Ok(()) + }; + + if let Some(named_generics) = storage_def.named_generics.clone() { + args.args.clear(); + args.args.push(syn::parse_quote!( #prefix_ident<#type_use_gen> )); + match named_generics { + StorageGenerics::Value { value, query_kind, on_empty } => { + args.args.push(syn::GenericArgument::Type(value.clone())); + let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone()); + set_result_query_type_parameter(&mut query_kind)?; + args.args.push(syn::GenericArgument::Type(query_kind)); + let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value)); + args.args.push(syn::GenericArgument::Type(on_empty)); + }, + StorageGenerics::Map { hasher, key, value, query_kind, on_empty, max_values } | + StorageGenerics::CountedMap { + hasher, + key, + value, + query_kind, + on_empty, + max_values, + } => { + args.args.push(syn::GenericArgument::Type(hasher)); + args.args.push(syn::GenericArgument::Type(key)); + args.args.push(syn::GenericArgument::Type(value.clone())); + let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone()); + set_result_query_type_parameter(&mut query_kind)?; + args.args.push(syn::GenericArgument::Type(query_kind)); + let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value)); + args.args.push(syn::GenericArgument::Type(on_empty)); + let max_values = max_values.unwrap_or_else(|| default_max_values.clone()); + args.args.push(syn::GenericArgument::Type(max_values)); + }, + StorageGenerics::DoubleMap { + hasher1, + key1, + hasher2, + key2, + value, + query_kind, + on_empty, + max_values, + } => { + args.args.push(syn::GenericArgument::Type(hasher1)); + args.args.push(syn::GenericArgument::Type(key1)); + args.args.push(syn::GenericArgument::Type(hasher2)); + args.args.push(syn::GenericArgument::Type(key2)); + args.args.push(syn::GenericArgument::Type(value.clone())); + let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone()); + set_result_query_type_parameter(&mut query_kind)?; + args.args.push(syn::GenericArgument::Type(query_kind)); + let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value)); + args.args.push(syn::GenericArgument::Type(on_empty)); + let max_values = max_values.unwrap_or_else(|| default_max_values.clone()); + args.args.push(syn::GenericArgument::Type(max_values)); + }, + StorageGenerics::NMap { keygen, value, query_kind, on_empty, max_values } | + StorageGenerics::CountedNMap { + keygen, + value, + query_kind, + on_empty, + max_values, + } => { + args.args.push(syn::GenericArgument::Type(keygen)); + args.args.push(syn::GenericArgument::Type(value.clone())); + let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone()); + set_result_query_type_parameter(&mut query_kind)?; + args.args.push(syn::GenericArgument::Type(query_kind)); + let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value)); + args.args.push(syn::GenericArgument::Type(on_empty)); + let max_values = max_values.unwrap_or_else(|| default_max_values.clone()); + args.args.push(syn::GenericArgument::Type(max_values)); + }, + } + } else { + args.args[0] = syn::parse_quote!( #prefix_ident<#type_use_gen> ); + + let (value_idx, query_idx, on_empty_idx) = match storage_def.metadata { + Metadata::Value { .. } => (1, 2, 3), + Metadata::NMap { .. } | Metadata::CountedNMap { .. } => (2, 3, 4), + Metadata::Map { .. } | Metadata::CountedMap { .. } => (3, 4, 5), + Metadata::DoubleMap { .. } => (5, 6, 7), + }; + + if storage_def.use_default_hasher { + let hasher_indices: Vec = match storage_def.metadata { + Metadata::Map { .. } | Metadata::CountedMap { .. } => vec![1], + Metadata::DoubleMap { .. } => vec![1, 3], + _ => vec![], + }; + for hasher_idx in hasher_indices { + args.args[hasher_idx] = syn::GenericArgument::Type( + syn::parse_quote!(#frame_support::Blake2_128Concat), + ); + } + } + + if query_idx < args.args.len() { + if let syn::GenericArgument::Type(query_kind) = args.args.index_mut(query_idx) { + set_result_query_type_parameter(query_kind)?; + } + } else if let Some(QueryKind::ResultQuery(error_path, _)) = + storage_def.query_kind.as_ref() + { + args.args.push(syn::GenericArgument::Type(syn::parse_quote!(#error_path))) + } + + // Here, we only need to check if OnEmpty is *not* specified, and if so, then we have to + // generate a default OnEmpty struct for it. + if on_empty_idx >= args.args.len() && + matches!(storage_def.query_kind.as_ref(), Some(QueryKind::ResultQuery(_, _))) + { + let value_ty = match args.args[value_idx].clone() { + syn::GenericArgument::Type(ty) => ty, + _ => unreachable!(), + }; + let on_empty = default_on_empty(value_ty); + args.args.push(syn::GenericArgument::Type(on_empty)); + } + } + } + + Ok(on_empty_struct_metadata) } fn augment_final_docs(def: &mut Def) { - // expand the docs with a new line showing the storage type (value, map, double map, etc), and - // the key/value type(s). - let mut push_string_literal = |doc_line: &str, storage: &mut StorageDef| { - let item = &mut def.item.content.as_mut().expect("Checked by def").1[storage.index]; - let typ_item = match item { - syn::Item::Type(t) => t, - _ => unreachable!("Checked by def"), - }; - typ_item.attrs.push(syn::parse_quote!(#[doc = ""])); - typ_item.attrs.push(syn::parse_quote!(#[doc = #doc_line])); - }; - def.storages - .iter_mut() - .for_each(|storage| match &storage.metadata { - Metadata::Value { value } => { - let doc_line = format!( - "Storage type is [`StorageValue`] with value type `{}`.", - value.to_token_stream() - ); - push_string_literal(&doc_line, storage); - } - Metadata::Map { key, value } => { - let doc_line = format!( - "Storage type is [`StorageMap`] with key type `{}` and value type `{}`.", - key.to_token_stream(), - value.to_token_stream() - ); - push_string_literal(&doc_line, storage); - } - Metadata::DoubleMap { key1, key2, value } => { - let doc_line = format!( + // expand the docs with a new line showing the storage type (value, map, double map, etc), and + // the key/value type(s). + let mut push_string_literal = |doc_line: &str, storage: &mut StorageDef| { + let item = &mut def.item.content.as_mut().expect("Checked by def").1[storage.index]; + let typ_item = match item { + syn::Item::Type(t) => t, + _ => unreachable!("Checked by def"), + }; + typ_item.attrs.push(syn::parse_quote!(#[doc = ""])); + typ_item.attrs.push(syn::parse_quote!(#[doc = #doc_line])); + }; + def.storages.iter_mut().for_each(|storage| match &storage.metadata { + Metadata::Value { value } => { + let doc_line = format!( + "Storage type is [`StorageValue`] with value type `{}`.", + value.to_token_stream() + ); + push_string_literal(&doc_line, storage); + }, + Metadata::Map { key, value } => { + let doc_line = format!( + "Storage type is [`StorageMap`] with key type `{}` and value type `{}`.", + key.to_token_stream(), + value.to_token_stream() + ); + push_string_literal(&doc_line, storage); + }, + Metadata::DoubleMap { key1, key2, value } => { + let doc_line = format!( "Storage type is [`StorageDoubleMap`] with key1 type {}, key2 type {} and value type {}.", key1.to_token_stream(), key2.to_token_stream(), value.to_token_stream() ); - push_string_literal(&doc_line, storage); - } - Metadata::NMap { keys, value, .. } => { - let doc_line = format!( - "Storage type is [`StorageNMap`] with keys type ({}) and value type {}.", - keys.iter() - .map(|k| k.to_token_stream().to_string()) - .collect::>() - .join(", "), - value.to_token_stream() - ); - push_string_literal(&doc_line, storage); - } - Metadata::CountedNMap { keys, value, .. } => { - let doc_line = format!( - "Storage type is [`CountedStorageNMap`] with keys type ({}) and value type {}.", - keys.iter() - .map(|k| k.to_token_stream().to_string()) - .collect::>() - .join(", "), - value.to_token_stream() - ); - push_string_literal(&doc_line, storage); - } - Metadata::CountedMap { key, value } => { - let doc_line = format!( - "Storage type is [`CountedStorageMap`] with key type {} and value type {}.", - key.to_token_stream(), - value.to_token_stream() - ); - push_string_literal(&doc_line, storage); - } - }); + push_string_literal(&doc_line, storage); + }, + Metadata::NMap { keys, value, .. } => { + let doc_line = format!( + "Storage type is [`StorageNMap`] with keys type ({}) and value type {}.", + keys.iter() + .map(|k| k.to_token_stream().to_string()) + .collect::>() + .join(", "), + value.to_token_stream() + ); + push_string_literal(&doc_line, storage); + }, + Metadata::CountedNMap { keys, value, .. } => { + let doc_line = format!( + "Storage type is [`CountedStorageNMap`] with keys type ({}) and value type {}.", + keys.iter() + .map(|k| k.to_token_stream().to_string()) + .collect::>() + .join(", "), + value.to_token_stream() + ); + push_string_literal(&doc_line, storage); + }, + Metadata::CountedMap { key, value } => { + let doc_line = format!( + "Storage type is [`CountedStorageMap`] with key type {} and value type {}.", + key.to_token_stream(), + value.to_token_stream() + ); + push_string_literal(&doc_line, storage); + }, + }); } /// @@ -418,29 +387,29 @@ fn augment_final_docs(def: &mut Def) { /// * Add `#[allow(type_alias_bounds)]` on storages type alias /// * generate metadatas pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { - let on_empty_struct_metadata = match process_generics(def) { - Ok(idents) => idents, - Err(e) => return e.into_compile_error(), - }; - - augment_final_docs(def); - - // Check for duplicate prefixes - let mut prefix_set = HashMap::new(); - let mut errors = def - .storages - .iter() - .filter_map(|storage_def| check_prefix_duplicates(storage_def, &mut prefix_set).err()); - if let Some(mut final_error) = errors.next() { - errors.for_each(|error| final_error.combine(error)); - return final_error.into_compile_error(); - } - - let frame_support = &def.frame_support; - let frame_system = &def.frame_system; - let pallet_ident = &def.pallet_struct.pallet; - - let entries_builder = def.storages.iter().map(|storage| { + let on_empty_struct_metadata = match process_generics(def) { + Ok(idents) => idents, + Err(e) => return e.into_compile_error(), + }; + + augment_final_docs(def); + + // Check for duplicate prefixes + let mut prefix_set = HashMap::new(); + let mut errors = def + .storages + .iter() + .filter_map(|storage_def| check_prefix_duplicates(storage_def, &mut prefix_set).err()); + if let Some(mut final_error) = errors.next() { + errors.for_each(|error| final_error.combine(error)); + return final_error.into_compile_error() + } + + let frame_support = &def.frame_support; + let frame_system = &def.frame_system; + let pallet_ident = &def.pallet_struct.pallet; + + let entries_builder = def.storages.iter().map(|storage| { let no_docs = vec![]; let docs = if cfg!(feature = "no-metadata-docs") { &no_docs } else { &storage.docs }; @@ -463,202 +432,202 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { ) }); - let getters = def.storages.iter().map(|storage| { - if let Some(getter) = &storage.getter { - let completed_where_clause = - super::merge_where_clauses(&[&storage.where_clause, &def.config.where_clause]); - - let ident = &storage.ident; - let gen = &def.type_use_generics(storage.attr_span); - let type_impl_gen = &def.type_impl_generics(storage.attr_span); - let type_use_gen = &def.type_use_generics(storage.attr_span); - let full_ident = quote::quote_spanned!(storage.attr_span => #ident<#gen> ); - - let cfg_attrs = &storage.cfg_attrs; - - // If the storage item is public, link it and otherwise just mention it. - // - // We can not just copy the docs from a non-public type as it may links to internal - // types which makes the compiler very unhappy :( - let getter_doc_line = if matches!(storage.vis, syn::Visibility::Public(_)) { - format!("An auto-generated getter for [`{}`].", storage.ident) - } else { - format!("An auto-generated getter for `{}`.", storage.ident) - }; - - match &storage.metadata { - Metadata::Value { value } => { - let query = match storage.query_kind.as_ref().expect("Checked by def") { - QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => - Option<#value> - ), - QueryKind::ResultQuery(error_path, _) => { - quote::quote_spanned!(storage.attr_span => - Result<#value, #error_path> - ) - } - QueryKind::ValueQuery => quote::quote!(#value), - }; - quote::quote_spanned!(storage.attr_span => - #(#cfg_attrs)* - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { - #[doc = #getter_doc_line] - pub fn #getter() -> #query { - < - #full_ident as #frame_support::storage::StorageValue<#value> - >::get() - } - } - ) - } - Metadata::Map { key, value } => { - let query = match storage.query_kind.as_ref().expect("Checked by def") { - QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => - Option<#value> - ), - QueryKind::ResultQuery(error_path, _) => { - quote::quote_spanned!(storage.attr_span => - Result<#value, #error_path> - ) - } - QueryKind::ValueQuery => quote::quote!(#value), - }; - quote::quote_spanned!(storage.attr_span => - #(#cfg_attrs)* - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { - #[doc = #getter_doc_line] - pub fn #getter(k: KArg) -> #query where - KArg: #frame_support::__private::codec::EncodeLike<#key>, - { - < - #full_ident as #frame_support::storage::StorageMap<#key, #value> - >::get(k) - } - } - ) - } - Metadata::CountedMap { key, value } => { - let query = match storage.query_kind.as_ref().expect("Checked by def") { - QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => - Option<#value> - ), - QueryKind::ResultQuery(error_path, _) => { - quote::quote_spanned!(storage.attr_span => - Result<#value, #error_path> - ) - } - QueryKind::ValueQuery => quote::quote!(#value), - }; - quote::quote_spanned!(storage.attr_span => - #(#cfg_attrs)* - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { - #[doc = #getter_doc_line] - pub fn #getter(k: KArg) -> #query where - KArg: #frame_support::__private::codec::EncodeLike<#key>, - { - // NOTE: we can't use any trait here because CountedStorageMap - // doesn't implement any. - <#full_ident>::get(k) - } - } - ) - } - Metadata::DoubleMap { key1, key2, value } => { - let query = match storage.query_kind.as_ref().expect("Checked by def") { - QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => - Option<#value> - ), - QueryKind::ResultQuery(error_path, _) => { - quote::quote_spanned!(storage.attr_span => - Result<#value, #error_path> - ) - } - QueryKind::ValueQuery => quote::quote!(#value), - }; - quote::quote_spanned!(storage.attr_span => - #(#cfg_attrs)* - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { - #[doc = #getter_doc_line] - pub fn #getter(k1: KArg1, k2: KArg2) -> #query where - KArg1: #frame_support::__private::codec::EncodeLike<#key1>, - KArg2: #frame_support::__private::codec::EncodeLike<#key2>, - { - < - #full_ident as - #frame_support::storage::StorageDoubleMap<#key1, #key2, #value> - >::get(k1, k2) - } - } - ) - } - Metadata::NMap { keygen, value, .. } => { - let query = match storage.query_kind.as_ref().expect("Checked by def") { - QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => - Option<#value> - ), - QueryKind::ResultQuery(error_path, _) => { - quote::quote_spanned!(storage.attr_span => - Result<#value, #error_path> - ) - } - QueryKind::ValueQuery => quote::quote!(#value), - }; - quote::quote_spanned!(storage.attr_span => - #(#cfg_attrs)* - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { - #[doc = #getter_doc_line] - pub fn #getter(key: KArg) -> #query - where - KArg: #frame_support::storage::types::EncodeLikeTuple< - <#keygen as #frame_support::storage::types::KeyGenerator>::KArg - > - + #frame_support::storage::types::TupleToEncodedIter, - { - < - #full_ident as - #frame_support::storage::StorageNMap<#keygen, #value> - >::get(key) - } - } - ) - } - Metadata::CountedNMap { keygen, value, .. } => { - let query = match storage.query_kind.as_ref().expect("Checked by def") { - QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => - Option<#value> - ), - QueryKind::ResultQuery(error_path, _) => { - quote::quote_spanned!(storage.attr_span => - Result<#value, #error_path> - ) - } - QueryKind::ValueQuery => quote::quote!(#value), - }; - quote::quote_spanned!(storage.attr_span => - #(#cfg_attrs)* - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { - #[doc = #getter_doc_line] - pub fn #getter(key: KArg) -> #query - where - KArg: #frame_support::storage::types::EncodeLikeTuple< - <#keygen as #frame_support::storage::types::KeyGenerator>::KArg - > - + #frame_support::storage::types::TupleToEncodedIter, - { - // NOTE: we can't use any trait here because CountedStorageNMap - // doesn't implement any. - <#full_ident>::get(key) - } - } - ) - } - } - } else { - Default::default() - } - }); - - let prefix_structs = def.storages.iter().map(|storage_def| { + let getters = def.storages.iter().map(|storage| { + if let Some(getter) = &storage.getter { + let completed_where_clause = + super::merge_where_clauses(&[&storage.where_clause, &def.config.where_clause]); + + let ident = &storage.ident; + let gen = &def.type_use_generics(storage.attr_span); + let type_impl_gen = &def.type_impl_generics(storage.attr_span); + let type_use_gen = &def.type_use_generics(storage.attr_span); + let full_ident = quote::quote_spanned!(storage.attr_span => #ident<#gen> ); + + let cfg_attrs = &storage.cfg_attrs; + + // If the storage item is public, link it and otherwise just mention it. + // + // We can not just copy the docs from a non-public type as it may links to internal + // types which makes the compiler very unhappy :( + let getter_doc_line = if matches!(storage.vis, syn::Visibility::Public(_)) { + format!("An auto-generated getter for [`{}`].", storage.ident) + } else { + format!("An auto-generated getter for `{}`.", storage.ident) + }; + + match &storage.metadata { + Metadata::Value { value } => { + let query = match storage.query_kind.as_ref().expect("Checked by def") { + QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => + Option<#value> + ), + QueryKind::ResultQuery(error_path, _) => { + quote::quote_spanned!(storage.attr_span => + Result<#value, #error_path> + ) + }, + QueryKind::ValueQuery => quote::quote!(#value), + }; + quote::quote_spanned!(storage.attr_span => + #(#cfg_attrs)* + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { + #[doc = #getter_doc_line] + pub fn #getter() -> #query { + < + #full_ident as #frame_support::storage::StorageValue<#value> + >::get() + } + } + ) + }, + Metadata::Map { key, value } => { + let query = match storage.query_kind.as_ref().expect("Checked by def") { + QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => + Option<#value> + ), + QueryKind::ResultQuery(error_path, _) => { + quote::quote_spanned!(storage.attr_span => + Result<#value, #error_path> + ) + }, + QueryKind::ValueQuery => quote::quote!(#value), + }; + quote::quote_spanned!(storage.attr_span => + #(#cfg_attrs)* + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { + #[doc = #getter_doc_line] + pub fn #getter(k: KArg) -> #query where + KArg: #frame_support::__private::codec::EncodeLike<#key>, + { + < + #full_ident as #frame_support::storage::StorageMap<#key, #value> + >::get(k) + } + } + ) + }, + Metadata::CountedMap { key, value } => { + let query = match storage.query_kind.as_ref().expect("Checked by def") { + QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => + Option<#value> + ), + QueryKind::ResultQuery(error_path, _) => { + quote::quote_spanned!(storage.attr_span => + Result<#value, #error_path> + ) + }, + QueryKind::ValueQuery => quote::quote!(#value), + }; + quote::quote_spanned!(storage.attr_span => + #(#cfg_attrs)* + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { + #[doc = #getter_doc_line] + pub fn #getter(k: KArg) -> #query where + KArg: #frame_support::__private::codec::EncodeLike<#key>, + { + // NOTE: we can't use any trait here because CountedStorageMap + // doesn't implement any. + <#full_ident>::get(k) + } + } + ) + }, + Metadata::DoubleMap { key1, key2, value } => { + let query = match storage.query_kind.as_ref().expect("Checked by def") { + QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => + Option<#value> + ), + QueryKind::ResultQuery(error_path, _) => { + quote::quote_spanned!(storage.attr_span => + Result<#value, #error_path> + ) + }, + QueryKind::ValueQuery => quote::quote!(#value), + }; + quote::quote_spanned!(storage.attr_span => + #(#cfg_attrs)* + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { + #[doc = #getter_doc_line] + pub fn #getter(k1: KArg1, k2: KArg2) -> #query where + KArg1: #frame_support::__private::codec::EncodeLike<#key1>, + KArg2: #frame_support::__private::codec::EncodeLike<#key2>, + { + < + #full_ident as + #frame_support::storage::StorageDoubleMap<#key1, #key2, #value> + >::get(k1, k2) + } + } + ) + }, + Metadata::NMap { keygen, value, .. } => { + let query = match storage.query_kind.as_ref().expect("Checked by def") { + QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => + Option<#value> + ), + QueryKind::ResultQuery(error_path, _) => { + quote::quote_spanned!(storage.attr_span => + Result<#value, #error_path> + ) + }, + QueryKind::ValueQuery => quote::quote!(#value), + }; + quote::quote_spanned!(storage.attr_span => + #(#cfg_attrs)* + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { + #[doc = #getter_doc_line] + pub fn #getter(key: KArg) -> #query + where + KArg: #frame_support::storage::types::EncodeLikeTuple< + <#keygen as #frame_support::storage::types::KeyGenerator>::KArg + > + + #frame_support::storage::types::TupleToEncodedIter, + { + < + #full_ident as + #frame_support::storage::StorageNMap<#keygen, #value> + >::get(key) + } + } + ) + }, + Metadata::CountedNMap { keygen, value, .. } => { + let query = match storage.query_kind.as_ref().expect("Checked by def") { + QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => + Option<#value> + ), + QueryKind::ResultQuery(error_path, _) => { + quote::quote_spanned!(storage.attr_span => + Result<#value, #error_path> + ) + }, + QueryKind::ValueQuery => quote::quote!(#value), + }; + quote::quote_spanned!(storage.attr_span => + #(#cfg_attrs)* + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { + #[doc = #getter_doc_line] + pub fn #getter(key: KArg) -> #query + where + KArg: #frame_support::storage::types::EncodeLikeTuple< + <#keygen as #frame_support::storage::types::KeyGenerator>::KArg + > + + #frame_support::storage::types::TupleToEncodedIter, + { + // NOTE: we can't use any trait here because CountedStorageNMap + // doesn't implement any. + <#full_ident>::get(key) + } + } + ) + }, + } + } else { + Default::default() + } + }); + + let prefix_structs = def.storages.iter().map(|storage_def| { let type_impl_gen = &def.type_impl_generics(storage_def.attr_span); let type_use_gen = &def.type_use_generics(storage_def.attr_span); let prefix_struct_ident = prefix_ident(storage_def); @@ -798,159 +767,153 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { ) }); - let on_empty_structs = on_empty_struct_metadata.into_iter().map(|metadata| { - use crate::pallet::parse::GenericKind; - use syn::{GenericArgument, Path, PathArguments, PathSegment, Type, TypePath}; - - let ResultOnEmptyStructMetadata { - name, - visibility, - value_ty, - error_path, - variant_name, - span, - } = metadata; - - let generic_kind = match error_path.segments.last() { - Some(PathSegment { - arguments: PathArguments::AngleBracketed(args), - .. - }) => { - let (has_config, has_instance) = - args.args - .iter() - .fold( - (false, false), - |(has_config, has_instance), arg| match arg { - GenericArgument::Type(Type::Path(TypePath { - path: Path { segments, .. }, - .. - })) => { - let maybe_config = - segments.first().map_or(false, |seg| seg.ident == "T"); - let maybe_instance = - segments.first().map_or(false, |seg| seg.ident == "I"); - - (has_config || maybe_config, has_instance || maybe_instance) - } - _ => (has_config, has_instance), - }, - ); - GenericKind::from_gens(has_config, has_instance).unwrap_or(GenericKind::None) - } - _ => GenericKind::None, - }; - let type_impl_gen = generic_kind.type_impl_gen(proc_macro2::Span::call_site()); - let config_where_clause = &def.config.where_clause; - - quote::quote_spanned!(span => - #[doc(hidden)] - #[allow(non_camel_case_types)] - #visibility struct #name; - - impl<#type_impl_gen> #frame_support::traits::Get> - for #name - #config_where_clause - { - fn get() -> Result<#value_ty, #error_path> { - Err(<#error_path>::#variant_name) - } - } - ) - }); - - // aggregated where clause of all storage types and the whole pallet. - let mut where_clauses = vec![&def.config.where_clause]; - where_clauses.extend(def.storages.iter().map(|storage| &storage.where_clause)); - let completed_where_clause = super::merge_where_clauses(&where_clauses); - let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site()); - let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site()); - - let try_decode_entire_state = { - let mut storage_names = def - .storages - .iter() - .filter_map(|storage| { - // A little hacky; don't generate for cfg gated storages to not get compile errors - // when building "frame-feature-testing" gated storages in the "frame-support-test" - // crate. - if storage.try_decode && storage.cfg_attrs.is_empty() { - let ident = &storage.ident; - let gen = &def.type_use_generics(storage.attr_span); - Some(quote::quote_spanned!(storage.attr_span => #ident<#gen> )) - } else { - None - } - }) - .collect::>(); - storage_names.sort_by_cached_key(|ident| ident.to_string()); - - quote::quote!( - #[cfg(feature = "try-runtime")] - impl<#type_impl_gen> #frame_support::traits::TryDecodeEntireStorage - for #pallet_ident<#type_use_gen> #completed_where_clause - { - fn try_decode_entire_state() -> Result> { - let pallet_name = <::PalletInfo as frame_support::traits::PalletInfo> - ::name::<#pallet_ident<#type_use_gen>>() - .expect("Every active pallet has a name in the runtime; qed"); - - #frame_support::__private::log::debug!(target: "runtime::try-decode-state", "trying to decode pallet: {pallet_name}"); - - // NOTE: for now, we have to exclude storage items that are feature gated. - let mut errors = #frame_support::__private::sp_std::vec::Vec::new(); - let mut decoded = 0usize; - - #( - #frame_support::__private::log::debug!(target: "runtime::try-decode-state", "trying to decode storage: \ - {pallet_name}::{}", stringify!(#storage_names)); - - match <#storage_names as #frame_support::traits::TryDecodeEntireStorage>::try_decode_entire_state() { - Ok(count) => { - decoded += count; - }, - Err(err) => { - errors.extend(err); - }, - } - )* - - if errors.is_empty() { - Ok(decoded) - } else { - Err(errors) - } - } - } - ) - }; - - quote::quote!( - impl<#type_impl_gen> #pallet_ident<#type_use_gen> - #completed_where_clause - { - #[doc(hidden)] - pub fn storage_metadata() -> #frame_support::__private::metadata_ir::PalletStorageMetadataIR { - #frame_support::__private::metadata_ir::PalletStorageMetadataIR { - prefix: < - ::PalletInfo as - #frame_support::traits::PalletInfo - >::name::<#pallet_ident<#type_use_gen>>() - .expect("No name found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`."), - entries: { - #[allow(unused_mut)] - let mut entries = #frame_support::__private::sp_std::vec![]; - #( #entries_builder )* - entries - }, - } - } - } - - #( #getters )* - #( #prefix_structs )* - #( #on_empty_structs )* - - #try_decode_entire_state - ) + let on_empty_structs = on_empty_struct_metadata.into_iter().map(|metadata| { + use crate::pallet::parse::GenericKind; + use syn::{GenericArgument, Path, PathArguments, PathSegment, Type, TypePath}; + + let ResultOnEmptyStructMetadata { + name, + visibility, + value_ty, + error_path, + variant_name, + span, + } = metadata; + + let generic_kind = match error_path.segments.last() { + Some(PathSegment { arguments: PathArguments::AngleBracketed(args), .. }) => { + let (has_config, has_instance) = + args.args.iter().fold((false, false), |(has_config, has_instance), arg| { + match arg { + GenericArgument::Type(Type::Path(TypePath { + path: Path { segments, .. }, + .. + })) => { + let maybe_config = + segments.first().map_or(false, |seg| seg.ident == "T"); + let maybe_instance = + segments.first().map_or(false, |seg| seg.ident == "I"); + + (has_config || maybe_config, has_instance || maybe_instance) + }, + _ => (has_config, has_instance), + } + }); + GenericKind::from_gens(has_config, has_instance).unwrap_or(GenericKind::None) + }, + _ => GenericKind::None, + }; + let type_impl_gen = generic_kind.type_impl_gen(proc_macro2::Span::call_site()); + let config_where_clause = &def.config.where_clause; + + quote::quote_spanned!(span => + #[doc(hidden)] + #[allow(non_camel_case_types)] + #visibility struct #name; + + impl<#type_impl_gen> #frame_support::traits::Get> + for #name + #config_where_clause + { + fn get() -> Result<#value_ty, #error_path> { + Err(<#error_path>::#variant_name) + } + } + ) + }); + + // aggregated where clause of all storage types and the whole pallet. + let mut where_clauses = vec![&def.config.where_clause]; + where_clauses.extend(def.storages.iter().map(|storage| &storage.where_clause)); + let completed_where_clause = super::merge_where_clauses(&where_clauses); + let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site()); + let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site()); + + let try_decode_entire_state = { + let mut storage_names = def + .storages + .iter() + .filter_map(|storage| { + // A little hacky; don't generate for cfg gated storages to not get compile errors + // when building "frame-feature-testing" gated storages in the "frame-support-test" + // crate. + if storage.try_decode && storage.cfg_attrs.is_empty() { + let ident = &storage.ident; + let gen = &def.type_use_generics(storage.attr_span); + Some(quote::quote_spanned!(storage.attr_span => #ident<#gen> )) + } else { + None + } + }) + .collect::>(); + storage_names.sort_by_cached_key(|ident| ident.to_string()); + + quote::quote!( + #[cfg(feature = "try-runtime")] + impl<#type_impl_gen> #frame_support::traits::TryDecodeEntireStorage + for #pallet_ident<#type_use_gen> #completed_where_clause + { + fn try_decode_entire_state() -> Result> { + let pallet_name = <::PalletInfo as frame_support::traits::PalletInfo> + ::name::<#pallet_ident<#type_use_gen>>() + .expect("Every active pallet has a name in the runtime; qed"); + + #frame_support::__private::log::debug!(target: "runtime::try-decode-state", "trying to decode pallet: {pallet_name}"); + + // NOTE: for now, we have to exclude storage items that are feature gated. + let mut errors = #frame_support::__private::sp_std::vec::Vec::new(); + let mut decoded = 0usize; + + #( + #frame_support::__private::log::debug!(target: "runtime::try-decode-state", "trying to decode storage: \ + {pallet_name}::{}", stringify!(#storage_names)); + + match <#storage_names as #frame_support::traits::TryDecodeEntireStorage>::try_decode_entire_state() { + Ok(count) => { + decoded += count; + }, + Err(err) => { + errors.extend(err); + }, + } + )* + + if errors.is_empty() { + Ok(decoded) + } else { + Err(errors) + } + } + } + ) + }; + + quote::quote!( + impl<#type_impl_gen> #pallet_ident<#type_use_gen> + #completed_where_clause + { + #[doc(hidden)] + pub fn storage_metadata() -> #frame_support::__private::metadata_ir::PalletStorageMetadataIR { + #frame_support::__private::metadata_ir::PalletStorageMetadataIR { + prefix: < + ::PalletInfo as + #frame_support::traits::PalletInfo + >::name::<#pallet_ident<#type_use_gen>>() + .expect("No name found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`."), + entries: { + #[allow(unused_mut)] + let mut entries = #frame_support::__private::sp_std::vec![]; + #( #entries_builder )* + entries + }, + } + } + } + + #( #getters )* + #( #prefix_structs )* + #( #on_empty_structs )* + + #try_decode_entire_state + ) } diff --git a/support/procedural-fork/src/pallet/expand/tasks.rs b/support/procedural-fork/src/pallet/expand/tasks.rs index 2db53b7d9..6697e5c82 100644 --- a/support/procedural-fork/src/pallet/expand/tasks.rs +++ b/support/procedural-fork/src/pallet/expand/tasks.rs @@ -27,145 +27,141 @@ use quote::{format_ident, quote, ToTokens}; use syn::{parse_quote, spanned::Spanned, ItemEnum, ItemImpl}; impl TaskEnumDef { - /// Since we optionally allow users to manually specify a `#[pallet::task_enum]`, in the - /// event they _don't_ specify one (which is actually the most common behavior) we have to - /// generate one based on the existing [`TasksDef`]. This method performs that generation. - pub fn generate( - tasks: &TasksDef, - type_decl_bounded_generics: TokenStream2, - type_use_generics: TokenStream2, - ) -> Self { - let variants = if tasks.tasks_attr.is_some() { - tasks - .tasks - .iter() - .map(|task| { - let ident = &task.item.sig.ident; - let ident = - format_ident!("{}", ident.to_string().to_class_case(), span = ident.span()); + /// Since we optionally allow users to manually specify a `#[pallet::task_enum]`, in the + /// event they _don't_ specify one (which is actually the most common behavior) we have to + /// generate one based on the existing [`TasksDef`]. This method performs that generation. + pub fn generate( + tasks: &TasksDef, + type_decl_bounded_generics: TokenStream2, + type_use_generics: TokenStream2, + ) -> Self { + let variants = if tasks.tasks_attr.is_some() { + tasks + .tasks + .iter() + .map(|task| { + let ident = &task.item.sig.ident; + let ident = + format_ident!("{}", ident.to_string().to_class_case(), span = ident.span()); - let args = task.item.sig.inputs.iter().collect::>(); + let args = task.item.sig.inputs.iter().collect::>(); - if args.is_empty() { - quote!(#ident) - } else { - quote!(#ident { - #(#args),* - }) - } - }) - .collect::>() - } else { - Vec::new() - }; - let mut task_enum_def: TaskEnumDef = parse_quote! { - /// Auto-generated enum that encapsulates all tasks defined by this pallet. - /// - /// Conceptually similar to the [`Call`] enum, but for tasks. This is only - /// generated if there are tasks present in this pallet. - #[pallet::task_enum] - pub enum Task<#type_decl_bounded_generics> { - #( - #variants, - )* - } - }; - task_enum_def.type_use_generics = type_use_generics; - task_enum_def - } + if args.is_empty() { + quote!(#ident) + } else { + quote!(#ident { + #(#args),* + }) + } + }) + .collect::>() + } else { + Vec::new() + }; + let mut task_enum_def: TaskEnumDef = parse_quote! { + /// Auto-generated enum that encapsulates all tasks defined by this pallet. + /// + /// Conceptually similar to the [`Call`] enum, but for tasks. This is only + /// generated if there are tasks present in this pallet. + #[pallet::task_enum] + pub enum Task<#type_decl_bounded_generics> { + #( + #variants, + )* + } + }; + task_enum_def.type_use_generics = type_use_generics; + task_enum_def + } } impl ToTokens for TaskEnumDef { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let item_enum = &self.item_enum; - let ident = &item_enum.ident; - let vis = &item_enum.vis; - let attrs = &item_enum.attrs; - let generics = &item_enum.generics; - let variants = &item_enum.variants; - let scrate = &self.scrate; - let type_use_generics = &self.type_use_generics; - if self.attr.is_some() { - // `item_enum` is short-hand / generated enum - tokens.extend(quote! { - #(#attrs)* - #[derive( - #scrate::CloneNoBound, - #scrate::EqNoBound, - #scrate::PartialEqNoBound, - #scrate::pallet_prelude::Encode, - #scrate::pallet_prelude::Decode, - #scrate::pallet_prelude::TypeInfo, - )] - #[codec(encode_bound())] - #[codec(decode_bound())] - #[scale_info(skip_type_params(#type_use_generics))] - #vis enum #ident #generics { - #variants - #[doc(hidden)] - #[codec(skip)] - __Ignore(core::marker::PhantomData, #scrate::Never), - } + fn to_tokens(&self, tokens: &mut TokenStream2) { + let item_enum = &self.item_enum; + let ident = &item_enum.ident; + let vis = &item_enum.vis; + let attrs = &item_enum.attrs; + let generics = &item_enum.generics; + let variants = &item_enum.variants; + let scrate = &self.scrate; + let type_use_generics = &self.type_use_generics; + if self.attr.is_some() { + // `item_enum` is short-hand / generated enum + tokens.extend(quote! { + #(#attrs)* + #[derive( + #scrate::CloneNoBound, + #scrate::EqNoBound, + #scrate::PartialEqNoBound, + #scrate::pallet_prelude::Encode, + #scrate::pallet_prelude::Decode, + #scrate::pallet_prelude::TypeInfo, + )] + #[codec(encode_bound())] + #[codec(decode_bound())] + #[scale_info(skip_type_params(#type_use_generics))] + #vis enum #ident #generics { + #variants + #[doc(hidden)] + #[codec(skip)] + __Ignore(core::marker::PhantomData, #scrate::Never), + } - impl core::fmt::Debug for #ident<#type_use_generics> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct(stringify!(#ident)).field("value", self).finish() - } - } - }); - } else { - // `item_enum` is a manually specified enum (no attribute) - tokens.extend(item_enum.to_token_stream()); - } - } + impl core::fmt::Debug for #ident<#type_use_generics> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct(stringify!(#ident)).field("value", self).finish() + } + } + }); + } else { + // `item_enum` is a manually specified enum (no attribute) + tokens.extend(item_enum.to_token_stream()); + } + } } /// Represents an already-expanded [`TasksDef`]. #[derive(Parse)] pub struct ExpandedTasksDef { - pub task_item_impl: ItemImpl, - pub task_trait_impl: ItemImpl, + pub task_item_impl: ItemImpl, + pub task_trait_impl: ItemImpl, } impl ToTokens for TasksDef { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let scrate = &self.scrate; - let enum_ident = syn::Ident::new("Task", self.enum_ident.span()); - let enum_arguments = &self.enum_arguments; - let enum_use = quote!(#enum_ident #enum_arguments); + fn to_tokens(&self, tokens: &mut TokenStream2) { + let scrate = &self.scrate; + let enum_ident = syn::Ident::new("Task", self.enum_ident.span()); + let enum_arguments = &self.enum_arguments; + let enum_use = quote!(#enum_ident #enum_arguments); - let task_fn_idents = self - .tasks - .iter() - .map(|task| { - format_ident!( - "{}", - &task.item.sig.ident.to_string().to_class_case(), - span = task.item.sig.ident.span() - ) - }) - .collect::>(); - let task_indices = self.tasks.iter().map(|task| &task.index_attr.meta.index); - let task_conditions = self.tasks.iter().map(|task| &task.condition_attr.meta.expr); - let task_weights = self.tasks.iter().map(|task| &task.weight_attr.meta.expr); - let task_iters = self.tasks.iter().map(|task| &task.list_attr.meta.expr); + let task_fn_idents = self + .tasks + .iter() + .map(|task| { + format_ident!( + "{}", + &task.item.sig.ident.to_string().to_class_case(), + span = task.item.sig.ident.span() + ) + }) + .collect::>(); + let task_indices = self.tasks.iter().map(|task| &task.index_attr.meta.index); + let task_conditions = self.tasks.iter().map(|task| &task.condition_attr.meta.expr); + let task_weights = self.tasks.iter().map(|task| &task.weight_attr.meta.expr); + let task_iters = self.tasks.iter().map(|task| &task.list_attr.meta.expr); - let task_fn_impls = self.tasks.iter().map(|task| { - let mut task_fn_impl = task.item.clone(); - task_fn_impl.attrs = vec![]; - task_fn_impl - }); + let task_fn_impls = self.tasks.iter().map(|task| { + let mut task_fn_impl = task.item.clone(); + task_fn_impl.attrs = vec![]; + task_fn_impl + }); - let task_fn_names = self.tasks.iter().map(|task| &task.item.sig.ident); - let task_arg_names = self - .tasks - .iter() - .map(|task| &task.arg_names) - .collect::>(); + let task_fn_names = self.tasks.iter().map(|task| &task.item.sig.ident); + let task_arg_names = self.tasks.iter().map(|task| &task.arg_names).collect::>(); - let sp_std = quote!(#scrate::__private::sp_std); - let impl_generics = &self.item_impl.generics; - tokens.extend(quote! { + let sp_std = quote!(#scrate::__private::sp_std); + let impl_generics = &self.item_impl.generics; + tokens.extend(quote! { impl #impl_generics #enum_use { #(#task_fn_impls)* @@ -216,66 +212,56 @@ impl ToTokens for TasksDef { } } }); - } + } } /// Expands the [`TasksDef`] in the enclosing [`Def`], if present, and returns its tokens. /// /// This modifies the underlying [`Def`] in addition to returning any tokens that were added. pub fn expand_tasks_impl(def: &mut Def) -> TokenStream2 { - let Some(tasks) = &mut def.tasks else { - return quote!(); - }; - let ExpandedTasksDef { - task_item_impl, - task_trait_impl, - } = parse_quote!(#tasks); - quote! { - #task_item_impl - #task_trait_impl - } + let Some(tasks) = &mut def.tasks else { return quote!() }; + let ExpandedTasksDef { task_item_impl, task_trait_impl } = parse_quote!(#tasks); + quote! { + #task_item_impl + #task_trait_impl + } } /// Represents a fully-expanded [`TaskEnumDef`]. #[derive(Parse)] pub struct ExpandedTaskEnum { - pub item_enum: ItemEnum, - pub debug_impl: ItemImpl, + pub item_enum: ItemEnum, + pub debug_impl: ItemImpl, } /// Modifies a [`Def`] to expand the underlying [`TaskEnumDef`] if present, and also returns /// its tokens. A blank [`TokenStream2`] is returned if no [`TaskEnumDef`] has been generated /// or defined. pub fn expand_task_enum(def: &mut Def) -> TokenStream2 { - let Some(task_enum) = &mut def.task_enum else { - return quote!(); - }; - let ExpandedTaskEnum { - item_enum, - debug_impl, - } = parse_quote!(#task_enum); - quote! { - #item_enum - #debug_impl - } + let Some(task_enum) = &mut def.task_enum else { return quote!() }; + let ExpandedTaskEnum { item_enum, debug_impl } = parse_quote!(#task_enum); + quote! { + #item_enum + #debug_impl + } } /// Modifies a [`Def`] to expand the underlying [`TasksDef`] and also generate a /// [`TaskEnumDef`] if applicable. The tokens for these items are returned if they are created. pub fn expand_tasks(def: &mut Def) -> TokenStream2 { - if let Some(tasks_def) = &def.tasks { - if def.task_enum.is_none() { - def.task_enum = Some(TaskEnumDef::generate( - tasks_def, - def.type_decl_bounded_generics(tasks_def.item_impl.span()), - def.type_use_generics(tasks_def.item_impl.span()), - )); - } - } - let tasks_extra_output = expand_tasks_impl(def); - let task_enum_extra_output = expand_task_enum(def); - quote! { - #tasks_extra_output - #task_enum_extra_output - } + if let Some(tasks_def) = &def.tasks { + if def.task_enum.is_none() { + def.task_enum = Some(TaskEnumDef::generate( + &tasks_def, + def.type_decl_bounded_generics(tasks_def.item_impl.span()), + def.type_use_generics(tasks_def.item_impl.span()), + )); + } + } + let tasks_extra_output = expand_tasks_impl(def); + let task_enum_extra_output = expand_task_enum(def); + quote! { + #tasks_extra_output + #task_enum_extra_output + } } diff --git a/support/procedural-fork/src/pallet/expand/tt_default_parts.rs b/support/procedural-fork/src/pallet/expand/tt_default_parts.rs index 57b78339a..99364aaa9 100644 --- a/support/procedural-fork/src/pallet/expand/tt_default_parts.rs +++ b/support/procedural-fork/src/pallet/expand/tt_default_parts.rs @@ -16,211 +16,201 @@ // limitations under the License. use crate::{ - pallet::{CompositeKeyword, Def}, - COUNTER, + pallet::{CompositeKeyword, Def}, + COUNTER, }; use syn::spanned::Spanned; /// Generate the `tt_default_parts` macro. pub fn expand_tt_default_parts(def: &mut Def) -> proc_macro2::TokenStream { - let count = COUNTER.with(|counter| counter.borrow_mut().inc()); - let default_parts_unique_id = - syn::Ident::new(&format!("__tt_default_parts_{}", count), def.item.span()); - let extra_parts_unique_id = - syn::Ident::new(&format!("__tt_extra_parts_{}", count), def.item.span()); - let default_parts_unique_id_v2 = - syn::Ident::new(&format!("__tt_default_parts_v2_{}", count), def.item.span()); - - let call_part = def.call.as_ref().map(|_| quote::quote!(Call,)); - - let task_part = def.task_enum.as_ref().map(|_| quote::quote!(Task,)); - - let storage_part = (!def.storages.is_empty()).then(|| quote::quote!(Storage,)); - - let event_part = def.event.as_ref().map(|event| { - let gen = event.gen_kind.is_generic().then(|| quote::quote!( )); - quote::quote!( Event #gen , ) - }); - - let error_part = def.error.as_ref().map(|_| quote::quote!(Error,)); - - let origin_part = def.origin.as_ref().map(|origin| { - let gen = origin.is_generic.then(|| quote::quote!( )); - quote::quote!( Origin #gen , ) - }); - - let config_part = def.genesis_config.as_ref().map(|genesis_config| { - let gen = genesis_config - .gen_kind - .is_generic() - .then(|| quote::quote!( )); - quote::quote!( Config #gen , ) - }); - - let inherent_part = def.inherent.as_ref().map(|_| quote::quote!(Inherent,)); - - let validate_unsigned_part = def - .validate_unsigned - .as_ref() - .map(|_| quote::quote!(ValidateUnsigned,)); - - let freeze_reason_part = def - .composites - .iter() - .any(|c| matches!(c.composite_keyword, CompositeKeyword::FreezeReason(_))) - .then_some(quote::quote!(FreezeReason,)); - - let hold_reason_part = def - .composites - .iter() - .any(|c| matches!(c.composite_keyword, CompositeKeyword::HoldReason(_))) - .then_some(quote::quote!(HoldReason,)); - - let lock_id_part = def - .composites - .iter() - .any(|c| matches!(c.composite_keyword, CompositeKeyword::LockId(_))) - .then_some(quote::quote!(LockId,)); - - let slash_reason_part = def - .composites - .iter() - .any(|c| matches!(c.composite_keyword, CompositeKeyword::SlashReason(_))) - .then_some(quote::quote!(SlashReason,)); - - let call_part_v2 = def.call.as_ref().map(|_| quote::quote!(+ Call)); - - let task_part_v2 = def.task_enum.as_ref().map(|_| quote::quote!(+ Task)); - - let storage_part_v2 = (!def.storages.is_empty()).then(|| quote::quote!(+ Storage)); - - let event_part_v2 = def.event.as_ref().map(|event| { - let gen = event.gen_kind.is_generic().then(|| quote::quote!()); - quote::quote!(+ Event #gen) - }); - - let error_part_v2 = def.error.as_ref().map(|_| quote::quote!(+ Error)); - - let origin_part_v2 = def.origin.as_ref().map(|origin| { - let gen = origin.is_generic.then(|| quote::quote!()); - quote::quote!(+ Origin #gen) - }); - - let config_part_v2 = def.genesis_config.as_ref().map(|genesis_config| { - let gen = genesis_config - .gen_kind - .is_generic() - .then(|| quote::quote!()); - quote::quote!(+ Config #gen) - }); - - let inherent_part_v2 = def.inherent.as_ref().map(|_| quote::quote!(+ Inherent)); - - let validate_unsigned_part_v2 = def - .validate_unsigned - .as_ref() - .map(|_| quote::quote!(+ ValidateUnsigned)); - - let freeze_reason_part_v2 = def - .composites - .iter() - .any(|c| matches!(c.composite_keyword, CompositeKeyword::FreezeReason(_))) - .then_some(quote::quote!(+ FreezeReason)); - - let hold_reason_part_v2 = def - .composites - .iter() - .any(|c| matches!(c.composite_keyword, CompositeKeyword::HoldReason(_))) - .then_some(quote::quote!(+ HoldReason)); - - let lock_id_part_v2 = def - .composites - .iter() - .any(|c| matches!(c.composite_keyword, CompositeKeyword::LockId(_))) - .then_some(quote::quote!(+ LockId)); - - let slash_reason_part_v2 = def - .composites - .iter() - .any(|c| matches!(c.composite_keyword, CompositeKeyword::SlashReason(_))) - .then_some(quote::quote!(+ SlashReason)); - - quote::quote!( - // This macro follows the conventions as laid out by the `tt-call` crate. It does not - // accept any arguments and simply returns the pallet parts, separated by commas, then - // wrapped inside of braces and finally prepended with double colons, to the caller inside - // of a key named `tokens`. - // - // We need to accept a path argument here, because this macro gets expanded on the - // crate that called the `construct_runtime!` macro, and the actual path is unknown. - #[macro_export] - #[doc(hidden)] - macro_rules! #default_parts_unique_id { - { - $caller:tt - your_tt_return = [{ $my_tt_return:path }] - } => { - $my_tt_return! { - $caller - tokens = [{ - expanded::{ - Pallet, #call_part #storage_part #event_part #error_part #origin_part #config_part - #inherent_part #validate_unsigned_part #freeze_reason_part #task_part - #hold_reason_part #lock_id_part #slash_reason_part - } - }] - } - }; - } - - pub use #default_parts_unique_id as tt_default_parts; - - - // This macro is similar to the `tt_default_parts!`. It expands the pallets that are declared - // explicitly (`System: frame_system::{Pallet, Call}`) with extra parts. - // - // For example, after expansion an explicit pallet would look like: - // `System: expanded::{Error} ::{Pallet, Call}`. - // - // The `expanded` keyword is a marker of the final state of the `construct_runtime!`. - #[macro_export] - #[doc(hidden)] - macro_rules! #extra_parts_unique_id { - { - $caller:tt - your_tt_return = [{ $my_tt_return:path }] - } => { - $my_tt_return! { - $caller - tokens = [{ - expanded::{ - #error_part - } - }] - } - }; - } - - pub use #extra_parts_unique_id as tt_extra_parts; - - #[macro_export] - #[doc(hidden)] - macro_rules! #default_parts_unique_id_v2 { - { - $caller:tt - frame_support = [{ $($frame_support:ident)::* }] - } => { - $($frame_support)*::__private::tt_return! { - $caller - tokens = [{ - + Pallet #call_part_v2 #storage_part_v2 #event_part_v2 #error_part_v2 #origin_part_v2 #config_part_v2 - #inherent_part_v2 #validate_unsigned_part_v2 #freeze_reason_part_v2 #task_part_v2 - #hold_reason_part_v2 #lock_id_part_v2 #slash_reason_part_v2 - }] - } - }; - } - - pub use #default_parts_unique_id_v2 as tt_default_parts_v2; - ) + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + let default_parts_unique_id = + syn::Ident::new(&format!("__tt_default_parts_{}", count), def.item.span()); + let extra_parts_unique_id = + syn::Ident::new(&format!("__tt_extra_parts_{}", count), def.item.span()); + let default_parts_unique_id_v2 = + syn::Ident::new(&format!("__tt_default_parts_v2_{}", count), def.item.span()); + + let call_part = def.call.as_ref().map(|_| quote::quote!(Call,)); + + let task_part = def.task_enum.as_ref().map(|_| quote::quote!(Task,)); + + let storage_part = (!def.storages.is_empty()).then(|| quote::quote!(Storage,)); + + let event_part = def.event.as_ref().map(|event| { + let gen = event.gen_kind.is_generic().then(|| quote::quote!( )); + quote::quote!( Event #gen , ) + }); + + let error_part = def.error.as_ref().map(|_| quote::quote!(Error,)); + + let origin_part = def.origin.as_ref().map(|origin| { + let gen = origin.is_generic.then(|| quote::quote!( )); + quote::quote!( Origin #gen , ) + }); + + let config_part = def.genesis_config.as_ref().map(|genesis_config| { + let gen = genesis_config.gen_kind.is_generic().then(|| quote::quote!( )); + quote::quote!( Config #gen , ) + }); + + let inherent_part = def.inherent.as_ref().map(|_| quote::quote!(Inherent,)); + + let validate_unsigned_part = + def.validate_unsigned.as_ref().map(|_| quote::quote!(ValidateUnsigned,)); + + let freeze_reason_part = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::FreezeReason(_))) + .then_some(quote::quote!(FreezeReason,)); + + let hold_reason_part = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::HoldReason(_))) + .then_some(quote::quote!(HoldReason,)); + + let lock_id_part = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::LockId(_))) + .then_some(quote::quote!(LockId,)); + + let slash_reason_part = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::SlashReason(_))) + .then_some(quote::quote!(SlashReason,)); + + let call_part_v2 = def.call.as_ref().map(|_| quote::quote!(+ Call)); + + let task_part_v2 = def.task_enum.as_ref().map(|_| quote::quote!(+ Task)); + + let storage_part_v2 = (!def.storages.is_empty()).then(|| quote::quote!(+ Storage)); + + let event_part_v2 = def.event.as_ref().map(|event| { + let gen = event.gen_kind.is_generic().then(|| quote::quote!()); + quote::quote!(+ Event #gen) + }); + + let error_part_v2 = def.error.as_ref().map(|_| quote::quote!(+ Error)); + + let origin_part_v2 = def.origin.as_ref().map(|origin| { + let gen = origin.is_generic.then(|| quote::quote!()); + quote::quote!(+ Origin #gen) + }); + + let config_part_v2 = def.genesis_config.as_ref().map(|genesis_config| { + let gen = genesis_config.gen_kind.is_generic().then(|| quote::quote!()); + quote::quote!(+ Config #gen) + }); + + let inherent_part_v2 = def.inherent.as_ref().map(|_| quote::quote!(+ Inherent)); + + let validate_unsigned_part_v2 = + def.validate_unsigned.as_ref().map(|_| quote::quote!(+ ValidateUnsigned)); + + let freeze_reason_part_v2 = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::FreezeReason(_))) + .then_some(quote::quote!(+ FreezeReason)); + + let hold_reason_part_v2 = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::HoldReason(_))) + .then_some(quote::quote!(+ HoldReason)); + + let lock_id_part_v2 = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::LockId(_))) + .then_some(quote::quote!(+ LockId)); + + let slash_reason_part_v2 = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::SlashReason(_))) + .then_some(quote::quote!(+ SlashReason)); + + quote::quote!( + // This macro follows the conventions as laid out by the `tt-call` crate. It does not + // accept any arguments and simply returns the pallet parts, separated by commas, then + // wrapped inside of braces and finally prepended with double colons, to the caller inside + // of a key named `tokens`. + // + // We need to accept a path argument here, because this macro gets expanded on the + // crate that called the `construct_runtime!` macro, and the actual path is unknown. + #[macro_export] + #[doc(hidden)] + macro_rules! #default_parts_unique_id { + { + $caller:tt + your_tt_return = [{ $my_tt_return:path }] + } => { + $my_tt_return! { + $caller + tokens = [{ + expanded::{ + Pallet, #call_part #storage_part #event_part #error_part #origin_part #config_part + #inherent_part #validate_unsigned_part #freeze_reason_part #task_part + #hold_reason_part #lock_id_part #slash_reason_part + } + }] + } + }; + } + + pub use #default_parts_unique_id as tt_default_parts; + + + // This macro is similar to the `tt_default_parts!`. It expands the pallets that are declared + // explicitly (`System: frame_system::{Pallet, Call}`) with extra parts. + // + // For example, after expansion an explicit pallet would look like: + // `System: expanded::{Error} ::{Pallet, Call}`. + // + // The `expanded` keyword is a marker of the final state of the `construct_runtime!`. + #[macro_export] + #[doc(hidden)] + macro_rules! #extra_parts_unique_id { + { + $caller:tt + your_tt_return = [{ $my_tt_return:path }] + } => { + $my_tt_return! { + $caller + tokens = [{ + expanded::{ + #error_part + } + }] + } + }; + } + + pub use #extra_parts_unique_id as tt_extra_parts; + + #[macro_export] + #[doc(hidden)] + macro_rules! #default_parts_unique_id_v2 { + { + $caller:tt + frame_support = [{ $($frame_support:ident)::* }] + } => { + $($frame_support)*::__private::tt_return! { + $caller + tokens = [{ + + Pallet #call_part_v2 #storage_part_v2 #event_part_v2 #error_part_v2 #origin_part_v2 #config_part_v2 + #inherent_part_v2 #validate_unsigned_part_v2 #freeze_reason_part_v2 #task_part_v2 + #hold_reason_part_v2 #lock_id_part_v2 #slash_reason_part_v2 + }] + } + }; + } + + pub use #default_parts_unique_id_v2 as tt_default_parts_v2; + ) } diff --git a/support/procedural-fork/src/pallet/expand/type_value.rs b/support/procedural-fork/src/pallet/expand/type_value.rs index 84db3e431..5dc6309c0 100644 --- a/support/procedural-fork/src/pallet/expand/type_value.rs +++ b/support/procedural-fork/src/pallet/expand/type_value.rs @@ -22,56 +22,56 @@ use crate::pallet::Def; /// * implement the `Get<..>` on it /// * Rename the name of the function to internal name pub fn expand_type_values(def: &mut Def) -> proc_macro2::TokenStream { - let mut expand = quote::quote!(); - let frame_support = &def.frame_support; + let mut expand = quote::quote!(); + let frame_support = &def.frame_support; - for type_value in &def.type_values { - let fn_name_str = &type_value.ident.to_string(); - let fn_name_snakecase = inflector::cases::snakecase::to_snake_case(fn_name_str); - let fn_ident_renamed = syn::Ident::new( - &format!("__type_value_for_{}", fn_name_snakecase), - type_value.ident.span(), - ); + for type_value in &def.type_values { + let fn_name_str = &type_value.ident.to_string(); + let fn_name_snakecase = inflector::cases::snakecase::to_snake_case(fn_name_str); + let fn_ident_renamed = syn::Ident::new( + &format!("__type_value_for_{}", fn_name_snakecase), + type_value.ident.span(), + ); - let type_value_item = { - let item = &mut def.item.content.as_mut().expect("Checked by def").1[type_value.index]; - if let syn::Item::Fn(item) = item { - item - } else { - unreachable!("Checked by error parser") - } - }; + let type_value_item = { + let item = &mut def.item.content.as_mut().expect("Checked by def").1[type_value.index]; + if let syn::Item::Fn(item) = item { + item + } else { + unreachable!("Checked by error parser") + } + }; - // Rename the type_value function name - type_value_item.sig.ident = fn_ident_renamed.clone(); + // Rename the type_value function name + type_value_item.sig.ident = fn_ident_renamed.clone(); - let vis = &type_value.vis; - let ident = &type_value.ident; - let type_ = &type_value.type_; - let where_clause = &type_value.where_clause; + let vis = &type_value.vis; + let ident = &type_value.ident; + let type_ = &type_value.type_; + let where_clause = &type_value.where_clause; - let (struct_impl_gen, struct_use_gen) = if type_value.is_generic { - ( - def.type_impl_generics(type_value.attr_span), - def.type_use_generics(type_value.attr_span), - ) - } else { - (Default::default(), Default::default()) - }; + let (struct_impl_gen, struct_use_gen) = if type_value.is_generic { + ( + def.type_impl_generics(type_value.attr_span), + def.type_use_generics(type_value.attr_span), + ) + } else { + (Default::default(), Default::default()) + }; - let docs = &type_value.docs; + let docs = &type_value.docs; - expand.extend(quote::quote_spanned!(type_value.attr_span => - #( #[doc = #docs] )* - #vis struct #ident<#struct_use_gen>(core::marker::PhantomData<((), #struct_use_gen)>); - impl<#struct_impl_gen> #frame_support::traits::Get<#type_> for #ident<#struct_use_gen> - #where_clause - { - fn get() -> #type_ { - #fn_ident_renamed::<#struct_use_gen>() - } - } - )); - } - expand + expand.extend(quote::quote_spanned!(type_value.attr_span => + #( #[doc = #docs] )* + #vis struct #ident<#struct_use_gen>(core::marker::PhantomData<((), #struct_use_gen)>); + impl<#struct_impl_gen> #frame_support::traits::Get<#type_> for #ident<#struct_use_gen> + #where_clause + { + fn get() -> #type_ { + #fn_ident_renamed::<#struct_use_gen>() + } + } + )); + } + expand } diff --git a/support/procedural-fork/src/pallet/expand/validate_unsigned.rs b/support/procedural-fork/src/pallet/expand/validate_unsigned.rs index 28c78a1c6..876995585 100644 --- a/support/procedural-fork/src/pallet/expand/validate_unsigned.rs +++ b/support/procedural-fork/src/pallet/expand/validate_unsigned.rs @@ -21,38 +21,36 @@ use quote::quote; use syn::{spanned::Spanned, Ident}; pub fn expand_validate_unsigned(def: &mut Def) -> TokenStream { - let count = COUNTER.with(|counter| counter.borrow_mut().inc()); - let macro_ident = Ident::new( - &format!("__is_validate_unsigned_part_defined_{}", count), - def.item.span(), - ); + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + let macro_ident = + Ident::new(&format!("__is_validate_unsigned_part_defined_{}", count), def.item.span()); - let maybe_compile_error = if def.validate_unsigned.is_none() { - quote! { - compile_error!(concat!( - "`", - stringify!($pallet_name), - "` does not have #[pallet::validate_unsigned] defined, perhaps you should \ - remove `ValidateUnsigned` from construct_runtime?", - )); - } - } else { - TokenStream::new() - }; + let maybe_compile_error = if def.validate_unsigned.is_none() { + quote! { + compile_error!(concat!( + "`", + stringify!($pallet_name), + "` does not have #[pallet::validate_unsigned] defined, perhaps you should \ + remove `ValidateUnsigned` from construct_runtime?", + )); + } + } else { + TokenStream::new() + }; - quote! { - #[doc(hidden)] - pub mod __substrate_validate_unsigned_check { - #[macro_export] - #[doc(hidden)] - macro_rules! #macro_ident { - ($pallet_name:ident) => { - #maybe_compile_error - } - } + quote! { + #[doc(hidden)] + pub mod __substrate_validate_unsigned_check { + #[macro_export] + #[doc(hidden)] + macro_rules! #macro_ident { + ($pallet_name:ident) => { + #maybe_compile_error + } + } - #[doc(hidden)] - pub use #macro_ident as is_validate_unsigned_part_defined; - } - } + #[doc(hidden)] + pub use #macro_ident as is_validate_unsigned_part_defined; + } + } } diff --git a/support/procedural-fork/src/pallet/expand/warnings.rs b/support/procedural-fork/src/pallet/expand/warnings.rs index ece03a13a..030e3ddaf 100644 --- a/support/procedural-fork/src/pallet/expand/warnings.rs +++ b/support/procedural-fork/src/pallet/expand/warnings.rs @@ -20,84 +20,79 @@ use crate::pallet::parse::call::{CallVariantDef, CallWeightDef}; use proc_macro_warning::Warning; use syn::{ - spanned::Spanned, - visit::{self, Visit}, + spanned::Spanned, + visit::{self, Visit}, }; /// Warn if any of the call arguments starts with a underscore and is used in a weight formula. pub(crate) fn weight_witness_warning( - method: &CallVariantDef, - dev_mode: bool, - warnings: &mut Vec, + method: &CallVariantDef, + dev_mode: bool, + warnings: &mut Vec, ) { - if dev_mode { - return; - } - let CallWeightDef::Immediate(w) = &method.weight else { - return; - }; + if dev_mode { + return + } + let CallWeightDef::Immediate(w) = &method.weight else { return }; - let partial_warning = Warning::new_deprecated("UncheckedWeightWitness") - .old("not check weight witness data") - .new("ensure that all witness data for weight calculation is checked before usage") - .help_link("https://github.com/paritytech/polkadot-sdk/pull/1818"); + let partial_warning = Warning::new_deprecated("UncheckedWeightWitness") + .old("not check weight witness data") + .new("ensure that all witness data for weight calculation is checked before usage") + .help_link("https://github.com/paritytech/polkadot-sdk/pull/1818"); - for (_, arg_ident, _) in method.args.iter() { - if !arg_ident.to_string().starts_with('_') || !contains_ident(w.clone(), arg_ident) { - continue; - } + for (_, arg_ident, _) in method.args.iter() { + if !arg_ident.to_string().starts_with('_') || !contains_ident(w.clone(), &arg_ident) { + continue + } - let warning = partial_warning - .clone() - .index(warnings.len()) - .span(arg_ident.span()) - .build_or_panic(); + let warning = partial_warning + .clone() + .index(warnings.len()) + .span(arg_ident.span()) + .build_or_panic(); - warnings.push(warning); - } + warnings.push(warning); + } } /// Warn if the weight is a constant and the pallet not in `dev_mode`. pub(crate) fn weight_constant_warning( - weight: &syn::Expr, - dev_mode: bool, - warnings: &mut Vec, + weight: &syn::Expr, + dev_mode: bool, + warnings: &mut Vec, ) { - if dev_mode { - return; - } - let syn::Expr::Lit(lit) = weight else { return }; + if dev_mode { + return + } + let syn::Expr::Lit(lit) = weight else { return }; - let warning = Warning::new_deprecated("ConstantWeight") - .index(warnings.len()) - .old("use hard-coded constant as call weight") - .new("benchmark all calls or put the pallet into `dev` mode") - .help_link("https://github.com/paritytech/substrate/pull/13798") - .span(lit.span()) - .build_or_panic(); + let warning = Warning::new_deprecated("ConstantWeight") + .index(warnings.len()) + .old("use hard-coded constant as call weight") + .new("benchmark all calls or put the pallet into `dev` mode") + .help_link("https://github.com/paritytech/substrate/pull/13798") + .span(lit.span()) + .build_or_panic(); - warnings.push(warning); + warnings.push(warning); } /// Returns whether `expr` contains `ident`. fn contains_ident(mut expr: syn::Expr, ident: &syn::Ident) -> bool { - struct ContainsIdent { - ident: syn::Ident, - found: bool, - } + struct ContainsIdent { + ident: syn::Ident, + found: bool, + } - impl<'a> Visit<'a> for ContainsIdent { - fn visit_ident(&mut self, i: &syn::Ident) { - if *i == self.ident { - self.found = true; - } - } - } + impl<'a> Visit<'a> for ContainsIdent { + fn visit_ident(&mut self, i: &syn::Ident) { + if *i == self.ident { + self.found = true; + } + } + } - let mut visitor = ContainsIdent { - ident: ident.clone(), - found: false, - }; - visit::visit_expr(&mut visitor, &mut expr); - visitor.found + let mut visitor = ContainsIdent { ident: ident.clone(), found: false }; + visit::visit_expr(&mut visitor, &mut expr); + visitor.found } diff --git a/support/procedural-fork/src/pallet/mod.rs b/support/procedural-fork/src/pallet/mod.rs index 5b9bc621b..42d8272fb 100644 --- a/support/procedural-fork/src/pallet/mod.rs +++ b/support/procedural-fork/src/pallet/mod.rs @@ -32,30 +32,30 @@ pub use parse::{composite::keyword::CompositeKeyword, Def}; use syn::spanned::Spanned; mod keyword { - syn::custom_keyword!(dev_mode); + syn::custom_keyword!(dev_mode); } pub fn pallet( - attr: proc_macro::TokenStream, - item: proc_macro::TokenStream, + attr: proc_macro::TokenStream, + item: proc_macro::TokenStream, ) -> proc_macro::TokenStream { - let mut dev_mode = false; - if !attr.is_empty() { - if syn::parse::(attr.clone()).is_ok() { - dev_mode = true; - } else { - let msg = "Invalid pallet macro call: unexpected attribute. Macro call must be \ + let mut dev_mode = false; + if !attr.is_empty() { + if let Ok(_) = syn::parse::(attr.clone()) { + dev_mode = true; + } else { + let msg = "Invalid pallet macro call: unexpected attribute. Macro call must be \ bare, such as `#[frame_support::pallet]` or `#[pallet]`, or must specify the \ `dev_mode` attribute, such as `#[frame_support::pallet(dev_mode)]` or \ #[pallet(dev_mode)]."; - let span = proc_macro2::TokenStream::from(attr).span(); - return syn::Error::new(span, msg).to_compile_error().into(); - } - } - - let item = syn::parse_macro_input!(item as syn::ItemMod); - match parse::Def::try_from(item, dev_mode) { - Ok(def) => expand::expand(def).into(), - Err(e) => e.to_compile_error().into(), - } + let span = proc_macro2::TokenStream::from(attr).span(); + return syn::Error::new(span, msg).to_compile_error().into() + } + } + + let item = syn::parse_macro_input!(item as syn::ItemMod); + match parse::Def::try_from(item, dev_mode) { + Ok(def) => expand::expand(def).into(), + Err(e) => e.to_compile_error().into(), + } } diff --git a/support/procedural-fork/src/pallet/parse/call.rs b/support/procedural-fork/src/pallet/parse/call.rs index 0bcf38a4e..4e09b86fd 100644 --- a/support/procedural-fork/src/pallet/parse/call.rs +++ b/support/procedural-fork/src/pallet/parse/call.rs @@ -24,124 +24,124 @@ use syn::{spanned::Spanned, ExprClosure}; /// List of additional token to be used for parsing. mod keyword { - syn::custom_keyword!(Call); - syn::custom_keyword!(OriginFor); - syn::custom_keyword!(RuntimeOrigin); - syn::custom_keyword!(weight); - syn::custom_keyword!(call_index); - syn::custom_keyword!(compact); - syn::custom_keyword!(T); - syn::custom_keyword!(pallet); - syn::custom_keyword!(feeless_if); + syn::custom_keyword!(Call); + syn::custom_keyword!(OriginFor); + syn::custom_keyword!(RuntimeOrigin); + syn::custom_keyword!(weight); + syn::custom_keyword!(call_index); + syn::custom_keyword!(compact); + syn::custom_keyword!(T); + syn::custom_keyword!(pallet); + syn::custom_keyword!(feeless_if); } /// Definition of dispatchables typically `impl Pallet { ... }` pub struct CallDef { - /// The where_clause used. - pub where_clause: Option, - /// A set of usage of instance, must be check for consistency with trait. - pub instances: Vec, - /// The index of call item in pallet module. - pub index: usize, - /// Information on methods (used for expansion). - pub methods: Vec, - /// The span of the pallet::call attribute. - pub attr_span: proc_macro2::Span, - /// Docs, specified on the impl Block. - pub docs: Vec, - /// The optional `weight` attribute on the `pallet::call`. - pub inherited_call_weight: Option, + /// The where_clause used. + pub where_clause: Option, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Vec, + /// The index of call item in pallet module. + pub index: usize, + /// Information on methods (used for expansion). + pub methods: Vec, + /// The span of the pallet::call attribute. + pub attr_span: proc_macro2::Span, + /// Docs, specified on the impl Block. + pub docs: Vec, + /// The optional `weight` attribute on the `pallet::call`. + pub inherited_call_weight: Option, } /// The weight of a call. #[derive(Clone)] pub enum CallWeightDef { - /// Explicitly set on the call itself with `#[pallet::weight(…)]`. This value is used. - Immediate(syn::Expr), + /// Explicitly set on the call itself with `#[pallet::weight(…)]`. This value is used. + Immediate(syn::Expr), - /// The default value that should be set for dev-mode pallets. Usually zero. - DevModeDefault, + /// The default value that should be set for dev-mode pallets. Usually zero. + DevModeDefault, - /// Inherits whatever value is configured on the pallet level. - /// - /// The concrete value is not known at this point. - Inherited, + /// Inherits whatever value is configured on the pallet level. + /// + /// The concrete value is not known at this point. + Inherited, } /// Definition of dispatchable typically: `#[weight...] fn foo(origin .., param1: ...) -> ..` #[derive(Clone)] pub struct CallVariantDef { - /// Function name. - pub name: syn::Ident, - /// Information on args: `(is_compact, name, type)` - pub args: Vec<(bool, syn::Ident, Box)>, - /// Weight for the call. - pub weight: CallWeightDef, - /// Call index of the dispatchable. - pub call_index: u8, - /// Whether an explicit call index was specified. - pub explicit_call_index: bool, - /// Docs, used for metadata. - pub docs: Vec, - /// Attributes annotated at the top of the dispatchable function. - pub attrs: Vec, - /// The `cfg` attributes. - pub cfg_attrs: Vec, - /// The optional `feeless_if` attribute on the `pallet::call`. - pub feeless_check: Option, + /// Function name. + pub name: syn::Ident, + /// Information on args: `(is_compact, name, type)` + pub args: Vec<(bool, syn::Ident, Box)>, + /// Weight for the call. + pub weight: CallWeightDef, + /// Call index of the dispatchable. + pub call_index: u8, + /// Whether an explicit call index was specified. + pub explicit_call_index: bool, + /// Docs, used for metadata. + pub docs: Vec, + /// Attributes annotated at the top of the dispatchable function. + pub attrs: Vec, + /// The `cfg` attributes. + pub cfg_attrs: Vec, + /// The optional `feeless_if` attribute on the `pallet::call`. + pub feeless_check: Option, } /// Attributes for functions in call impl block. pub enum FunctionAttr { - /// Parse for `#[pallet::call_index(expr)]` - CallIndex(u8), - /// Parse for `#[pallet::weight(expr)]` - Weight(syn::Expr), - /// Parse for `#[pallet::feeless_if(expr)]` - FeelessIf(Span, syn::ExprClosure), + /// Parse for `#[pallet::call_index(expr)]` + CallIndex(u8), + /// Parse for `#[pallet::weight(expr)]` + Weight(syn::Expr), + /// Parse for `#[pallet::feeless_if(expr)]` + FeelessIf(Span, syn::ExprClosure), } impl syn::parse::Parse for FunctionAttr { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - let content; - syn::bracketed!(content in input); - content.parse::()?; - content.parse::()?; - - let lookahead = content.lookahead1(); - if lookahead.peek(keyword::weight) { - content.parse::()?; - let weight_content; - syn::parenthesized!(weight_content in content); - Ok(FunctionAttr::Weight(weight_content.parse::()?)) - } else if lookahead.peek(keyword::call_index) { - content.parse::()?; - let call_index_content; - syn::parenthesized!(call_index_content in content); - let index = call_index_content.parse::()?; - if !index.suffix().is_empty() { - let msg = "Number literal must not have a suffix"; - return Err(syn::Error::new(index.span(), msg)); - } - Ok(FunctionAttr::CallIndex(index.base10_parse()?)) - } else if lookahead.peek(keyword::feeless_if) { - content.parse::()?; - let closure_content; - syn::parenthesized!(closure_content in content); - Ok(FunctionAttr::FeelessIf( - closure_content.span(), - closure_content.parse::().map_err(|e| { - let msg = "Invalid feeless_if attribute: expected a closure"; - let mut err = syn::Error::new(closure_content.span(), msg); - err.combine(e); - err - })?, - )) - } else { - Err(lookahead.error()) - } - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + let lookahead = content.lookahead1(); + if lookahead.peek(keyword::weight) { + content.parse::()?; + let weight_content; + syn::parenthesized!(weight_content in content); + Ok(FunctionAttr::Weight(weight_content.parse::()?)) + } else if lookahead.peek(keyword::call_index) { + content.parse::()?; + let call_index_content; + syn::parenthesized!(call_index_content in content); + let index = call_index_content.parse::()?; + if !index.suffix().is_empty() { + let msg = "Number literal must not have a suffix"; + return Err(syn::Error::new(index.span(), msg)) + } + Ok(FunctionAttr::CallIndex(index.base10_parse()?)) + } else if lookahead.peek(keyword::feeless_if) { + content.parse::()?; + let closure_content; + syn::parenthesized!(closure_content in content); + Ok(FunctionAttr::FeelessIf( + closure_content.span(), + closure_content.parse::().map_err(|e| { + let msg = "Invalid feeless_if attribute: expected a closure"; + let mut err = syn::Error::new(closure_content.span(), msg); + err.combine(e); + err + })?, + )) + } else { + Err(lookahead.error()) + } + } } /// Attribute for arguments in function in call impl block. @@ -149,324 +149,319 @@ impl syn::parse::Parse for FunctionAttr { pub struct ArgAttrIsCompact; impl syn::parse::Parse for ArgAttrIsCompact { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - let content; - syn::bracketed!(content in input); - content.parse::()?; - content.parse::()?; - - content.parse::()?; - Ok(ArgAttrIsCompact) - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + content.parse::()?; + Ok(ArgAttrIsCompact) + } } /// Check the syntax is `OriginFor`, `&OriginFor` or `T::RuntimeOrigin`. pub fn check_dispatchable_first_arg_type(ty: &syn::Type, is_ref: bool) -> syn::Result<()> { - pub struct CheckOriginFor(bool); - impl syn::parse::Parse for CheckOriginFor { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let is_ref = input.parse::().is_ok(); - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::]>()?; - - Ok(Self(is_ref)) - } - } - - pub struct CheckRuntimeOrigin; - impl syn::parse::Parse for CheckRuntimeOrigin { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - input.parse::()?; - input.parse::()?; - - Ok(Self) - } - } - - let result_origin_for = syn::parse2::(ty.to_token_stream()); - let result_runtime_origin = syn::parse2::(ty.to_token_stream()); - match (result_origin_for, result_runtime_origin) { - (Ok(CheckOriginFor(has_ref)), _) if is_ref == has_ref => Ok(()), - (_, Ok(_)) => Ok(()), - (_, _) => { - let msg = if is_ref { - "Invalid type: expected `&OriginFor`" - } else { - "Invalid type: expected `OriginFor` or `T::RuntimeOrigin`" - }; - Err(syn::Error::new(ty.span(), msg)) - } - } + pub struct CheckOriginFor(bool); + impl syn::parse::Parse for CheckOriginFor { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let is_ref = input.parse::().is_ok(); + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::]>()?; + + Ok(Self(is_ref)) + } + } + + pub struct CheckRuntimeOrigin; + impl syn::parse::Parse for CheckRuntimeOrigin { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + input.parse::()?; + input.parse::()?; + + Ok(Self) + } + } + + let result_origin_for = syn::parse2::(ty.to_token_stream()); + let result_runtime_origin = syn::parse2::(ty.to_token_stream()); + return match (result_origin_for, result_runtime_origin) { + (Ok(CheckOriginFor(has_ref)), _) if is_ref == has_ref => Ok(()), + (_, Ok(_)) => Ok(()), + (_, _) => { + let msg = if is_ref { + "Invalid type: expected `&OriginFor`" + } else { + "Invalid type: expected `OriginFor` or `T::RuntimeOrigin`" + }; + return Err(syn::Error::new(ty.span(), msg)) + }, + } } impl CallDef { - pub fn try_from( - attr_span: proc_macro2::Span, - index: usize, - item: &mut syn::Item, - dev_mode: bool, - inherited_call_weight: Option, - ) -> syn::Result { - let item_impl = if let syn::Item::Impl(item) = item { - item - } else { - return Err(syn::Error::new( - item.span(), - "Invalid pallet::call, expected item impl", - )); - }; - - let instances = vec![ - helper::check_impl_gen(&item_impl.generics, item_impl.impl_token.span())?, - helper::check_pallet_struct_usage(&item_impl.self_ty)?, - ]; - - if let Some((_, _, for_)) = item_impl.trait_ { - let msg = "Invalid pallet::call, expected no trait ident as in \ + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + dev_mode: bool, + inherited_call_weight: Option, + ) -> syn::Result { + let item_impl = if let syn::Item::Impl(item) = item { + item + } else { + return Err(syn::Error::new(item.span(), "Invalid pallet::call, expected item impl")) + }; + + let instances = vec![ + helper::check_impl_gen(&item_impl.generics, item_impl.impl_token.span())?, + helper::check_pallet_struct_usage(&item_impl.self_ty)?, + ]; + + if let Some((_, _, for_)) = item_impl.trait_ { + let msg = "Invalid pallet::call, expected no trait ident as in \ `impl<..> Pallet<..> { .. }`"; - return Err(syn::Error::new(for_.span(), msg)); - } - - let mut methods = vec![]; - let mut indices = HashMap::new(); - let mut last_index: Option = None; - for item in &mut item_impl.items { - if let syn::ImplItem::Fn(method) = item { - if !matches!(method.vis, syn::Visibility::Public(_)) { - let msg = "Invalid pallet::call, dispatchable function must be public: \ + return Err(syn::Error::new(for_.span(), msg)) + } + + let mut methods = vec![]; + let mut indices = HashMap::new(); + let mut last_index: Option = None; + for item in &mut item_impl.items { + if let syn::ImplItem::Fn(method) = item { + if !matches!(method.vis, syn::Visibility::Public(_)) { + let msg = "Invalid pallet::call, dispatchable function must be public: \ `pub fn`"; - let span = match method.vis { - syn::Visibility::Inherited => method.sig.span(), - _ => method.vis.span(), - }; - - return Err(syn::Error::new(span, msg)); - } - - match method.sig.inputs.first() { - None => { - let msg = "Invalid pallet::call, must have at least origin arg"; - return Err(syn::Error::new(method.sig.span(), msg)); - } - Some(syn::FnArg::Receiver(_)) => { - let msg = "Invalid pallet::call, first argument must be a typed argument, \ + let span = match method.vis { + syn::Visibility::Inherited => method.sig.span(), + _ => method.vis.span(), + }; + + return Err(syn::Error::new(span, msg)) + } + + match method.sig.inputs.first() { + None => { + let msg = "Invalid pallet::call, must have at least origin arg"; + return Err(syn::Error::new(method.sig.span(), msg)) + }, + Some(syn::FnArg::Receiver(_)) => { + let msg = "Invalid pallet::call, first argument must be a typed argument, \ e.g. `origin: OriginFor`"; - return Err(syn::Error::new(method.sig.span(), msg)); - } - Some(syn::FnArg::Typed(arg)) => { - check_dispatchable_first_arg_type(&arg.ty, false)?; - } - } - - if let syn::ReturnType::Type(_, type_) = &method.sig.output { - helper::check_pallet_call_return_type(type_)?; - } else { - let msg = "Invalid pallet::call, require return type \ + return Err(syn::Error::new(method.sig.span(), msg)) + }, + Some(syn::FnArg::Typed(arg)) => { + check_dispatchable_first_arg_type(&arg.ty, false)?; + }, + } + + if let syn::ReturnType::Type(_, type_) = &method.sig.output { + helper::check_pallet_call_return_type(type_)?; + } else { + let msg = "Invalid pallet::call, require return type \ DispatchResultWithPostInfo"; - return Err(syn::Error::new(method.sig.span(), msg)); - } - - let cfg_attrs: Vec = helper::get_item_cfg_attrs(&method.attrs); - let mut call_idx_attrs = vec![]; - let mut weight_attrs = vec![]; - let mut feeless_attrs = vec![]; - for attr in helper::take_item_pallet_attrs(&mut method.attrs)?.into_iter() { - match attr { - FunctionAttr::CallIndex(_) => { - call_idx_attrs.push(attr); - } - FunctionAttr::Weight(_) => { - weight_attrs.push(attr); - } - FunctionAttr::FeelessIf(span, _) => { - feeless_attrs.push((span, attr)); - } - } - } - - if weight_attrs.is_empty() && dev_mode { - // inject a default O(1) weight when dev mode is enabled and no weight has - // been specified on the call - let empty_weight: syn::Expr = syn::parse_quote!(0); - weight_attrs.push(FunctionAttr::Weight(empty_weight)); - } - - let weight = match weight_attrs.len() { - 0 if inherited_call_weight.is_some() => CallWeightDef::Inherited, - 0 if dev_mode => CallWeightDef::DevModeDefault, - 0 => return Err(syn::Error::new( - method.sig.span(), - "A pallet::call requires either a concrete `#[pallet::weight($expr)]` or an + return Err(syn::Error::new(method.sig.span(), msg)) + } + + let cfg_attrs: Vec = helper::get_item_cfg_attrs(&method.attrs); + let mut call_idx_attrs = vec![]; + let mut weight_attrs = vec![]; + let mut feeless_attrs = vec![]; + for attr in helper::take_item_pallet_attrs(&mut method.attrs)?.into_iter() { + match attr { + FunctionAttr::CallIndex(_) => { + call_idx_attrs.push(attr); + }, + FunctionAttr::Weight(_) => { + weight_attrs.push(attr); + }, + FunctionAttr::FeelessIf(span, _) => { + feeless_attrs.push((span, attr)); + }, + } + } + + if weight_attrs.is_empty() && dev_mode { + // inject a default O(1) weight when dev mode is enabled and no weight has + // been specified on the call + let empty_weight: syn::Expr = syn::parse_quote!(0); + weight_attrs.push(FunctionAttr::Weight(empty_weight)); + } + + let weight = match weight_attrs.len() { + 0 if inherited_call_weight.is_some() => CallWeightDef::Inherited, + 0 if dev_mode => CallWeightDef::DevModeDefault, + 0 => return Err(syn::Error::new( + method.sig.span(), + "A pallet::call requires either a concrete `#[pallet::weight($expr)]` or an inherited weight from the `#[pallet:call(weight($type))]` attribute, but none were given.", - )), - 1 => match weight_attrs.pop().unwrap() { - FunctionAttr::Weight(w) => CallWeightDef::Immediate(w), - _ => unreachable!("checked during creation of the let binding"), - }, - _ => { - let msg = "Invalid pallet::call, too many weight attributes given"; - return Err(syn::Error::new(method.sig.span(), msg)); - } - }; - - if call_idx_attrs.len() > 1 { - let msg = "Invalid pallet::call, too many call_index attributes given"; - return Err(syn::Error::new(method.sig.span(), msg)); - } - let call_index = call_idx_attrs.pop().map(|attr| match attr { - FunctionAttr::CallIndex(idx) => idx, - _ => unreachable!("checked during creation of the let binding"), - }); - let explicit_call_index = call_index.is_some(); - - let final_index = match call_index { - Some(i) => i, - None => last_index - .map_or(Some(0), |idx| idx.checked_add(1)) - .ok_or_else(|| { - let msg = "Call index doesn't fit into u8, index is 256"; - syn::Error::new(method.sig.span(), msg) - })?, - }; - last_index = Some(final_index); - - if let Some(used_fn) = indices.insert(final_index, method.sig.ident.clone()) { - let msg = format!( - "Call indices are conflicting: Both functions {} and {} are at index {}", - used_fn, method.sig.ident, final_index, - ); - let mut err = syn::Error::new(used_fn.span(), &msg); - err.combine(syn::Error::new(method.sig.ident.span(), msg)); - return Err(err); - } - - let mut args = vec![]; - for arg in method.sig.inputs.iter_mut().skip(1) { - let arg = if let syn::FnArg::Typed(arg) = arg { - arg - } else { - unreachable!("Only first argument can be receiver"); - }; - - let arg_attrs: Vec = - helper::take_item_pallet_attrs(&mut arg.attrs)?; - - if arg_attrs.len() > 1 { - let msg = "Invalid pallet::call, argument has too many attributes"; - return Err(syn::Error::new(arg.span(), msg)); - } - - let arg_ident = if let syn::Pat::Ident(pat) = &*arg.pat { - pat.ident.clone() - } else { - let msg = "Invalid pallet::call, argument must be ident"; - return Err(syn::Error::new(arg.pat.span(), msg)); - }; - - args.push((!arg_attrs.is_empty(), arg_ident, arg.ty.clone())); - } - - let docs = get_doc_literals(&method.attrs); - - if feeless_attrs.len() > 1 { - let msg = "Invalid pallet::call, there can only be one feeless_if attribute"; - return Err(syn::Error::new(feeless_attrs[1].0, msg)); - } - let feeless_check: Option = - feeless_attrs.pop().map(|(_, attr)| match attr { - FunctionAttr::FeelessIf(_, closure) => closure, - _ => unreachable!("checked during creation of the let binding"), - }); - - if let Some(ref feeless_check) = feeless_check { - if feeless_check.inputs.len() != args.len() + 1 { - let msg = "Invalid pallet::call, feeless_if closure must have same \ + )), + 1 => match weight_attrs.pop().unwrap() { + FunctionAttr::Weight(w) => CallWeightDef::Immediate(w), + _ => unreachable!("checked during creation of the let binding"), + }, + _ => { + let msg = "Invalid pallet::call, too many weight attributes given"; + return Err(syn::Error::new(method.sig.span(), msg)) + }, + }; + + if call_idx_attrs.len() > 1 { + let msg = "Invalid pallet::call, too many call_index attributes given"; + return Err(syn::Error::new(method.sig.span(), msg)) + } + let call_index = call_idx_attrs.pop().map(|attr| match attr { + FunctionAttr::CallIndex(idx) => idx, + _ => unreachable!("checked during creation of the let binding"), + }); + let explicit_call_index = call_index.is_some(); + + let final_index = match call_index { + Some(i) => i, + None => + last_index.map_or(Some(0), |idx| idx.checked_add(1)).ok_or_else(|| { + let msg = "Call index doesn't fit into u8, index is 256"; + syn::Error::new(method.sig.span(), msg) + })?, + }; + last_index = Some(final_index); + + if let Some(used_fn) = indices.insert(final_index, method.sig.ident.clone()) { + let msg = format!( + "Call indices are conflicting: Both functions {} and {} are at index {}", + used_fn, method.sig.ident, final_index, + ); + let mut err = syn::Error::new(used_fn.span(), &msg); + err.combine(syn::Error::new(method.sig.ident.span(), msg)); + return Err(err) + } + + let mut args = vec![]; + for arg in method.sig.inputs.iter_mut().skip(1) { + let arg = if let syn::FnArg::Typed(arg) = arg { + arg + } else { + unreachable!("Only first argument can be receiver"); + }; + + let arg_attrs: Vec = + helper::take_item_pallet_attrs(&mut arg.attrs)?; + + if arg_attrs.len() > 1 { + let msg = "Invalid pallet::call, argument has too many attributes"; + return Err(syn::Error::new(arg.span(), msg)) + } + + let arg_ident = if let syn::Pat::Ident(pat) = &*arg.pat { + pat.ident.clone() + } else { + let msg = "Invalid pallet::call, argument must be ident"; + return Err(syn::Error::new(arg.pat.span(), msg)) + }; + + args.push((!arg_attrs.is_empty(), arg_ident, arg.ty.clone())); + } + + let docs = get_doc_literals(&method.attrs); + + if feeless_attrs.len() > 1 { + let msg = "Invalid pallet::call, there can only be one feeless_if attribute"; + return Err(syn::Error::new(feeless_attrs[1].0, msg)) + } + let feeless_check: Option = + feeless_attrs.pop().map(|(_, attr)| match attr { + FunctionAttr::FeelessIf(_, closure) => closure, + _ => unreachable!("checked during creation of the let binding"), + }); + + if let Some(ref feeless_check) = feeless_check { + if feeless_check.inputs.len() != args.len() + 1 { + let msg = "Invalid pallet::call, feeless_if closure must have same \ number of arguments as the dispatchable function"; - return Err(syn::Error::new(feeless_check.span(), msg)); - } - - match feeless_check.inputs.first() { - None => { - let msg = "Invalid pallet::call, feeless_if closure must have at least origin arg"; - return Err(syn::Error::new(feeless_check.span(), msg)); - } - Some(syn::Pat::Type(arg)) => { - check_dispatchable_first_arg_type(&arg.ty, true)?; - } - _ => { - let msg = "Invalid pallet::call, feeless_if closure first argument must be a typed argument, \ + return Err(syn::Error::new(feeless_check.span(), msg)) + } + + match feeless_check.inputs.first() { + None => { + let msg = "Invalid pallet::call, feeless_if closure must have at least origin arg"; + return Err(syn::Error::new(feeless_check.span(), msg)) + }, + Some(syn::Pat::Type(arg)) => { + check_dispatchable_first_arg_type(&arg.ty, true)?; + }, + _ => { + let msg = "Invalid pallet::call, feeless_if closure first argument must be a typed argument, \ e.g. `origin: OriginFor`"; - return Err(syn::Error::new(feeless_check.span(), msg)); - } - } - - for (feeless_arg, arg) in feeless_check.inputs.iter().skip(1).zip(args.iter()) { - let feeless_arg_type = if let syn::Pat::Type(syn::PatType { ty, .. }) = - feeless_arg.clone() - { - if let syn::Type::Reference(pat) = *ty { - pat.elem.clone() - } else { - let msg = "Invalid pallet::call, feeless_if closure argument must be a reference"; - return Err(syn::Error::new(ty.span(), msg)); - } - } else { - let msg = "Invalid pallet::call, feeless_if closure argument must be a type ascription pattern"; - return Err(syn::Error::new(feeless_arg.span(), msg)); - }; - - if feeless_arg_type != arg.2 { - let msg = - "Invalid pallet::call, feeless_if closure argument must have \ + return Err(syn::Error::new(feeless_check.span(), msg)) + }, + } + + for (feeless_arg, arg) in feeless_check.inputs.iter().skip(1).zip(args.iter()) { + let feeless_arg_type = + if let syn::Pat::Type(syn::PatType { ty, .. }) = feeless_arg.clone() { + if let syn::Type::Reference(pat) = *ty { + pat.elem.clone() + } else { + let msg = "Invalid pallet::call, feeless_if closure argument must be a reference"; + return Err(syn::Error::new(ty.span(), msg)) + } + } else { + let msg = "Invalid pallet::call, feeless_if closure argument must be a type ascription pattern"; + return Err(syn::Error::new(feeless_arg.span(), msg)) + }; + + if feeless_arg_type != arg.2 { + let msg = + "Invalid pallet::call, feeless_if closure argument must have \ a reference to the same type as the dispatchable function argument"; - return Err(syn::Error::new(feeless_arg.span(), msg)); - } - } - - let valid_return = match &feeless_check.output { - syn::ReturnType::Type(_, type_) => match *(type_.clone()) { - syn::Type::Path(syn::TypePath { path, .. }) => path.is_ident("bool"), - _ => false, - }, - _ => false, - }; - if !valid_return { - let msg = "Invalid pallet::call, feeless_if closure must return `bool`"; - return Err(syn::Error::new(feeless_check.output.span(), msg)); - } - } - - methods.push(CallVariantDef { - name: method.sig.ident.clone(), - weight, - call_index: final_index, - explicit_call_index, - args, - docs, - attrs: method.attrs.clone(), - cfg_attrs, - feeless_check, - }); - } else { - let msg = "Invalid pallet::call, only method accepted"; - return Err(syn::Error::new(item.span(), msg)); - } - } - - Ok(Self { - index, - attr_span, - instances, - methods, - where_clause: item_impl.generics.where_clause.clone(), - docs: get_doc_literals(&item_impl.attrs), - inherited_call_weight, - }) - } + return Err(syn::Error::new(feeless_arg.span(), msg)) + } + } + + let valid_return = match &feeless_check.output { + syn::ReturnType::Type(_, type_) => match *(type_.clone()) { + syn::Type::Path(syn::TypePath { path, .. }) => path.is_ident("bool"), + _ => false, + }, + _ => false, + }; + if !valid_return { + let msg = "Invalid pallet::call, feeless_if closure must return `bool`"; + return Err(syn::Error::new(feeless_check.output.span(), msg)) + } + } + + methods.push(CallVariantDef { + name: method.sig.ident.clone(), + weight, + call_index: final_index, + explicit_call_index, + args, + docs, + attrs: method.attrs.clone(), + cfg_attrs, + feeless_check, + }); + } else { + let msg = "Invalid pallet::call, only method accepted"; + return Err(syn::Error::new(item.span(), msg)) + } + } + + Ok(Self { + index, + attr_span, + instances, + methods, + where_clause: item_impl.generics.where_clause.clone(), + docs: get_doc_literals(&item_impl.attrs), + inherited_call_weight, + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/composite.rs b/support/procedural-fork/src/pallet/parse/composite.rs index 38da1f205..c3ac74846 100644 --- a/support/procedural-fork/src/pallet/parse/composite.rs +++ b/support/procedural-fork/src/pallet/parse/composite.rs @@ -20,178 +20,172 @@ use quote::ToTokens; use syn::spanned::Spanned; pub mod keyword { - use super::*; - - syn::custom_keyword!(FreezeReason); - syn::custom_keyword!(HoldReason); - syn::custom_keyword!(LockId); - syn::custom_keyword!(SlashReason); - syn::custom_keyword!(Task); - - pub enum CompositeKeyword { - FreezeReason(FreezeReason), - HoldReason(HoldReason), - LockId(LockId), - SlashReason(SlashReason), - Task(Task), - } - - impl ToTokens for CompositeKeyword { - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - use CompositeKeyword::*; - match self { - FreezeReason(inner) => inner.to_tokens(tokens), - HoldReason(inner) => inner.to_tokens(tokens), - LockId(inner) => inner.to_tokens(tokens), - SlashReason(inner) => inner.to_tokens(tokens), - Task(inner) => inner.to_tokens(tokens), - } - } - } - - impl syn::parse::Parse for CompositeKeyword { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let lookahead = input.lookahead1(); - if lookahead.peek(FreezeReason) { - Ok(Self::FreezeReason(input.parse()?)) - } else if lookahead.peek(HoldReason) { - Ok(Self::HoldReason(input.parse()?)) - } else if lookahead.peek(LockId) { - Ok(Self::LockId(input.parse()?)) - } else if lookahead.peek(SlashReason) { - Ok(Self::SlashReason(input.parse()?)) - } else if lookahead.peek(Task) { - Ok(Self::Task(input.parse()?)) - } else { - Err(lookahead.error()) - } - } - } - - impl std::fmt::Display for CompositeKeyword { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use CompositeKeyword::*; - write!( - f, - "{}", - match self { - FreezeReason(_) => "FreezeReason", - HoldReason(_) => "HoldReason", - Task(_) => "Task", - LockId(_) => "LockId", - SlashReason(_) => "SlashReason", - } - ) - } - } + use super::*; + + syn::custom_keyword!(FreezeReason); + syn::custom_keyword!(HoldReason); + syn::custom_keyword!(LockId); + syn::custom_keyword!(SlashReason); + syn::custom_keyword!(Task); + + pub enum CompositeKeyword { + FreezeReason(FreezeReason), + HoldReason(HoldReason), + LockId(LockId), + SlashReason(SlashReason), + Task(Task), + } + + impl ToTokens for CompositeKeyword { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + use CompositeKeyword::*; + match self { + FreezeReason(inner) => inner.to_tokens(tokens), + HoldReason(inner) => inner.to_tokens(tokens), + LockId(inner) => inner.to_tokens(tokens), + SlashReason(inner) => inner.to_tokens(tokens), + Task(inner) => inner.to_tokens(tokens), + } + } + } + + impl syn::parse::Parse for CompositeKeyword { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let lookahead = input.lookahead1(); + if lookahead.peek(FreezeReason) { + Ok(Self::FreezeReason(input.parse()?)) + } else if lookahead.peek(HoldReason) { + Ok(Self::HoldReason(input.parse()?)) + } else if lookahead.peek(LockId) { + Ok(Self::LockId(input.parse()?)) + } else if lookahead.peek(SlashReason) { + Ok(Self::SlashReason(input.parse()?)) + } else if lookahead.peek(Task) { + Ok(Self::Task(input.parse()?)) + } else { + Err(lookahead.error()) + } + } + } + + impl std::fmt::Display for CompositeKeyword { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use CompositeKeyword::*; + write!( + f, + "{}", + match self { + FreezeReason(_) => "FreezeReason", + HoldReason(_) => "HoldReason", + Task(_) => "Task", + LockId(_) => "LockId", + SlashReason(_) => "SlashReason", + } + ) + } + } } pub struct CompositeDef { - /// The index of the CompositeDef item in the pallet module. - pub index: usize, - /// The composite keyword used (contains span). - pub composite_keyword: keyword::CompositeKeyword, - /// Name of the associated type. - pub ident: syn::Ident, - /// Type parameters and where clause attached to a declaration of the pallet::composite_enum. - pub generics: syn::Generics, - /// The span of the pallet::composite_enum attribute. - pub attr_span: proc_macro2::Span, - /// Variant count of the pallet::composite_enum. - pub variant_count: u32, + /// The index of the CompositeDef item in the pallet module. + pub index: usize, + /// The composite keyword used (contains span). + pub composite_keyword: keyword::CompositeKeyword, + /// Name of the associated type. + pub ident: syn::Ident, + /// Type parameters and where clause attached to a declaration of the pallet::composite_enum. + pub generics: syn::Generics, + /// The span of the pallet::composite_enum attribute. + pub attr_span: proc_macro2::Span, + /// Variant count of the pallet::composite_enum. + pub variant_count: u32, } impl CompositeDef { - pub fn try_from( - attr_span: proc_macro2::Span, - index: usize, - scrate: &syn::Path, - item: &mut syn::Item, - ) -> syn::Result { - let item = if let syn::Item::Enum(item) = item { - // check variants: composite enums support only field-less enum variants. This is - // because fields can introduce too many possibilities, making it challenging to compute - // a fixed variant count. - for variant in &item.variants { - match variant.fields { - syn::Fields::Named(_) | syn::Fields::Unnamed(_) => { - return Err(syn::Error::new( - variant.ident.span(), - "The composite enum does not support variants with fields!", - )) - } - syn::Fields::Unit => (), - } - } - item - } else { - return Err(syn::Error::new( - item.span(), - "Invalid pallet::composite_enum, expected enum item", - )); - }; - - if !matches!(item.vis, syn::Visibility::Public(_)) { - let msg = format!( - "Invalid pallet::composite_enum, `{}` must be public", - item.ident - ); - return Err(syn::Error::new(item.span(), msg)); - } - - let has_instance = if item.generics.params.first().is_some() { - helper::check_config_def_gen(&item.generics, item.ident.span())?; - true - } else { - false - }; - - let has_derive_attr = item.attrs.iter().any(|attr| { - if let syn::Meta::List(syn::MetaList { path, .. }) = &attr.meta { - path.get_ident() - .map(|ident| ident == "derive") - .unwrap_or(false) - } else { - false - } - }); - - if !has_derive_attr { - let derive_attr: syn::Attribute = syn::parse_quote! { - #[derive( - Copy, Clone, Eq, PartialEq, - #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, - #scrate::__private::scale_info::TypeInfo, - #scrate::__private::RuntimeDebug, - )] - }; - item.attrs.push(derive_attr); - } - - if has_instance { - item.attrs.push(syn::parse_quote! { - #[scale_info(skip_type_params(I))] - }); - - item.variants.push(syn::parse_quote! { - #[doc(hidden)] - #[codec(skip)] - __Ignore( - ::core::marker::PhantomData, - ) - }); - } - - let composite_keyword = - syn::parse2::(item.ident.to_token_stream())?; - - Ok(CompositeDef { - index, - composite_keyword, - attr_span, - generics: item.generics.clone(), - variant_count: item.variants.len() as u32, - ident: item.ident.clone(), - }) - } + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + scrate: &syn::Path, + item: &mut syn::Item, + ) -> syn::Result { + let item = if let syn::Item::Enum(item) = item { + // check variants: composite enums support only field-less enum variants. This is + // because fields can introduce too many possibilities, making it challenging to compute + // a fixed variant count. + for variant in &item.variants { + match variant.fields { + syn::Fields::Named(_) | syn::Fields::Unnamed(_) => + return Err(syn::Error::new( + variant.ident.span(), + "The composite enum does not support variants with fields!", + )), + syn::Fields::Unit => (), + } + } + item + } else { + return Err(syn::Error::new( + item.span(), + "Invalid pallet::composite_enum, expected enum item", + )) + }; + + if !matches!(item.vis, syn::Visibility::Public(_)) { + let msg = format!("Invalid pallet::composite_enum, `{}` must be public", item.ident); + return Err(syn::Error::new(item.span(), msg)) + } + + let has_instance = if item.generics.params.first().is_some() { + helper::check_config_def_gen(&item.generics, item.ident.span())?; + true + } else { + false + }; + + let has_derive_attr = item.attrs.iter().any(|attr| { + if let syn::Meta::List(syn::MetaList { path, .. }) = &attr.meta { + path.get_ident().map(|ident| ident == "derive").unwrap_or(false) + } else { + false + } + }); + + if !has_derive_attr { + let derive_attr: syn::Attribute = syn::parse_quote! { + #[derive( + Copy, Clone, Eq, PartialEq, + #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, + #scrate::__private::scale_info::TypeInfo, + #scrate::__private::RuntimeDebug, + )] + }; + item.attrs.push(derive_attr); + } + + if has_instance { + item.attrs.push(syn::parse_quote! { + #[scale_info(skip_type_params(I))] + }); + + item.variants.push(syn::parse_quote! { + #[doc(hidden)] + #[codec(skip)] + __Ignore( + ::core::marker::PhantomData, + ) + }); + } + + let composite_keyword = + syn::parse2::(item.ident.to_token_stream())?; + + Ok(CompositeDef { + index, + composite_keyword, + attr_span, + generics: item.generics.clone(), + variant_count: item.variants.len() as u32, + ident: item.ident.clone(), + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/config.rs b/support/procedural-fork/src/pallet/parse/config.rs index 9ecdbddc3..fbab92db1 100644 --- a/support/procedural-fork/src/pallet/parse/config.rs +++ b/support/procedural-fork/src/pallet/parse/config.rs @@ -22,592 +22,569 @@ use syn::{spanned::Spanned, token, Token}; /// List of additional token to be used for parsing. mod keyword { - syn::custom_keyword!(Config); - syn::custom_keyword!(From); - syn::custom_keyword!(T); - syn::custom_keyword!(I); - syn::custom_keyword!(config); - syn::custom_keyword!(pallet); - syn::custom_keyword!(IsType); - syn::custom_keyword!(RuntimeEvent); - syn::custom_keyword!(Event); - syn::custom_keyword!(frame_system); - syn::custom_keyword!(disable_frame_system_supertrait_check); - syn::custom_keyword!(no_default); - syn::custom_keyword!(no_default_bounds); - syn::custom_keyword!(constant); + syn::custom_keyword!(Config); + syn::custom_keyword!(From); + syn::custom_keyword!(T); + syn::custom_keyword!(I); + syn::custom_keyword!(config); + syn::custom_keyword!(pallet); + syn::custom_keyword!(IsType); + syn::custom_keyword!(RuntimeEvent); + syn::custom_keyword!(Event); + syn::custom_keyword!(frame_system); + syn::custom_keyword!(disable_frame_system_supertrait_check); + syn::custom_keyword!(no_default); + syn::custom_keyword!(no_default_bounds); + syn::custom_keyword!(constant); } #[derive(Default)] pub struct DefaultTrait { - /// A bool for each sub-trait item indicates whether the item has - /// `#[pallet::no_default_bounds]` attached to it. If true, the item will not have any bounds - /// in the generated default sub-trait. - pub items: Vec<(syn::TraitItem, bool)>, - pub has_system: bool, + /// A bool for each sub-trait item indicates whether the item has + /// `#[pallet::no_default_bounds]` attached to it. If true, the item will not have any bounds + /// in the generated default sub-trait. + pub items: Vec<(syn::TraitItem, bool)>, + pub has_system: bool, } /// Input definition for the pallet config. pub struct ConfigDef { - /// The index of item in pallet module. - pub index: usize, - /// Whether the trait has instance (i.e. define with `Config`) - pub has_instance: bool, - /// Const associated type. - pub consts_metadata: Vec, - /// Whether the trait has the associated type `Event`, note that those bounds are - /// checked: - /// * `IsType::RuntimeEvent` - /// * `From` or `From>` or `From>` - pub has_event_type: bool, - /// The where clause on trait definition but modified so `Self` is `T`. - pub where_clause: Option, - /// The span of the pallet::config attribute. - pub attr_span: proc_macro2::Span, - /// Whether a default sub-trait should be generated. - /// - /// Contains default sub-trait items (instantiated by `#[pallet::config(with_default)]`). - /// Vec will be empty if `#[pallet::config(with_default)]` is not specified or if there are - /// no trait items. - pub default_sub_trait: Option, + /// The index of item in pallet module. + pub index: usize, + /// Whether the trait has instance (i.e. define with `Config`) + pub has_instance: bool, + /// Const associated type. + pub consts_metadata: Vec, + /// Whether the trait has the associated type `Event`, note that those bounds are + /// checked: + /// * `IsType::RuntimeEvent` + /// * `From` or `From>` or `From>` + pub has_event_type: bool, + /// The where clause on trait definition but modified so `Self` is `T`. + pub where_clause: Option, + /// The span of the pallet::config attribute. + pub attr_span: proc_macro2::Span, + /// Whether a default sub-trait should be generated. + /// + /// Contains default sub-trait items (instantiated by `#[pallet::config(with_default)]`). + /// Vec will be empty if `#[pallet::config(with_default)]` is not specified or if there are + /// no trait items. + pub default_sub_trait: Option, } /// Input definition for a constant in pallet config. pub struct ConstMetadataDef { - /// Name of the associated type. - pub ident: syn::Ident, - /// The type in Get, e.g. `u32` in `type Foo: Get;`, but `Self` is replaced by `T` - pub type_: syn::Type, - /// The doc associated - pub doc: Vec, + /// Name of the associated type. + pub ident: syn::Ident, + /// The type in Get, e.g. `u32` in `type Foo: Get;`, but `Self` is replaced by `T` + pub type_: syn::Type, + /// The doc associated + pub doc: Vec, } impl TryFrom<&syn::TraitItemType> for ConstMetadataDef { - type Error = syn::Error; - - fn try_from(trait_ty: &syn::TraitItemType) -> Result { - let err = |span, msg| { - syn::Error::new( - span, - format!("Invalid usage of `#[pallet::constant]`: {}", msg), - ) - }; - let doc = get_doc_literals(&trait_ty.attrs); - let ident = trait_ty.ident.clone(); - let bound = trait_ty - .bounds - .iter() - .find_map(|b| { - if let syn::TypeParamBound::Trait(tb) = b { - tb.path - .segments - .last() - .and_then(|s| if s.ident == "Get" { Some(s) } else { None }) - } else { - None - } - }) - .ok_or_else(|| err(trait_ty.span(), "`Get` trait bound not found"))?; - let type_arg = if let syn::PathArguments::AngleBracketed(ref ab) = bound.arguments { - if ab.args.len() == 1 { - if let syn::GenericArgument::Type(ref ty) = ab.args[0] { - Ok(ty) - } else { - Err(err(ab.args[0].span(), "Expected a type argument")) - } - } else { - Err(err(bound.span(), "Expected a single type argument")) - } - } else { - Err(err(bound.span(), "Expected trait generic args")) - }?; - let type_ = syn::parse2::(replace_self_by_t(type_arg.to_token_stream())) - .expect("Internal error: replacing `Self` by `T` should result in valid type"); - - Ok(Self { ident, type_, doc }) - } + type Error = syn::Error; + + fn try_from(trait_ty: &syn::TraitItemType) -> Result { + let err = |span, msg| { + syn::Error::new(span, format!("Invalid usage of `#[pallet::constant]`: {}", msg)) + }; + let doc = get_doc_literals(&trait_ty.attrs); + let ident = trait_ty.ident.clone(); + let bound = trait_ty + .bounds + .iter() + .find_map(|b| { + if let syn::TypeParamBound::Trait(tb) = b { + tb.path + .segments + .last() + .and_then(|s| if s.ident == "Get" { Some(s) } else { None }) + } else { + None + } + }) + .ok_or_else(|| err(trait_ty.span(), "`Get` trait bound not found"))?; + let type_arg = if let syn::PathArguments::AngleBracketed(ref ab) = bound.arguments { + if ab.args.len() == 1 { + if let syn::GenericArgument::Type(ref ty) = ab.args[0] { + Ok(ty) + } else { + Err(err(ab.args[0].span(), "Expected a type argument")) + } + } else { + Err(err(bound.span(), "Expected a single type argument")) + } + } else { + Err(err(bound.span(), "Expected trait generic args")) + }?; + let type_ = syn::parse2::(replace_self_by_t(type_arg.to_token_stream())) + .expect("Internal error: replacing `Self` by `T` should result in valid type"); + + Ok(Self { ident, type_, doc }) + } } /// Parse for `#[pallet::disable_frame_system_supertrait_check]` pub struct DisableFrameSystemSupertraitCheck; impl syn::parse::Parse for DisableFrameSystemSupertraitCheck { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - let content; - syn::bracketed!(content in input); - content.parse::()?; - content.parse::()?; - - content.parse::()?; - Ok(Self) - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + content.parse::()?; + Ok(Self) + } } /// Parsing for the `typ` portion of `PalletAttr` #[derive(derive_syn_parse::Parse, PartialEq, Eq)] pub enum PalletAttrType { - #[peek(keyword::no_default, name = "no_default")] - NoDefault(keyword::no_default), - #[peek(keyword::no_default_bounds, name = "no_default_bounds")] - NoBounds(keyword::no_default_bounds), - #[peek(keyword::constant, name = "constant")] - Constant(keyword::constant), + #[peek(keyword::no_default, name = "no_default")] + NoDefault(keyword::no_default), + #[peek(keyword::no_default_bounds, name = "no_default_bounds")] + NoBounds(keyword::no_default_bounds), + #[peek(keyword::constant, name = "constant")] + Constant(keyword::constant), } /// Parsing for `#[pallet::X]` #[derive(derive_syn_parse::Parse)] pub struct PalletAttr { - _pound: Token![#], - #[bracket] - _bracket: token::Bracket, - #[inside(_bracket)] - _pallet: keyword::pallet, - #[prefix(Token![::] in _bracket)] - #[inside(_bracket)] - typ: PalletAttrType, + _pound: Token![#], + #[bracket] + _bracket: token::Bracket, + #[inside(_bracket)] + _pallet: keyword::pallet, + #[prefix(Token![::] in _bracket)] + #[inside(_bracket)] + typ: PalletAttrType, } /// Parse for `IsType<::RuntimeEvent>` and retrieve `$path` pub struct IsTypeBoundEventParse(syn::Path); impl syn::parse::Parse for IsTypeBoundEventParse { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - let config_path = input.parse::()?; - input.parse::]>()?; - input.parse::()?; - input.parse::()?; - input.parse::]>()?; - - Ok(Self(config_path)) - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + let config_path = input.parse::()?; + input.parse::]>()?; + input.parse::()?; + input.parse::()?; + input.parse::]>()?; + + Ok(Self(config_path)) + } } /// Parse for `From` or `From>` or `From>` pub struct FromEventParse { - is_generic: bool, - has_instance: bool, + is_generic: bool, + has_instance: bool, } impl syn::parse::Parse for FromEventParse { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let mut is_generic = false; - let mut has_instance = false; - - input.parse::()?; - input.parse::()?; - input.parse::()?; - if input.peek(syn::Token![<]) { - is_generic = true; - input.parse::()?; - input.parse::()?; - if input.peek(syn::Token![,]) { - input.parse::()?; - input.parse::()?; - has_instance = true; - } - input.parse::]>()?; - } - input.parse::]>()?; - - Ok(Self { - is_generic, - has_instance, - }) - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut is_generic = false; + let mut has_instance = false; + + input.parse::()?; + input.parse::()?; + input.parse::()?; + if input.peek(syn::Token![<]) { + is_generic = true; + input.parse::()?; + input.parse::()?; + if input.peek(syn::Token![,]) { + input.parse::()?; + input.parse::()?; + has_instance = true; + } + input.parse::]>()?; + } + input.parse::]>()?; + + Ok(Self { is_generic, has_instance }) + } } /// Check if trait_item is `type RuntimeEvent`, if so checks its bounds are those expected. /// (Event type is reserved type) fn check_event_type( - frame_system: &syn::Path, - trait_item: &syn::TraitItem, - trait_has_instance: bool, + frame_system: &syn::Path, + trait_item: &syn::TraitItem, + trait_has_instance: bool, ) -> syn::Result { - if let syn::TraitItem::Type(type_) = trait_item { - if type_.ident == "RuntimeEvent" { - // Check event has no generics - if !type_.generics.params.is_empty() || type_.generics.where_clause.is_some() { - let msg = "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must have\ + if let syn::TraitItem::Type(type_) = trait_item { + if type_.ident == "RuntimeEvent" { + // Check event has no generics + if !type_.generics.params.is_empty() || type_.generics.where_clause.is_some() { + let msg = "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must have\ no generics nor where_clause"; - return Err(syn::Error::new(trait_item.span(), msg)); - } + return Err(syn::Error::new(trait_item.span(), msg)) + } - // Check bound contains IsType and From - let has_is_type_bound = type_.bounds.iter().any(|s| { - syn::parse2::(s.to_token_stream()) - .map_or(false, |b| has_expected_system_config(b.0, frame_system)) - }); + // Check bound contains IsType and From + let has_is_type_bound = type_.bounds.iter().any(|s| { + syn::parse2::(s.to_token_stream()) + .map_or(false, |b| has_expected_system_config(b.0, frame_system)) + }); - if !has_is_type_bound { - let msg = "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must \ + if !has_is_type_bound { + let msg = "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must \ bound: `IsType<::RuntimeEvent>`".to_string(); - return Err(syn::Error::new(type_.span(), msg)); - } - - let from_event_bound = type_ - .bounds - .iter() - .find_map(|s| syn::parse2::(s.to_token_stream()).ok()); - - let from_event_bound = if let Some(b) = from_event_bound { - b - } else { - let msg = "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must \ + return Err(syn::Error::new(type_.span(), msg)) + } + + let from_event_bound = type_ + .bounds + .iter() + .find_map(|s| syn::parse2::(s.to_token_stream()).ok()); + + let from_event_bound = if let Some(b) = from_event_bound { + b + } else { + let msg = "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must \ bound: `From` or `From>` or `From>`"; - return Err(syn::Error::new(type_.span(), msg)); - }; + return Err(syn::Error::new(type_.span(), msg)) + }; - if from_event_bound.is_generic && (from_event_bound.has_instance != trait_has_instance) - { - let msg = "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` bounds inconsistent \ + if from_event_bound.is_generic && (from_event_bound.has_instance != trait_has_instance) + { + let msg = "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` bounds inconsistent \ `From`. Config and generic Event must be both with instance or \ without instance"; - return Err(syn::Error::new(type_.span(), msg)); - } - - Ok(true) - } else { - Ok(false) - } - } else { - Ok(false) - } + return Err(syn::Error::new(type_.span(), msg)) + } + + Ok(true) + } else { + Ok(false) + } + } else { + Ok(false) + } } /// Check that the path to `frame_system::Config` is valid, this is that the path is just /// `frame_system::Config` or when using the `frame` crate it is `frame::xyz::frame_system::Config`. fn has_expected_system_config(path: syn::Path, frame_system: &syn::Path) -> bool { - // Check if `frame_system` is actually 'frame_system'. - if path.segments.iter().all(|s| s.ident != "frame_system") { - return false; - } - - let mut expected_system_config = match ( - is_using_frame_crate(&path), - is_using_frame_crate(frame_system), - ) { - (true, false) => - // We can't use the path to `frame_system` from `frame` if `frame_system` is not being - // in scope through `frame`. - { - return false - } - (false, true) => - // We know that the only valid frame_system path is one that is `frame_system`, as - // `frame` re-exports it as such. - { - syn::parse2::(quote::quote!(frame_system)).expect("is a valid path; qed") - } - (_, _) => - // They are either both `frame_system` or both `frame::xyz::frame_system`. - { - frame_system.clone() - } - }; - - expected_system_config - .segments - .push(syn::PathSegment::from(syn::Ident::new( - "Config", - path.span(), - ))); - - // the parse path might be something like `frame_system::Config<...>`, so we - // only compare the idents along the path. - expected_system_config - .segments - .into_iter() - .map(|ps| ps.ident) - .collect::>() - == path - .segments - .into_iter() - .map(|ps| ps.ident) - .collect::>() + // Check if `frame_system` is actually 'frame_system'. + if path.segments.iter().all(|s| s.ident != "frame_system") { + return false + } + + let mut expected_system_config = + match (is_using_frame_crate(&path), is_using_frame_crate(&frame_system)) { + (true, false) => + // We can't use the path to `frame_system` from `frame` if `frame_system` is not being + // in scope through `frame`. + return false, + (false, true) => + // We know that the only valid frame_system path is one that is `frame_system`, as + // `frame` re-exports it as such. + syn::parse2::(quote::quote!(frame_system)).expect("is a valid path; qed"), + (_, _) => + // They are either both `frame_system` or both `frame::xyz::frame_system`. + frame_system.clone(), + }; + + expected_system_config + .segments + .push(syn::PathSegment::from(syn::Ident::new("Config", path.span()))); + + // the parse path might be something like `frame_system::Config<...>`, so we + // only compare the idents along the path. + expected_system_config + .segments + .into_iter() + .map(|ps| ps.ident) + .collect::>() == + path.segments.into_iter().map(|ps| ps.ident).collect::>() } /// Replace ident `Self` by `T` pub fn replace_self_by_t(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream { - input - .into_iter() - .map(|token_tree| match token_tree { - proc_macro2::TokenTree::Group(group) => { - proc_macro2::Group::new(group.delimiter(), replace_self_by_t(group.stream())).into() - } - proc_macro2::TokenTree::Ident(ident) if ident == "Self" => { - proc_macro2::Ident::new("T", ident.span()).into() - } - other => other, - }) - .collect() + input + .into_iter() + .map(|token_tree| match token_tree { + proc_macro2::TokenTree::Group(group) => + proc_macro2::Group::new(group.delimiter(), replace_self_by_t(group.stream())).into(), + proc_macro2::TokenTree::Ident(ident) if ident == "Self" => + proc_macro2::Ident::new("T", ident.span()).into(), + other => other, + }) + .collect() } impl ConfigDef { - pub fn try_from( - frame_system: &syn::Path, - attr_span: proc_macro2::Span, - index: usize, - item: &mut syn::Item, - enable_default: bool, - ) -> syn::Result { - let item = if let syn::Item::Trait(item) = item { - item - } else { - let msg = "Invalid pallet::config, expected trait definition"; - return Err(syn::Error::new(item.span(), msg)); - }; - - if !matches!(item.vis, syn::Visibility::Public(_)) { - let msg = "Invalid pallet::config, trait must be public"; - return Err(syn::Error::new(item.span(), msg)); - } - - syn::parse2::(item.ident.to_token_stream())?; - - let where_clause = { - let stream = replace_self_by_t(item.generics.where_clause.to_token_stream()); - syn::parse2::>(stream).expect( - "Internal error: replacing `Self` by `T` should result in valid where + pub fn try_from( + frame_system: &syn::Path, + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + enable_default: bool, + ) -> syn::Result { + let item = if let syn::Item::Trait(item) = item { + item + } else { + let msg = "Invalid pallet::config, expected trait definition"; + return Err(syn::Error::new(item.span(), msg)) + }; + + if !matches!(item.vis, syn::Visibility::Public(_)) { + let msg = "Invalid pallet::config, trait must be public"; + return Err(syn::Error::new(item.span(), msg)) + } + + syn::parse2::(item.ident.to_token_stream())?; + + let where_clause = { + let stream = replace_self_by_t(item.generics.where_clause.to_token_stream()); + syn::parse2::>(stream).expect( + "Internal error: replacing `Self` by `T` should result in valid where clause", - ) - }; - - if item.generics.params.len() > 1 { - let msg = "Invalid pallet::config, expected no more than one generic"; - return Err(syn::Error::new(item.generics.params[2].span(), msg)); - } - - let has_instance = if item.generics.params.first().is_some() { - helper::check_config_def_gen(&item.generics, item.ident.span())?; - true - } else { - false - }; - - let has_frame_system_supertrait = item.supertraits.iter().any(|s| { - syn::parse2::(s.to_token_stream()) - .map_or(false, |b| has_expected_system_config(b, frame_system)) - }); - - let mut has_event_type = false; - let mut consts_metadata = vec![]; - let mut default_sub_trait = if enable_default { - Some(DefaultTrait { - items: Default::default(), - has_system: has_frame_system_supertrait, - }) - } else { - None - }; - for trait_item in &mut item.items { - let is_event = check_event_type(frame_system, trait_item, has_instance)?; - has_event_type = has_event_type || is_event; - - let mut already_no_default = false; - let mut already_constant = false; - let mut already_no_default_bounds = false; - - while let Ok(Some(pallet_attr)) = - helper::take_first_item_pallet_attr::(trait_item) - { - match (pallet_attr.typ, &trait_item) { - (PalletAttrType::Constant(_), syn::TraitItem::Type(ref typ)) => { - if already_constant { - return Err(syn::Error::new( - pallet_attr._bracket.span.join(), - "Duplicate #[pallet::constant] attribute not allowed.", - )); - } - already_constant = true; - consts_metadata.push(ConstMetadataDef::try_from(typ)?); - } - (PalletAttrType::Constant(_), _) => { - return Err(syn::Error::new( - trait_item.span(), - "Invalid #[pallet::constant] in #[pallet::config], expected type item", - )) - } - (PalletAttrType::NoDefault(_), _) => { - if !enable_default { - return Err(syn::Error::new( + ) + }; + + if item.generics.params.len() > 1 { + let msg = "Invalid pallet::config, expected no more than one generic"; + return Err(syn::Error::new(item.generics.params[2].span(), msg)) + } + + let has_instance = if item.generics.params.first().is_some() { + helper::check_config_def_gen(&item.generics, item.ident.span())?; + true + } else { + false + }; + + let has_frame_system_supertrait = item.supertraits.iter().any(|s| { + syn::parse2::(s.to_token_stream()) + .map_or(false, |b| has_expected_system_config(b, frame_system)) + }); + + let mut has_event_type = false; + let mut consts_metadata = vec![]; + let mut default_sub_trait = if enable_default { + Some(DefaultTrait { + items: Default::default(), + has_system: has_frame_system_supertrait, + }) + } else { + None + }; + for trait_item in &mut item.items { + let is_event = check_event_type(frame_system, trait_item, has_instance)?; + has_event_type = has_event_type || is_event; + + let mut already_no_default = false; + let mut already_constant = false; + let mut already_no_default_bounds = false; + + while let Ok(Some(pallet_attr)) = + helper::take_first_item_pallet_attr::(trait_item) + { + match (pallet_attr.typ, &trait_item) { + (PalletAttrType::Constant(_), syn::TraitItem::Type(ref typ)) => { + if already_constant { + return Err(syn::Error::new( + pallet_attr._bracket.span.join(), + "Duplicate #[pallet::constant] attribute not allowed.", + )) + } + already_constant = true; + consts_metadata.push(ConstMetadataDef::try_from(typ)?); + }, + (PalletAttrType::Constant(_), _) => + return Err(syn::Error::new( + trait_item.span(), + "Invalid #[pallet::constant] in #[pallet::config], expected type item", + )), + (PalletAttrType::NoDefault(_), _) => { + if !enable_default { + return Err(syn::Error::new( pallet_attr._bracket.span.join(), "`#[pallet:no_default]` can only be used if `#[pallet::config(with_default)]` \ has been specified" - )); - } - if already_no_default { - return Err(syn::Error::new( - pallet_attr._bracket.span.join(), - "Duplicate #[pallet::no_default] attribute not allowed.", - )); - } - - already_no_default = true; - } - (PalletAttrType::NoBounds(_), _) => { - if !enable_default { - return Err(syn::Error::new( + )) + } + if already_no_default { + return Err(syn::Error::new( + pallet_attr._bracket.span.join(), + "Duplicate #[pallet::no_default] attribute not allowed.", + )) + } + + already_no_default = true; + }, + (PalletAttrType::NoBounds(_), _) => { + if !enable_default { + return Err(syn::Error::new( pallet_attr._bracket.span.join(), "`#[pallet:no_default_bounds]` can only be used if `#[pallet::config(with_default)]` \ has been specified" - )); - } - if already_no_default_bounds { - return Err(syn::Error::new( - pallet_attr._bracket.span.join(), - "Duplicate #[pallet::no_default_bounds] attribute not allowed.", - )); - } - already_no_default_bounds = true; - } - } - } - - if !already_no_default && enable_default { - default_sub_trait - .as_mut() - .expect("is 'Some(_)' if 'enable_default'; qed") - .items - .push((trait_item.clone(), already_no_default_bounds)); - } - } - - let attr: Option = - helper::take_first_item_pallet_attr(&mut item.attrs)?; - let disable_system_supertrait_check = attr.is_some(); - - if !has_frame_system_supertrait && !disable_system_supertrait_check { - let found = if item.supertraits.is_empty() { - "none".to_string() - } else { - let mut found = item.supertraits.iter().fold(String::new(), |acc, s| { - format!("{}`{}`, ", acc, quote::quote!(#s)) - }); - found.pop(); - found.pop(); - found - }; - - let msg = format!( - "Invalid pallet::trait, expected explicit `{}::Config` as supertrait, \ + )) + } + if already_no_default_bounds { + return Err(syn::Error::new( + pallet_attr._bracket.span.join(), + "Duplicate #[pallet::no_default_bounds] attribute not allowed.", + )) + } + already_no_default_bounds = true; + }, + } + } + + if !already_no_default && enable_default { + default_sub_trait + .as_mut() + .expect("is 'Some(_)' if 'enable_default'; qed") + .items + .push((trait_item.clone(), already_no_default_bounds)); + } + } + + let attr: Option = + helper::take_first_item_pallet_attr(&mut item.attrs)?; + let disable_system_supertrait_check = attr.is_some(); + + if !has_frame_system_supertrait && !disable_system_supertrait_check { + let found = if item.supertraits.is_empty() { + "none".to_string() + } else { + let mut found = item + .supertraits + .iter() + .fold(String::new(), |acc, s| format!("{}`{}`, ", acc, quote::quote!(#s))); + found.pop(); + found.pop(); + found + }; + + let msg = format!( + "Invalid pallet::trait, expected explicit `{}::Config` as supertrait, \ found {}. \ (try `pub trait Config: frame_system::Config {{ ...` or \ `pub trait Config: frame_system::Config {{ ...`). \ To disable this check, use `#[pallet::disable_frame_system_supertrait_check]`", - frame_system.to_token_stream(), - found, - ); - return Err(syn::Error::new(item.span(), msg)); - } - - Ok(Self { - index, - has_instance, - consts_metadata, - has_event_type, - where_clause, - attr_span, - default_sub_trait, - }) - } + frame_system.to_token_stream(), + found, + ); + return Err(syn::Error::new(item.span(), msg)) + } + + Ok(Self { + index, + has_instance, + consts_metadata, + has_event_type, + where_clause, + attr_span, + default_sub_trait, + }) + } } #[cfg(test)] mod tests { - use super::*; - #[test] - fn has_expected_system_config_works() { - let frame_system = syn::parse2::(quote::quote!(frame_system)).unwrap(); - let path = syn::parse2::(quote::quote!(frame_system::Config)).unwrap(); - assert!(has_expected_system_config(path, &frame_system)); - } - - #[test] - fn has_expected_system_config_works_with_assoc_type() { - let frame_system = syn::parse2::(quote::quote!(frame_system)).unwrap(); - let path = - syn::parse2::(quote::quote!(frame_system::Config)) - .unwrap(); - assert!(has_expected_system_config(path, &frame_system)); - } - - #[test] - fn has_expected_system_config_works_with_frame() { - let frame_system = - syn::parse2::(quote::quote!(frame::deps::frame_system)).unwrap(); - let path = syn::parse2::(quote::quote!(frame_system::Config)).unwrap(); - assert!(has_expected_system_config(path, &frame_system)); - } - - #[test] - fn has_expected_system_config_works_with_frame_full_path() { - let frame_system = - syn::parse2::(quote::quote!(frame::deps::frame_system)).unwrap(); - let path = - syn::parse2::(quote::quote!(frame::deps::frame_system::Config)).unwrap(); - assert!(has_expected_system_config(path, &frame_system)); - } - - #[test] - fn has_expected_system_config_works_with_other_frame_full_path() { - let frame_system = - syn::parse2::(quote::quote!(frame::xyz::frame_system)).unwrap(); - let path = - syn::parse2::(quote::quote!(frame::xyz::frame_system::Config)).unwrap(); - assert!(has_expected_system_config(path, &frame_system)); - } - - #[test] - fn has_expected_system_config_does_not_works_with_mixed_frame_full_path() { - let frame_system = - syn::parse2::(quote::quote!(frame::xyz::frame_system)).unwrap(); - let path = - syn::parse2::(quote::quote!(frame::deps::frame_system::Config)).unwrap(); - assert!(!has_expected_system_config(path, &frame_system)); - } - - #[test] - fn has_expected_system_config_does_not_works_with_other_mixed_frame_full_path() { - let frame_system = - syn::parse2::(quote::quote!(frame::deps::frame_system)).unwrap(); - let path = - syn::parse2::(quote::quote!(frame::xyz::frame_system::Config)).unwrap(); - assert!(!has_expected_system_config(path, &frame_system)); - } - - #[test] - fn has_expected_system_config_does_not_work_with_frame_full_path_if_not_frame_crate() { - let frame_system = syn::parse2::(quote::quote!(frame_system)).unwrap(); - let path = - syn::parse2::(quote::quote!(frame::deps::frame_system::Config)).unwrap(); - assert!(!has_expected_system_config(path, &frame_system)); - } - - #[test] - fn has_expected_system_config_unexpected_frame_system() { - let frame_system = - syn::parse2::(quote::quote!(framez::deps::frame_system)).unwrap(); - let path = syn::parse2::(quote::quote!(frame_system::Config)).unwrap(); - assert!(!has_expected_system_config(path, &frame_system)); - } - - #[test] - fn has_expected_system_config_unexpected_path() { - let frame_system = syn::parse2::(quote::quote!(frame_system)).unwrap(); - let path = syn::parse2::(quote::quote!(frame_system::ConfigSystem)).unwrap(); - assert!(!has_expected_system_config(path, &frame_system)); - } - - #[test] - fn has_expected_system_config_not_frame_system() { - let frame_system = syn::parse2::(quote::quote!(something)).unwrap(); - let path = syn::parse2::(quote::quote!(something::Config)).unwrap(); - assert!(!has_expected_system_config(path, &frame_system)); - } + use super::*; + #[test] + fn has_expected_system_config_works() { + let frame_system = syn::parse2::(quote::quote!(frame_system)).unwrap(); + let path = syn::parse2::(quote::quote!(frame_system::Config)).unwrap(); + assert!(has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_works_with_assoc_type() { + let frame_system = syn::parse2::(quote::quote!(frame_system)).unwrap(); + let path = + syn::parse2::(quote::quote!(frame_system::Config)) + .unwrap(); + assert!(has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_works_with_frame() { + let frame_system = + syn::parse2::(quote::quote!(frame::deps::frame_system)).unwrap(); + let path = syn::parse2::(quote::quote!(frame_system::Config)).unwrap(); + assert!(has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_works_with_frame_full_path() { + let frame_system = + syn::parse2::(quote::quote!(frame::deps::frame_system)).unwrap(); + let path = + syn::parse2::(quote::quote!(frame::deps::frame_system::Config)).unwrap(); + assert!(has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_works_with_other_frame_full_path() { + let frame_system = + syn::parse2::(quote::quote!(frame::xyz::frame_system)).unwrap(); + let path = + syn::parse2::(quote::quote!(frame::xyz::frame_system::Config)).unwrap(); + assert!(has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_does_not_works_with_mixed_frame_full_path() { + let frame_system = + syn::parse2::(quote::quote!(frame::xyz::frame_system)).unwrap(); + let path = + syn::parse2::(quote::quote!(frame::deps::frame_system::Config)).unwrap(); + assert!(!has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_does_not_works_with_other_mixed_frame_full_path() { + let frame_system = + syn::parse2::(quote::quote!(frame::deps::frame_system)).unwrap(); + let path = + syn::parse2::(quote::quote!(frame::xyz::frame_system::Config)).unwrap(); + assert!(!has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_does_not_work_with_frame_full_path_if_not_frame_crate() { + let frame_system = syn::parse2::(quote::quote!(frame_system)).unwrap(); + let path = + syn::parse2::(quote::quote!(frame::deps::frame_system::Config)).unwrap(); + assert!(!has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_unexpected_frame_system() { + let frame_system = + syn::parse2::(quote::quote!(framez::deps::frame_system)).unwrap(); + let path = syn::parse2::(quote::quote!(frame_system::Config)).unwrap(); + assert!(!has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_unexpected_path() { + let frame_system = syn::parse2::(quote::quote!(frame_system)).unwrap(); + let path = syn::parse2::(quote::quote!(frame_system::ConfigSystem)).unwrap(); + assert!(!has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_not_frame_system() { + let frame_system = syn::parse2::(quote::quote!(something)).unwrap(); + let path = syn::parse2::(quote::quote!(something::Config)).unwrap(); + assert!(!has_expected_system_config(path, &frame_system)); + } } diff --git a/support/procedural-fork/src/pallet/parse/error.rs b/support/procedural-fork/src/pallet/parse/error.rs index e93e2113f..362df8d73 100644 --- a/support/procedural-fork/src/pallet/parse/error.rs +++ b/support/procedural-fork/src/pallet/parse/error.rs @@ -22,108 +22,94 @@ use syn::{spanned::Spanned, Fields}; /// List of additional token to be used for parsing. mod keyword { - syn::custom_keyword!(Error); + syn::custom_keyword!(Error); } /// Records information about the error enum variant field. pub struct VariantField { - /// Whether or not the field is named, i.e. whether it is a tuple variant or struct variant. - pub is_named: bool, + /// Whether or not the field is named, i.e. whether it is a tuple variant or struct variant. + pub is_named: bool, } /// Records information about the error enum variants. pub struct VariantDef { - /// The variant ident. - pub ident: syn::Ident, - /// The variant field, if any. - pub field: Option, - /// The variant doc literals. - pub docs: Vec, - /// The `cfg` attributes. - pub cfg_attrs: Vec, + /// The variant ident. + pub ident: syn::Ident, + /// The variant field, if any. + pub field: Option, + /// The variant doc literals. + pub docs: Vec, + /// The `cfg` attributes. + pub cfg_attrs: Vec, } /// This checks error declaration as a enum declaration with only variants without fields nor /// discriminant. pub struct ErrorDef { - /// The index of error item in pallet module. - pub index: usize, - /// Variant definitions. - pub variants: Vec, - /// A set of usage of instance, must be check for consistency with trait. - pub instances: Vec, - /// The keyword error used (contains span). - pub error: keyword::Error, - /// The span of the pallet::error attribute. - pub attr_span: proc_macro2::Span, + /// The index of error item in pallet module. + pub index: usize, + /// Variant definitions. + pub variants: Vec, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Vec, + /// The keyword error used (contains span). + pub error: keyword::Error, + /// The span of the pallet::error attribute. + pub attr_span: proc_macro2::Span, } impl ErrorDef { - pub fn try_from( - attr_span: proc_macro2::Span, - index: usize, - item: &mut syn::Item, - ) -> syn::Result { - let item = if let syn::Item::Enum(item) = item { - item - } else { - return Err(syn::Error::new( - item.span(), - "Invalid pallet::error, expected item enum", - )); - }; - if !matches!(item.vis, syn::Visibility::Public(_)) { - let msg = "Invalid pallet::error, `Error` must be public"; - return Err(syn::Error::new(item.span(), msg)); - } + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + ) -> syn::Result { + let item = if let syn::Item::Enum(item) = item { + item + } else { + return Err(syn::Error::new(item.span(), "Invalid pallet::error, expected item enum")) + }; + if !matches!(item.vis, syn::Visibility::Public(_)) { + let msg = "Invalid pallet::error, `Error` must be public"; + return Err(syn::Error::new(item.span(), msg)) + } - let instances = vec![helper::check_type_def_gen_no_bounds( - &item.generics, - item.ident.span(), - )?]; + let instances = + vec![helper::check_type_def_gen_no_bounds(&item.generics, item.ident.span())?]; - if item.generics.where_clause.is_some() { - let msg = "Invalid pallet::error, where clause is not allowed on pallet error item"; - return Err(syn::Error::new( - item.generics.where_clause.as_ref().unwrap().span(), - msg, - )); - } + if item.generics.where_clause.is_some() { + let msg = "Invalid pallet::error, where clause is not allowed on pallet error item"; + return Err(syn::Error::new(item.generics.where_clause.as_ref().unwrap().span(), msg)) + } - let error = syn::parse2::(item.ident.to_token_stream())?; + let error = syn::parse2::(item.ident.to_token_stream())?; - let variants = item - .variants - .iter() - .map(|variant| { - let field_ty = match &variant.fields { - Fields::Unit => None, - Fields::Named(_) => Some(VariantField { is_named: true }), - Fields::Unnamed(_) => Some(VariantField { is_named: false }), - }; - if variant.discriminant.is_some() { - let msg = "Invalid pallet::error, unexpected discriminant, discriminants \ + let variants = item + .variants + .iter() + .map(|variant| { + let field_ty = match &variant.fields { + Fields::Unit => None, + Fields::Named(_) => Some(VariantField { is_named: true }), + Fields::Unnamed(_) => Some(VariantField { is_named: false }), + }; + if variant.discriminant.is_some() { + let msg = "Invalid pallet::error, unexpected discriminant, discriminants \ are not supported"; - let span = variant.discriminant.as_ref().unwrap().0.span(); - return Err(syn::Error::new(span, msg)); - } - let cfg_attrs: Vec = helper::get_item_cfg_attrs(&variant.attrs); + let span = variant.discriminant.as_ref().unwrap().0.span(); + return Err(syn::Error::new(span, msg)) + } + let cfg_attrs: Vec = helper::get_item_cfg_attrs(&variant.attrs); - Ok(VariantDef { - ident: variant.ident.clone(), - field: field_ty, - docs: get_doc_literals(&variant.attrs), - cfg_attrs, - }) - }) - .collect::>()?; + Ok(VariantDef { + ident: variant.ident.clone(), + field: field_ty, + docs: get_doc_literals(&variant.attrs), + cfg_attrs, + }) + }) + .collect::>()?; - Ok(ErrorDef { - attr_span, - index, - variants, - instances, - error, - }) - } + Ok(ErrorDef { attr_span, index, variants, instances, error }) + } } diff --git a/support/procedural-fork/src/pallet/parse/event.rs b/support/procedural-fork/src/pallet/parse/event.rs index 6102dd31f..0fb8ee4f5 100644 --- a/support/procedural-fork/src/pallet/parse/event.rs +++ b/support/procedural-fork/src/pallet/parse/event.rs @@ -21,28 +21,28 @@ use syn::spanned::Spanned; /// List of additional token to be used for parsing. mod keyword { - syn::custom_keyword!(Event); - syn::custom_keyword!(pallet); - syn::custom_keyword!(generate_deposit); - syn::custom_keyword!(deposit_event); + syn::custom_keyword!(Event); + syn::custom_keyword!(pallet); + syn::custom_keyword!(generate_deposit); + syn::custom_keyword!(deposit_event); } /// Definition for pallet event enum. pub struct EventDef { - /// The index of event item in pallet module. - pub index: usize, - /// The keyword Event used (contains span). - pub event: keyword::Event, - /// A set of usage of instance, must be check for consistency with trait. - pub instances: Vec, - /// The kind of generic the type `Event` has. - pub gen_kind: super::GenericKind, - /// Whether the function `deposit_event` must be generated. - pub deposit_event: Option, - /// Where clause used in event definition. - pub where_clause: Option, - /// The span of the pallet::event attribute. - pub attr_span: proc_macro2::Span, + /// The index of event item in pallet module. + pub index: usize, + /// The keyword Event used (contains span). + pub event: keyword::Event, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Vec, + /// The kind of generic the type `Event` has. + pub gen_kind: super::GenericKind, + /// Whether the function `deposit_event` must be generated. + pub deposit_event: Option, + /// Where clause used in event definition. + pub where_clause: Option, + /// The span of the pallet::event attribute. + pub attr_span: proc_macro2::Span, } /// Attribute for a pallet's Event. @@ -50,110 +50,92 @@ pub struct EventDef { /// Syntax is: /// * `#[pallet::generate_deposit($vis fn deposit_event)]` pub struct PalletEventDepositAttr { - pub fn_vis: syn::Visibility, - // Span for the keyword deposit_event - pub fn_span: proc_macro2::Span, - // Span of the attribute - pub span: proc_macro2::Span, + pub fn_vis: syn::Visibility, + // Span for the keyword deposit_event + pub fn_span: proc_macro2::Span, + // Span of the attribute + pub span: proc_macro2::Span, } impl syn::parse::Parse for PalletEventDepositAttr { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - let content; - syn::bracketed!(content in input); - content.parse::()?; - content.parse::()?; - - let span = content.parse::()?.span(); - let generate_content; - syn::parenthesized!(generate_content in content); - let fn_vis = generate_content.parse::()?; - generate_content.parse::()?; - let fn_span = generate_content.parse::()?.span(); - - Ok(PalletEventDepositAttr { - fn_vis, - span, - fn_span, - }) - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + let span = content.parse::()?.span(); + let generate_content; + syn::parenthesized!(generate_content in content); + let fn_vis = generate_content.parse::()?; + generate_content.parse::()?; + let fn_span = generate_content.parse::()?.span(); + + Ok(PalletEventDepositAttr { fn_vis, span, fn_span }) + } } struct PalletEventAttrInfo { - deposit_event: Option, + deposit_event: Option, } impl PalletEventAttrInfo { - fn from_attrs(attrs: Vec) -> syn::Result { - let mut deposit_event = None; - for attr in attrs { - if deposit_event.is_none() { - deposit_event = Some(attr) - } else { - return Err(syn::Error::new(attr.span, "Duplicate attribute")); - } - } - - Ok(PalletEventAttrInfo { deposit_event }) - } + fn from_attrs(attrs: Vec) -> syn::Result { + let mut deposit_event = None; + for attr in attrs { + if deposit_event.is_none() { + deposit_event = Some(attr) + } else { + return Err(syn::Error::new(attr.span, "Duplicate attribute")) + } + } + + Ok(PalletEventAttrInfo { deposit_event }) + } } impl EventDef { - pub fn try_from( - attr_span: proc_macro2::Span, - index: usize, - item: &mut syn::Item, - ) -> syn::Result { - let item = if let syn::Item::Enum(item) = item { - item - } else { - return Err(syn::Error::new( - item.span(), - "Invalid pallet::event, expected enum item", - )); - }; - - let event_attrs: Vec = - helper::take_item_pallet_attrs(&mut item.attrs)?; - let attr_info = PalletEventAttrInfo::from_attrs(event_attrs)?; - let deposit_event = attr_info.deposit_event; - - if !matches!(item.vis, syn::Visibility::Public(_)) { - let msg = "Invalid pallet::event, `Event` must be public"; - return Err(syn::Error::new(item.span(), msg)); - } - - let where_clause = item.generics.where_clause.clone(); - - let mut instances = vec![]; - // NOTE: Event is not allowed to be only generic on I because it is not supported - // by construct_runtime. - if let Some(u) = helper::check_type_def_optional_gen(&item.generics, item.ident.span())? { - instances.push(u); - } else { - // construct_runtime only allow non generic event for non instantiable pallet. - instances.push(helper::InstanceUsage { - has_instance: false, - span: item.ident.span(), - }) - } - - let has_instance = item.generics.type_params().any(|t| t.ident == "I"); - let has_config = item.generics.type_params().any(|t| t.ident == "T"); - let gen_kind = super::GenericKind::from_gens(has_config, has_instance) - .expect("Checked by `helper::check_type_def_optional_gen` above"); - - let event = syn::parse2::(item.ident.to_token_stream())?; - - Ok(EventDef { - attr_span, - index, - instances, - deposit_event, - event, - gen_kind, - where_clause, - }) - } + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + ) -> syn::Result { + let item = if let syn::Item::Enum(item) = item { + item + } else { + return Err(syn::Error::new(item.span(), "Invalid pallet::event, expected enum item")) + }; + + let event_attrs: Vec = + helper::take_item_pallet_attrs(&mut item.attrs)?; + let attr_info = PalletEventAttrInfo::from_attrs(event_attrs)?; + let deposit_event = attr_info.deposit_event; + + if !matches!(item.vis, syn::Visibility::Public(_)) { + let msg = "Invalid pallet::event, `Event` must be public"; + return Err(syn::Error::new(item.span(), msg)) + } + + let where_clause = item.generics.where_clause.clone(); + + let mut instances = vec![]; + // NOTE: Event is not allowed to be only generic on I because it is not supported + // by construct_runtime. + if let Some(u) = helper::check_type_def_optional_gen(&item.generics, item.ident.span())? { + instances.push(u); + } else { + // construct_runtime only allow non generic event for non instantiable pallet. + instances.push(helper::InstanceUsage { has_instance: false, span: item.ident.span() }) + } + + let has_instance = item.generics.type_params().any(|t| t.ident == "I"); + let has_config = item.generics.type_params().any(|t| t.ident == "T"); + let gen_kind = super::GenericKind::from_gens(has_config, has_instance) + .expect("Checked by `helper::check_type_def_optional_gen` above"); + + let event = syn::parse2::(item.ident.to_token_stream())?; + + Ok(EventDef { attr_span, index, instances, deposit_event, event, gen_kind, where_clause }) + } } diff --git a/support/procedural-fork/src/pallet/parse/extra_constants.rs b/support/procedural-fork/src/pallet/parse/extra_constants.rs index 38acea21a..2ba6c44b7 100644 --- a/support/procedural-fork/src/pallet/parse/extra_constants.rs +++ b/support/procedural-fork/src/pallet/parse/extra_constants.rs @@ -21,148 +21,140 @@ use syn::spanned::Spanned; /// List of additional token to be used for parsing. mod keyword { - syn::custom_keyword!(DispatchResultWithPostInfo); - syn::custom_keyword!(Call); - syn::custom_keyword!(OriginFor); - syn::custom_keyword!(weight); - syn::custom_keyword!(compact); - syn::custom_keyword!(T); - syn::custom_keyword!(pallet); - syn::custom_keyword!(constant_name); + syn::custom_keyword!(DispatchResultWithPostInfo); + syn::custom_keyword!(Call); + syn::custom_keyword!(OriginFor); + syn::custom_keyword!(weight); + syn::custom_keyword!(compact); + syn::custom_keyword!(T); + syn::custom_keyword!(pallet); + syn::custom_keyword!(constant_name); } /// Definition of extra constants typically `impl Pallet { ... }` pub struct ExtraConstantsDef { - /// The where_clause used. - pub where_clause: Option, - /// A set of usage of instance, must be check for consistency with trait. - pub instances: Vec, - /// The index of call item in pallet module. - pub index: usize, - /// The extra constant defined. - pub extra_constants: Vec, + /// The where_clause used. + pub where_clause: Option, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Vec, + /// The index of call item in pallet module. + pub index: usize, + /// The extra constant defined. + pub extra_constants: Vec, } /// Input definition for an constant in pallet. pub struct ExtraConstantDef { - /// Name of the function - pub ident: syn::Ident, - /// The type returned by the function - pub type_: syn::Type, - /// The doc associated - pub doc: Vec, - /// Optional MetaData Name - pub metadata_name: Option, + /// Name of the function + pub ident: syn::Ident, + /// The type returned by the function + pub type_: syn::Type, + /// The doc associated + pub doc: Vec, + /// Optional MetaData Name + pub metadata_name: Option, } /// Attributes for functions in extra_constants impl block. /// Parse for `#[pallet::constant_name(ConstantName)]` pub struct ExtraConstAttr { - metadata_name: syn::Ident, + metadata_name: syn::Ident, } impl syn::parse::Parse for ExtraConstAttr { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - let content; - syn::bracketed!(content in input); - content.parse::()?; - content.parse::()?; - content.parse::()?; - - let metadata_name; - syn::parenthesized!(metadata_name in content); - Ok(ExtraConstAttr { - metadata_name: metadata_name.parse::()?, - }) - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + content.parse::()?; + + let metadata_name; + syn::parenthesized!(metadata_name in content); + Ok(ExtraConstAttr { metadata_name: metadata_name.parse::()? }) + } } impl ExtraConstantsDef { - pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { - let item = if let syn::Item::Impl(item) = item { - item - } else { - return Err(syn::Error::new( - item.span(), - "Invalid pallet::extra_constants, expected item impl", - )); - }; - - let instances = vec![ - helper::check_impl_gen(&item.generics, item.impl_token.span())?, - helper::check_pallet_struct_usage(&item.self_ty)?, - ]; - - if let Some((_, _, for_)) = item.trait_ { - let msg = "Invalid pallet::call, expected no trait ident as in \ + pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { + let item = if let syn::Item::Impl(item) = item { + item + } else { + return Err(syn::Error::new( + item.span(), + "Invalid pallet::extra_constants, expected item impl", + )) + }; + + let instances = vec![ + helper::check_impl_gen(&item.generics, item.impl_token.span())?, + helper::check_pallet_struct_usage(&item.self_ty)?, + ]; + + if let Some((_, _, for_)) = item.trait_ { + let msg = "Invalid pallet::call, expected no trait ident as in \ `impl<..> Pallet<..> { .. }`"; - return Err(syn::Error::new(for_.span(), msg)); - } - - let mut extra_constants = vec![]; - for impl_item in &mut item.items { - let method = if let syn::ImplItem::Fn(method) = impl_item { - method - } else { - let msg = "Invalid pallet::call, only method accepted"; - return Err(syn::Error::new(impl_item.span(), msg)); - }; - - if !method.sig.inputs.is_empty() { - let msg = "Invalid pallet::extra_constants, method must have 0 args"; - return Err(syn::Error::new(method.sig.span(), msg)); - } - - if !method.sig.generics.params.is_empty() { - let msg = "Invalid pallet::extra_constants, method must have 0 generics"; - return Err(syn::Error::new(method.sig.generics.params[0].span(), msg)); - } - - if method.sig.generics.where_clause.is_some() { - let msg = "Invalid pallet::extra_constants, method must have no where clause"; - return Err(syn::Error::new( - method.sig.generics.where_clause.span(), - msg, - )); - } - - let type_ = match &method.sig.output { - syn::ReturnType::Default => { - let msg = "Invalid pallet::extra_constants, method must have a return type"; - return Err(syn::Error::new(method.span(), msg)); - } - syn::ReturnType::Type(_, type_) => *type_.clone(), - }; - - // parse metadata_name - let mut extra_constant_attrs: Vec = - helper::take_item_pallet_attrs(method)?; - - if extra_constant_attrs.len() > 1 { - let msg = - "Invalid attribute in pallet::constant_name, only one attribute is expected"; - return Err(syn::Error::new( - extra_constant_attrs[1].metadata_name.span(), - msg, - )); - } - - let metadata_name = extra_constant_attrs.pop().map(|attr| attr.metadata_name); - - extra_constants.push(ExtraConstantDef { - ident: method.sig.ident.clone(), - type_, - doc: get_doc_literals(&method.attrs), - metadata_name, - }); - } - - Ok(Self { - index, - instances, - where_clause: item.generics.where_clause.clone(), - extra_constants, - }) - } + return Err(syn::Error::new(for_.span(), msg)) + } + + let mut extra_constants = vec![]; + for impl_item in &mut item.items { + let method = if let syn::ImplItem::Fn(method) = impl_item { + method + } else { + let msg = "Invalid pallet::call, only method accepted"; + return Err(syn::Error::new(impl_item.span(), msg)) + }; + + if !method.sig.inputs.is_empty() { + let msg = "Invalid pallet::extra_constants, method must have 0 args"; + return Err(syn::Error::new(method.sig.span(), msg)) + } + + if !method.sig.generics.params.is_empty() { + let msg = "Invalid pallet::extra_constants, method must have 0 generics"; + return Err(syn::Error::new(method.sig.generics.params[0].span(), msg)) + } + + if method.sig.generics.where_clause.is_some() { + let msg = "Invalid pallet::extra_constants, method must have no where clause"; + return Err(syn::Error::new(method.sig.generics.where_clause.span(), msg)) + } + + let type_ = match &method.sig.output { + syn::ReturnType::Default => { + let msg = "Invalid pallet::extra_constants, method must have a return type"; + return Err(syn::Error::new(method.span(), msg)) + }, + syn::ReturnType::Type(_, type_) => *type_.clone(), + }; + + // parse metadata_name + let mut extra_constant_attrs: Vec = + helper::take_item_pallet_attrs(method)?; + + if extra_constant_attrs.len() > 1 { + let msg = + "Invalid attribute in pallet::constant_name, only one attribute is expected"; + return Err(syn::Error::new(extra_constant_attrs[1].metadata_name.span(), msg)) + } + + let metadata_name = extra_constant_attrs.pop().map(|attr| attr.metadata_name); + + extra_constants.push(ExtraConstantDef { + ident: method.sig.ident.clone(), + type_, + doc: get_doc_literals(&method.attrs), + metadata_name, + }); + } + + Ok(Self { + index, + instances, + where_clause: item.generics.where_clause.clone(), + extra_constants, + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/genesis_build.rs b/support/procedural-fork/src/pallet/parse/genesis_build.rs index 670d4d5ef..d0e1d9ec9 100644 --- a/support/procedural-fork/src/pallet/parse/genesis_build.rs +++ b/support/procedural-fork/src/pallet/parse/genesis_build.rs @@ -20,47 +20,42 @@ use syn::spanned::Spanned; /// Definition for pallet genesis build implementation. pub struct GenesisBuildDef { - /// The index of item in pallet module. - pub index: usize, - /// A set of usage of instance, must be check for consistency with trait. - pub instances: Option>, - /// The where_clause used. - pub where_clause: Option, - /// The span of the pallet::genesis_build attribute. - pub attr_span: proc_macro2::Span, + /// The index of item in pallet module. + pub index: usize, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Option>, + /// The where_clause used. + pub where_clause: Option, + /// The span of the pallet::genesis_build attribute. + pub attr_span: proc_macro2::Span, } impl GenesisBuildDef { - pub fn try_from( - attr_span: proc_macro2::Span, - index: usize, - item: &mut syn::Item, - ) -> syn::Result { - let item = if let syn::Item::Impl(item) = item { - item - } else { - let msg = "Invalid pallet::genesis_build, expected item impl"; - return Err(syn::Error::new(item.span(), msg)); - }; - - let item_trait = &item - .trait_ - .as_ref() - .ok_or_else(|| { - let msg = "Invalid pallet::genesis_build, expected impl<..> GenesisBuild<..> \ + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + ) -> syn::Result { + let item = if let syn::Item::Impl(item) = item { + item + } else { + let msg = "Invalid pallet::genesis_build, expected item impl"; + return Err(syn::Error::new(item.span(), msg)) + }; + + let item_trait = &item + .trait_ + .as_ref() + .ok_or_else(|| { + let msg = "Invalid pallet::genesis_build, expected impl<..> GenesisBuild<..> \ for GenesisConfig<..>"; - syn::Error::new(item.span(), msg) - })? - .1; + syn::Error::new(item.span(), msg) + })? + .1; - let instances = - helper::check_genesis_builder_usage(item_trait)?.map(|instances| vec![instances]); + let instances = + helper::check_genesis_builder_usage(item_trait)?.map(|instances| vec![instances]); - Ok(Self { - attr_span, - index, - instances, - where_clause: item.generics.where_clause.clone(), - }) - } + Ok(Self { attr_span, index, instances, where_clause: item.generics.where_clause.clone() }) + } } diff --git a/support/procedural-fork/src/pallet/parse/genesis_config.rs b/support/procedural-fork/src/pallet/parse/genesis_config.rs index 1c52345eb..62da6ba13 100644 --- a/support/procedural-fork/src/pallet/parse/genesis_config.rs +++ b/support/procedural-fork/src/pallet/parse/genesis_config.rs @@ -24,55 +24,50 @@ use syn::spanned::Spanned; /// * `struct GenesisConfig` /// * `enum GenesisConfig` pub struct GenesisConfigDef { - /// The index of item in pallet module. - pub index: usize, - /// The kind of generic the type `GenesisConfig` has. - pub gen_kind: super::GenericKind, - /// A set of usage of instance, must be check for consistency with trait. - pub instances: Vec, - /// The ident of genesis_config, can be used for span. - pub genesis_config: syn::Ident, + /// The index of item in pallet module. + pub index: usize, + /// The kind of generic the type `GenesisConfig` has. + pub gen_kind: super::GenericKind, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Vec, + /// The ident of genesis_config, can be used for span. + pub genesis_config: syn::Ident, } impl GenesisConfigDef { - pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { - let item_span = item.span(); - let (vis, ident, generics) = match &item { - syn::Item::Enum(item) => (&item.vis, &item.ident, &item.generics), - syn::Item::Struct(item) => (&item.vis, &item.ident, &item.generics), - _ => { - let msg = "Invalid pallet::genesis_config, expected enum or struct"; - return Err(syn::Error::new(item.span(), msg)); - } - }; + pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { + let item_span = item.span(); + let (vis, ident, generics) = match &item { + syn::Item::Enum(item) => (&item.vis, &item.ident, &item.generics), + syn::Item::Struct(item) => (&item.vis, &item.ident, &item.generics), + _ => { + let msg = "Invalid pallet::genesis_config, expected enum or struct"; + return Err(syn::Error::new(item.span(), msg)) + }, + }; - let mut instances = vec![]; - // NOTE: GenesisConfig is not allowed to be only generic on I because it is not supported - // by construct_runtime. - if let Some(u) = helper::check_type_def_optional_gen(generics, ident.span())? { - instances.push(u); - } + let mut instances = vec![]; + // NOTE: GenesisConfig is not allowed to be only generic on I because it is not supported + // by construct_runtime. + if let Some(u) = helper::check_type_def_optional_gen(generics, ident.span())? { + instances.push(u); + } - let has_instance = generics.type_params().any(|t| t.ident == "I"); - let has_config = generics.type_params().any(|t| t.ident == "T"); - let gen_kind = super::GenericKind::from_gens(has_config, has_instance) - .expect("Checked by `helper::check_type_def_optional_gen` above"); + let has_instance = generics.type_params().any(|t| t.ident == "I"); + let has_config = generics.type_params().any(|t| t.ident == "T"); + let gen_kind = super::GenericKind::from_gens(has_config, has_instance) + .expect("Checked by `helper::check_type_def_optional_gen` above"); - if !matches!(vis, syn::Visibility::Public(_)) { - let msg = "Invalid pallet::genesis_config, GenesisConfig must be public"; - return Err(syn::Error::new(item_span, msg)); - } + if !matches!(vis, syn::Visibility::Public(_)) { + let msg = "Invalid pallet::genesis_config, GenesisConfig must be public"; + return Err(syn::Error::new(item_span, msg)) + } - if ident != "GenesisConfig" { - let msg = "Invalid pallet::genesis_config, ident must `GenesisConfig`"; - return Err(syn::Error::new(ident.span(), msg)); - } + if ident != "GenesisConfig" { + let msg = "Invalid pallet::genesis_config, ident must `GenesisConfig`"; + return Err(syn::Error::new(ident.span(), msg)) + } - Ok(GenesisConfigDef { - index, - genesis_config: ident.clone(), - instances, - gen_kind, - }) - } + Ok(GenesisConfigDef { index, genesis_config: ident.clone(), instances, gen_kind }) + } } diff --git a/support/procedural-fork/src/pallet/parse/helper.rs b/support/procedural-fork/src/pallet/parse/helper.rs index 3d39e0aa0..3187c9139 100644 --- a/support/procedural-fork/src/pallet/parse/helper.rs +++ b/support/procedural-fork/src/pallet/parse/helper.rs @@ -21,176 +21,164 @@ use syn::spanned::Spanned; /// List of additional token to be used for parsing. mod keyword { - syn::custom_keyword!(I); - syn::custom_keyword!(compact); - syn::custom_keyword!(GenesisBuild); - syn::custom_keyword!(BuildGenesisConfig); - syn::custom_keyword!(Config); - syn::custom_keyword!(T); - syn::custom_keyword!(Pallet); - syn::custom_keyword!(origin); - syn::custom_keyword!(DispatchResult); - syn::custom_keyword!(DispatchResultWithPostInfo); + syn::custom_keyword!(I); + syn::custom_keyword!(compact); + syn::custom_keyword!(GenesisBuild); + syn::custom_keyword!(BuildGenesisConfig); + syn::custom_keyword!(Config); + syn::custom_keyword!(T); + syn::custom_keyword!(Pallet); + syn::custom_keyword!(origin); + syn::custom_keyword!(DispatchResult); + syn::custom_keyword!(DispatchResultWithPostInfo); } /// A usage of instance, either the trait `Config` has been used with instance or without instance. /// Used to check for consistency. #[derive(Clone)] pub struct InstanceUsage { - pub has_instance: bool, - pub span: proc_macro2::Span, + pub has_instance: bool, + pub span: proc_macro2::Span, } /// Trait implemented for syn items to get mutable references on their attributes. /// /// NOTE: verbatim variants are not supported. pub trait MutItemAttrs { - fn mut_item_attrs(&mut self) -> Option<&mut Vec>; + fn mut_item_attrs(&mut self) -> Option<&mut Vec>; } /// Take the first pallet attribute (e.g. attribute like `#[pallet..]`) and decode it to `Attr` pub(crate) fn take_first_item_pallet_attr( - item: &mut impl MutItemAttrs, + item: &mut impl MutItemAttrs, ) -> syn::Result> where - Attr: syn::parse::Parse, + Attr: syn::parse::Parse, { - let attrs = if let Some(attrs) = item.mut_item_attrs() { - attrs - } else { - return Ok(None); - }; - - if let Some(index) = attrs.iter().position(|attr| { - attr.path() - .segments - .first() - .map_or(false, |segment| segment.ident == "pallet") - }) { - let pallet_attr = attrs.remove(index); - Ok(Some(syn::parse2(pallet_attr.into_token_stream())?)) - } else { - Ok(None) - } + let attrs = if let Some(attrs) = item.mut_item_attrs() { attrs } else { return Ok(None) }; + + if let Some(index) = attrs.iter().position(|attr| { + attr.path().segments.first().map_or(false, |segment| segment.ident == "pallet") + }) { + let pallet_attr = attrs.remove(index); + Ok(Some(syn::parse2(pallet_attr.into_token_stream())?)) + } else { + Ok(None) + } } /// Take all the pallet attributes (e.g. attribute like `#[pallet..]`) and decode them to `Attr` pub(crate) fn take_item_pallet_attrs(item: &mut impl MutItemAttrs) -> syn::Result> where - Attr: syn::parse::Parse, + Attr: syn::parse::Parse, { - let mut pallet_attrs = Vec::new(); + let mut pallet_attrs = Vec::new(); - while let Some(attr) = take_first_item_pallet_attr(item)? { - pallet_attrs.push(attr) - } + while let Some(attr) = take_first_item_pallet_attr(item)? { + pallet_attrs.push(attr) + } - Ok(pallet_attrs) + Ok(pallet_attrs) } /// Get all the cfg attributes (e.g. attribute like `#[cfg..]`) and decode them to `Attr` pub fn get_item_cfg_attrs(attrs: &[syn::Attribute]) -> Vec { - attrs - .iter() - .filter_map(|attr| { - if attr - .path() - .segments - .first() - .map_or(false, |segment| segment.ident == "cfg") - { - Some(attr.clone()) - } else { - None - } - }) - .collect::>() + attrs + .iter() + .filter_map(|attr| { + if attr.path().segments.first().map_or(false, |segment| segment.ident == "cfg") { + Some(attr.clone()) + } else { + None + } + }) + .collect::>() } impl MutItemAttrs for syn::Item { - fn mut_item_attrs(&mut self) -> Option<&mut Vec> { - match self { - Self::Const(item) => Some(item.attrs.as_mut()), - Self::Enum(item) => Some(item.attrs.as_mut()), - Self::ExternCrate(item) => Some(item.attrs.as_mut()), - Self::Fn(item) => Some(item.attrs.as_mut()), - Self::ForeignMod(item) => Some(item.attrs.as_mut()), - Self::Impl(item) => Some(item.attrs.as_mut()), - Self::Macro(item) => Some(item.attrs.as_mut()), - Self::Mod(item) => Some(item.attrs.as_mut()), - Self::Static(item) => Some(item.attrs.as_mut()), - Self::Struct(item) => Some(item.attrs.as_mut()), - Self::Trait(item) => Some(item.attrs.as_mut()), - Self::TraitAlias(item) => Some(item.attrs.as_mut()), - Self::Type(item) => Some(item.attrs.as_mut()), - Self::Union(item) => Some(item.attrs.as_mut()), - Self::Use(item) => Some(item.attrs.as_mut()), - _ => None, - } - } + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + match self { + Self::Const(item) => Some(item.attrs.as_mut()), + Self::Enum(item) => Some(item.attrs.as_mut()), + Self::ExternCrate(item) => Some(item.attrs.as_mut()), + Self::Fn(item) => Some(item.attrs.as_mut()), + Self::ForeignMod(item) => Some(item.attrs.as_mut()), + Self::Impl(item) => Some(item.attrs.as_mut()), + Self::Macro(item) => Some(item.attrs.as_mut()), + Self::Mod(item) => Some(item.attrs.as_mut()), + Self::Static(item) => Some(item.attrs.as_mut()), + Self::Struct(item) => Some(item.attrs.as_mut()), + Self::Trait(item) => Some(item.attrs.as_mut()), + Self::TraitAlias(item) => Some(item.attrs.as_mut()), + Self::Type(item) => Some(item.attrs.as_mut()), + Self::Union(item) => Some(item.attrs.as_mut()), + Self::Use(item) => Some(item.attrs.as_mut()), + _ => None, + } + } } impl MutItemAttrs for syn::TraitItem { - fn mut_item_attrs(&mut self) -> Option<&mut Vec> { - match self { - Self::Const(item) => Some(item.attrs.as_mut()), - Self::Fn(item) => Some(item.attrs.as_mut()), - Self::Type(item) => Some(item.attrs.as_mut()), - Self::Macro(item) => Some(item.attrs.as_mut()), - _ => None, - } - } + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + match self { + Self::Const(item) => Some(item.attrs.as_mut()), + Self::Fn(item) => Some(item.attrs.as_mut()), + Self::Type(item) => Some(item.attrs.as_mut()), + Self::Macro(item) => Some(item.attrs.as_mut()), + _ => None, + } + } } impl MutItemAttrs for Vec { - fn mut_item_attrs(&mut self) -> Option<&mut Vec> { - Some(self) - } + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + Some(self) + } } impl MutItemAttrs for syn::ItemMod { - fn mut_item_attrs(&mut self) -> Option<&mut Vec> { - Some(&mut self.attrs) - } + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + Some(&mut self.attrs) + } } impl MutItemAttrs for syn::ImplItemFn { - fn mut_item_attrs(&mut self) -> Option<&mut Vec> { - Some(&mut self.attrs) - } + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + Some(&mut self.attrs) + } } impl MutItemAttrs for syn::ItemType { - fn mut_item_attrs(&mut self) -> Option<&mut Vec> { - Some(&mut self.attrs) - } + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + Some(&mut self.attrs) + } } /// Parse for `()` struct Unit; impl syn::parse::Parse for Unit { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let content; - syn::parenthesized!(content in input); - if !content.is_empty() { - let msg = "unexpected tokens, expected nothing inside parenthesis as `()`"; - return Err(syn::Error::new(content.span(), msg)); - } - Ok(Self) - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let content; + syn::parenthesized!(content in input); + if !content.is_empty() { + let msg = "unexpected tokens, expected nothing inside parenthesis as `()`"; + return Err(syn::Error::new(content.span(), msg)) + } + Ok(Self) + } } /// Parse for `'static` struct StaticLifetime; impl syn::parse::Parse for StaticLifetime { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let lifetime = input.parse::()?; - if lifetime.ident != "static" { - let msg = "unexpected tokens, expected `static`"; - return Err(syn::Error::new(lifetime.ident.span(), msg)); - } - Ok(Self) - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let lifetime = input.parse::()?; + if lifetime.ident != "static" { + let msg = "unexpected tokens, expected `static`"; + return Err(syn::Error::new(lifetime.ident.span(), msg)) + } + Ok(Self) + } } /// Check the syntax: `I: 'static = ()` @@ -199,28 +187,28 @@ impl syn::parse::Parse for StaticLifetime { /// /// return the instance if found. pub fn check_config_def_gen(gen: &syn::Generics, span: proc_macro2::Span) -> syn::Result<()> { - let expected = "expected `I: 'static = ()`"; - pub struct CheckTraitDefGenerics; - impl syn::parse::Parse for CheckTraitDefGenerics { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - - Ok(Self) - } - } - - syn::parse2::(gen.params.to_token_stream()).map_err(|e| { - let msg = format!("Invalid generics: {}", expected); - let mut err = syn::Error::new(span, msg); - err.combine(e); - err - })?; - - Ok(()) + let expected = "expected `I: 'static = ()`"; + pub struct CheckTraitDefGenerics; + impl syn::parse::Parse for CheckTraitDefGenerics { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + + Ok(Self) + } + } + + syn::parse2::(gen.params.to_token_stream()).map_err(|e| { + let msg = format!("Invalid generics: {}", expected); + let mut err = syn::Error::new(span, msg); + err.combine(e); + err + })?; + + Ok(()) } /// Check the syntax: @@ -231,41 +219,38 @@ pub fn check_config_def_gen(gen: &syn::Generics, span: proc_macro2::Span) -> syn /// /// return the instance if found. pub fn check_type_def_gen_no_bounds( - gen: &syn::Generics, - span: proc_macro2::Span, + gen: &syn::Generics, + span: proc_macro2::Span, ) -> syn::Result { - let expected = "expected `T` or `T, I = ()`"; - pub struct Checker(InstanceUsage); - impl syn::parse::Parse for Checker { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let mut instance_usage = InstanceUsage { - has_instance: false, - span: input.span(), - }; - - input.parse::()?; - if input.peek(syn::Token![,]) { - instance_usage.has_instance = true; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - } - - Ok(Self(instance_usage)) - } - } - - let i = syn::parse2::(gen.params.to_token_stream()) - .map_err(|e| { - let msg = format!("Invalid type def generics: {}", expected); - let mut err = syn::Error::new(span, msg); - err.combine(e); - err - })? - .0; - - Ok(i) + let expected = "expected `T` or `T, I = ()`"; + pub struct Checker(InstanceUsage); + impl syn::parse::Parse for Checker { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut instance_usage = InstanceUsage { has_instance: false, span: input.span() }; + + input.parse::()?; + if input.peek(syn::Token![,]) { + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + } + + Ok(Self(instance_usage)) + } + } + + let i = syn::parse2::(gen.params.to_token_stream()) + .map_err(|e| { + let msg = format!("Invalid type def generics: {}", expected); + let mut err = syn::Error::new(span, msg); + err.combine(e); + err + })? + .0; + + Ok(i) } /// Check the syntax: @@ -279,79 +264,76 @@ pub fn check_type_def_gen_no_bounds( /// /// return some instance usage if there is some generic, or none otherwise. pub fn check_type_def_optional_gen( - gen: &syn::Generics, - span: proc_macro2::Span, + gen: &syn::Generics, + span: proc_macro2::Span, ) -> syn::Result> { - let expected = "expected `` or `T` or `T: Config` or `T, I = ()` or \ + let expected = "expected `` or `T` or `T: Config` or `T, I = ()` or \ `T: Config, I: 'static = ()`"; - pub struct Checker(Option); - impl syn::parse::Parse for Checker { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - if input.is_empty() { - return Ok(Self(None)); - } - - let mut instance_usage = InstanceUsage { - span: input.span(), - has_instance: false, - }; - - input.parse::()?; - - if input.is_empty() { - return Ok(Self(Some(instance_usage))); - } - - let lookahead = input.lookahead1(); - if lookahead.peek(syn::Token![,]) { - instance_usage.has_instance = true; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - - Ok(Self(Some(instance_usage))) - } else if lookahead.peek(syn::Token![:]) { - input.parse::()?; - input.parse::()?; - - if input.is_empty() { - return Ok(Self(Some(instance_usage))); - } - - instance_usage.has_instance = true; - input.parse::()?; - input.parse::()?; - input.parse::]>()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - - Ok(Self(Some(instance_usage))) - } else { - Err(lookahead.error()) - } - } - } - - let i = syn::parse2::(gen.params.to_token_stream()) - .map_err(|e| { - let msg = format!("Invalid type def generics: {}", expected); - let mut err = syn::Error::new(span, msg); - err.combine(e); - err - })? - .0 - // Span can be call_site if generic is empty. Thus we replace it. - .map(|mut i| { - i.span = span; - i - }); - - Ok(i) + pub struct Checker(Option); + impl syn::parse::Parse for Checker { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + if input.is_empty() { + return Ok(Self(None)) + } + + let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false }; + + input.parse::()?; + + if input.is_empty() { + return Ok(Self(Some(instance_usage))) + } + + let lookahead = input.lookahead1(); + if lookahead.peek(syn::Token![,]) { + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + + Ok(Self(Some(instance_usage))) + } else if lookahead.peek(syn::Token![:]) { + input.parse::()?; + input.parse::()?; + + if input.is_empty() { + return Ok(Self(Some(instance_usage))) + } + + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + input.parse::]>()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + + Ok(Self(Some(instance_usage))) + } else { + Err(lookahead.error()) + } + } + } + + let i = syn::parse2::(gen.params.to_token_stream()) + .map_err(|e| { + let msg = format!("Invalid type def generics: {}", expected); + let mut err = syn::Error::new(span, msg); + err.combine(e); + err + })? + .0 + // Span can be call_site if generic is empty. Thus we replace it. + .map(|mut i| { + i.span = span; + i + }); + + Ok(i) } /// Check the syntax: @@ -360,39 +342,36 @@ pub fn check_type_def_optional_gen( /// /// return the instance if found. pub fn check_pallet_struct_usage(type_: &Box) -> syn::Result { - let expected = "expected `Pallet` or `Pallet`"; - pub struct Checker(InstanceUsage); - impl syn::parse::Parse for Checker { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let mut instance_usage = InstanceUsage { - span: input.span(), - has_instance: false, - }; - - input.parse::()?; - input.parse::()?; - input.parse::()?; - if input.peek(syn::Token![,]) { - instance_usage.has_instance = true; - input.parse::()?; - input.parse::()?; - } - input.parse::]>()?; - - Ok(Self(instance_usage)) - } - } - - let i = syn::parse2::(type_.to_token_stream()) - .map_err(|e| { - let msg = format!("Invalid pallet struct: {}", expected); - let mut err = syn::Error::new(type_.span(), msg); - err.combine(e); - err - })? - .0; - - Ok(i) + let expected = "expected `Pallet` or `Pallet`"; + pub struct Checker(InstanceUsage); + impl syn::parse::Parse for Checker { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false }; + + input.parse::()?; + input.parse::()?; + input.parse::()?; + if input.peek(syn::Token![,]) { + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + } + input.parse::]>()?; + + Ok(Self(instance_usage)) + } + } + + let i = syn::parse2::(type_.to_token_stream()) + .map_err(|e| { + let msg = format!("Invalid pallet struct: {}", expected); + let mut err = syn::Error::new(type_.span(), msg); + err.combine(e); + err + })? + .0; + + Ok(i) } /// Check the generic is: @@ -403,42 +382,39 @@ pub fn check_pallet_struct_usage(type_: &Box) -> syn::Result syn::Result { - let expected = "expected `impl` or `impl, I: 'static>`"; - pub struct Checker(InstanceUsage); - impl syn::parse::Parse for Checker { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let mut instance_usage = InstanceUsage { - span: input.span(), - has_instance: false, - }; - - input.parse::()?; - input.parse::()?; - input.parse::()?; - if input.peek(syn::Token![<]) { - instance_usage.has_instance = true; - input.parse::()?; - input.parse::()?; - input.parse::]>()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - } - - Ok(Self(instance_usage)) - } - } - - let i = syn::parse2::(gen.params.to_token_stream()) - .map_err(|e| { - let mut err = syn::Error::new(span, format!("Invalid generics: {}", expected)); - err.combine(e); - err - })? - .0; - - Ok(i) + let expected = "expected `impl` or `impl, I: 'static>`"; + pub struct Checker(InstanceUsage); + impl syn::parse::Parse for Checker { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false }; + + input.parse::()?; + input.parse::()?; + input.parse::()?; + if input.peek(syn::Token![<]) { + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + input.parse::]>()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + } + + Ok(Self(instance_usage)) + } + } + + let i = syn::parse2::(gen.params.to_token_stream()) + .map_err(|e| { + let mut err = syn::Error::new(span, format!("Invalid generics: {}", expected)); + err.combine(e); + err + })? + .0; + + Ok(i) } /// Check the syntax: @@ -451,73 +427,70 @@ pub fn check_impl_gen(gen: &syn::Generics, span: proc_macro2::Span) -> syn::Resu /// /// return the instance if found. pub fn check_type_def_gen( - gen: &syn::Generics, - span: proc_macro2::Span, + gen: &syn::Generics, + span: proc_macro2::Span, ) -> syn::Result { - let expected = "expected `T` or `T: Config` or `T, I = ()` or \ + let expected = "expected `T` or `T: Config` or `T, I = ()` or \ `T: Config, I: 'static = ()`"; - pub struct Checker(InstanceUsage); - impl syn::parse::Parse for Checker { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let mut instance_usage = InstanceUsage { - span: input.span(), - has_instance: false, - }; - - input.parse::()?; - - if input.is_empty() { - return Ok(Self(instance_usage)); - } - - let lookahead = input.lookahead1(); - if lookahead.peek(syn::Token![,]) { - instance_usage.has_instance = true; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - - Ok(Self(instance_usage)) - } else if lookahead.peek(syn::Token![:]) { - input.parse::()?; - input.parse::()?; - - if input.is_empty() { - return Ok(Self(instance_usage)); - } - - instance_usage.has_instance = true; - input.parse::()?; - input.parse::()?; - input.parse::]>()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - - Ok(Self(instance_usage)) - } else { - Err(lookahead.error()) - } - } - } - - let mut i = syn::parse2::(gen.params.to_token_stream()) - .map_err(|e| { - let msg = format!("Invalid type def generics: {}", expected); - let mut err = syn::Error::new(span, msg); - err.combine(e); - err - })? - .0; - - // Span can be call_site if generic is empty. Thus we replace it. - i.span = span; - - Ok(i) + pub struct Checker(InstanceUsage); + impl syn::parse::Parse for Checker { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false }; + + input.parse::()?; + + if input.is_empty() { + return Ok(Self(instance_usage)) + } + + let lookahead = input.lookahead1(); + if lookahead.peek(syn::Token![,]) { + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + + Ok(Self(instance_usage)) + } else if lookahead.peek(syn::Token![:]) { + input.parse::()?; + input.parse::()?; + + if input.is_empty() { + return Ok(Self(instance_usage)) + } + + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + input.parse::]>()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + + Ok(Self(instance_usage)) + } else { + Err(lookahead.error()) + } + } + } + + let mut i = syn::parse2::(gen.params.to_token_stream()) + .map_err(|e| { + let msg = format!("Invalid type def generics: {}", expected); + let mut err = syn::Error::new(span, msg); + err.combine(e); + err + })? + .0; + + // Span can be call_site if generic is empty. Thus we replace it. + i.span = span; + + Ok(i) } /// Check the syntax: @@ -528,43 +501,40 @@ pub fn check_type_def_gen( /// return the instance if found for `GenesisBuild` /// return None for BuildGenesisConfig pub fn check_genesis_builder_usage(type_: &syn::Path) -> syn::Result> { - let expected = "expected `BuildGenesisConfig` (or the deprecated `GenesisBuild` or `GenesisBuild`)"; - pub struct Checker(Option); - impl syn::parse::Parse for Checker { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let mut instance_usage = InstanceUsage { - span: input.span(), - has_instance: false, - }; - - if input.peek(keyword::GenesisBuild) { - input.parse::()?; - input.parse::()?; - input.parse::()?; - if input.peek(syn::Token![,]) { - instance_usage.has_instance = true; - input.parse::()?; - input.parse::()?; - } - input.parse::]>()?; - Ok(Self(Some(instance_usage))) - } else { - input.parse::()?; - Ok(Self(None)) - } - } - } - - let i = syn::parse2::(type_.to_token_stream()) - .map_err(|e| { - let msg = format!("Invalid genesis builder: {}", expected); - let mut err = syn::Error::new(type_.span(), msg); - err.combine(e); - err - })? - .0; - - Ok(i) + let expected = "expected `BuildGenesisConfig` (or the deprecated `GenesisBuild` or `GenesisBuild`)"; + pub struct Checker(Option); + impl syn::parse::Parse for Checker { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false }; + + if input.peek(keyword::GenesisBuild) { + input.parse::()?; + input.parse::()?; + input.parse::()?; + if input.peek(syn::Token![,]) { + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + } + input.parse::]>()?; + return Ok(Self(Some(instance_usage))) + } else { + input.parse::()?; + return Ok(Self(None)) + } + } + } + + let i = syn::parse2::(type_.to_token_stream()) + .map_err(|e| { + let msg = format!("Invalid genesis builder: {}", expected); + let mut err = syn::Error::new(type_.span(), msg); + err.combine(e); + err + })? + .0; + + Ok(i) } /// Check the syntax: @@ -576,89 +546,87 @@ pub fn check_genesis_builder_usage(type_: &syn::Path) -> syn::Result syn::Result> { - let expected = "expected `` or `T: Config` or `T: Config, I: 'static`"; - pub struct Checker(Option); - impl syn::parse::Parse for Checker { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - if input.is_empty() { - return Ok(Self(None)); - } - - input.parse::()?; - input.parse::()?; - input.parse::()?; - - let mut instance_usage = InstanceUsage { - span: input.span(), - has_instance: false, - }; - - if input.is_empty() { - return Ok(Self(Some(instance_usage))); - } - - instance_usage.has_instance = true; - input.parse::()?; - input.parse::()?; - input.parse::]>()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - - Ok(Self(Some(instance_usage))) - } - } - - let i = syn::parse2::(gen.params.to_token_stream()) - .map_err(|e| { - let msg = format!("Invalid type def generics: {}", expected); - let mut err = syn::Error::new(span, msg); - err.combine(e); - err - })? - .0 - // Span can be call_site if generic is empty. Thus we replace it. - .map(|mut i| { - i.span = span; - i - }); - - Ok(i) + let expected = "expected `` or `T: Config` or `T: Config, I: 'static`"; + pub struct Checker(Option); + impl syn::parse::Parse for Checker { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + if input.is_empty() { + return Ok(Self(None)) + } + + input.parse::()?; + input.parse::()?; + input.parse::()?; + + let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false }; + + if input.is_empty() { + return Ok(Self(Some(instance_usage))) + } + + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + input.parse::]>()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + + Ok(Self(Some(instance_usage))) + } + } + + let i = syn::parse2::(gen.params.to_token_stream()) + .map_err(|e| { + let msg = format!("Invalid type def generics: {}", expected); + let mut err = syn::Error::new(span, msg); + err.combine(e); + err + })? + .0 + // Span can be call_site if generic is empty. Thus we replace it. + .map(|mut i| { + i.span = span; + i + }); + + Ok(i) } /// Check the keyword `DispatchResultWithPostInfo` or `DispatchResult`. pub fn check_pallet_call_return_type(type_: &syn::Type) -> syn::Result<()> { - pub struct Checker; - impl syn::parse::Parse for Checker { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let lookahead = input.lookahead1(); - if lookahead.peek(keyword::DispatchResultWithPostInfo) { - input.parse::()?; - Ok(Self) - } else if lookahead.peek(keyword::DispatchResult) { - input.parse::()?; - Ok(Self) - } else { - Err(lookahead.error()) - } - } - } - - syn::parse2::(type_.to_token_stream()).map(|_| ()) + pub struct Checker; + impl syn::parse::Parse for Checker { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let lookahead = input.lookahead1(); + if lookahead.peek(keyword::DispatchResultWithPostInfo) { + input.parse::()?; + Ok(Self) + } else if lookahead.peek(keyword::DispatchResult) { + input.parse::()?; + Ok(Self) + } else { + Err(lookahead.error()) + } + } + } + + syn::parse2::(type_.to_token_stream()).map(|_| ()) } pub(crate) fn two128_str(s: &str) -> TokenStream { - bytes_to_array(sp_crypto_hashing::twox_128(s.as_bytes())) + bytes_to_array(sp_crypto_hashing::twox_128(s.as_bytes()).into_iter()) } pub(crate) fn bytes_to_array(bytes: impl IntoIterator) -> TokenStream { - let bytes = bytes.into_iter(); + let bytes = bytes.into_iter(); - quote!( - [ #( #bytes ),* ] - ) + quote!( + [ #( #bytes ),* ] + ) + .into() } diff --git a/support/procedural-fork/src/pallet/parse/hooks.rs b/support/procedural-fork/src/pallet/parse/hooks.rs index 1cf5c72cc..37d7d22f4 100644 --- a/support/procedural-fork/src/pallet/parse/hooks.rs +++ b/support/procedural-fork/src/pallet/parse/hooks.rs @@ -20,67 +20,67 @@ use syn::spanned::Spanned; /// Implementation of the pallet hooks. pub struct HooksDef { - /// The index of item in pallet. - pub index: usize, - /// A set of usage of instance, must be check for consistency with trait. - pub instances: Vec, - /// The where_clause used. - pub where_clause: Option, - /// The span of the pallet::hooks attribute. - pub attr_span: proc_macro2::Span, - /// Boolean flag, set to true if the `on_runtime_upgrade` method of hooks was implemented. - pub has_runtime_upgrade: bool, + /// The index of item in pallet. + pub index: usize, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Vec, + /// The where_clause used. + pub where_clause: Option, + /// The span of the pallet::hooks attribute. + pub attr_span: proc_macro2::Span, + /// Boolean flag, set to true if the `on_runtime_upgrade` method of hooks was implemented. + pub has_runtime_upgrade: bool, } impl HooksDef { - pub fn try_from( - attr_span: proc_macro2::Span, - index: usize, - item: &mut syn::Item, - ) -> syn::Result { - let item = if let syn::Item::Impl(item) = item { - item - } else { - let msg = "Invalid pallet::hooks, expected item impl"; - return Err(syn::Error::new(item.span(), msg)); - }; + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + ) -> syn::Result { + let item = if let syn::Item::Impl(item) = item { + item + } else { + let msg = "Invalid pallet::hooks, expected item impl"; + return Err(syn::Error::new(item.span(), msg)) + }; - let instances = vec![ - helper::check_pallet_struct_usage(&item.self_ty)?, - helper::check_impl_gen(&item.generics, item.impl_token.span())?, - ]; + let instances = vec![ + helper::check_pallet_struct_usage(&item.self_ty)?, + helper::check_impl_gen(&item.generics, item.impl_token.span())?, + ]; - let item_trait = &item - .trait_ - .as_ref() - .ok_or_else(|| { - let msg = "Invalid pallet::hooks, expected impl<..> Hooks \ + let item_trait = &item + .trait_ + .as_ref() + .ok_or_else(|| { + let msg = "Invalid pallet::hooks, expected impl<..> Hooks \ for Pallet<..>"; - syn::Error::new(item.span(), msg) - })? - .1; + syn::Error::new(item.span(), msg) + })? + .1; - if item_trait.segments.len() != 1 || item_trait.segments[0].ident != "Hooks" { - let msg = format!( - "Invalid pallet::hooks, expected trait to be `Hooks` found `{}`\ + if item_trait.segments.len() != 1 || item_trait.segments[0].ident != "Hooks" { + let msg = format!( + "Invalid pallet::hooks, expected trait to be `Hooks` found `{}`\ , you can import from `frame_support::pallet_prelude`", - quote::quote!(#item_trait) - ); + quote::quote!(#item_trait) + ); - return Err(syn::Error::new(item_trait.span(), msg)); - } + return Err(syn::Error::new(item_trait.span(), msg)) + } - let has_runtime_upgrade = item.items.iter().any(|i| match i { - syn::ImplItem::Fn(method) => method.sig.ident == "on_runtime_upgrade", - _ => false, - }); + let has_runtime_upgrade = item.items.iter().any(|i| match i { + syn::ImplItem::Fn(method) => method.sig.ident == "on_runtime_upgrade", + _ => false, + }); - Ok(Self { - attr_span, - index, - instances, - has_runtime_upgrade, - where_clause: item.generics.where_clause.clone(), - }) - } + Ok(Self { + attr_span, + index, + instances, + has_runtime_upgrade, + where_clause: item.generics.where_clause.clone(), + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/inherent.rs b/support/procedural-fork/src/pallet/parse/inherent.rs index 4eb04e914..d8641691a 100644 --- a/support/procedural-fork/src/pallet/parse/inherent.rs +++ b/support/procedural-fork/src/pallet/parse/inherent.rs @@ -20,41 +20,41 @@ use syn::spanned::Spanned; /// The definition of the pallet inherent implementation. pub struct InherentDef { - /// The index of inherent item in pallet module. - pub index: usize, - /// A set of usage of instance, must be check for consistency with trait. - pub instances: Vec, + /// The index of inherent item in pallet module. + pub index: usize, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Vec, } impl InherentDef { - pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { - let item = if let syn::Item::Impl(item) = item { - item - } else { - let msg = "Invalid pallet::inherent, expected item impl"; - return Err(syn::Error::new(item.span(), msg)); - }; - - if item.trait_.is_none() { - let msg = "Invalid pallet::inherent, expected impl<..> ProvideInherent for Pallet<..>"; - return Err(syn::Error::new(item.span(), msg)); - } - - if let Some(last) = item.trait_.as_ref().unwrap().1.segments.last() { - if last.ident != "ProvideInherent" { - let msg = "Invalid pallet::inherent, expected trait ProvideInherent"; - return Err(syn::Error::new(last.span(), msg)); - } - } else { - let msg = "Invalid pallet::inherent, expected impl<..> ProvideInherent for Pallet<..>"; - return Err(syn::Error::new(item.span(), msg)); - } - - let instances = vec![ - helper::check_pallet_struct_usage(&item.self_ty)?, - helper::check_impl_gen(&item.generics, item.impl_token.span())?, - ]; - - Ok(InherentDef { index, instances }) - } + pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { + let item = if let syn::Item::Impl(item) = item { + item + } else { + let msg = "Invalid pallet::inherent, expected item impl"; + return Err(syn::Error::new(item.span(), msg)) + }; + + if item.trait_.is_none() { + let msg = "Invalid pallet::inherent, expected impl<..> ProvideInherent for Pallet<..>"; + return Err(syn::Error::new(item.span(), msg)) + } + + if let Some(last) = item.trait_.as_ref().unwrap().1.segments.last() { + if last.ident != "ProvideInherent" { + let msg = "Invalid pallet::inherent, expected trait ProvideInherent"; + return Err(syn::Error::new(last.span(), msg)) + } + } else { + let msg = "Invalid pallet::inherent, expected impl<..> ProvideInherent for Pallet<..>"; + return Err(syn::Error::new(item.span(), msg)) + } + + let instances = vec![ + helper::check_pallet_struct_usage(&item.self_ty)?, + helper::check_impl_gen(&item.generics, item.impl_token.span())?, + ]; + + Ok(InherentDef { index, instances }) + } } diff --git a/support/procedural-fork/src/pallet/parse/mod.rs b/support/procedural-fork/src/pallet/parse/mod.rs index 53e65fd12..6e1277461 100644 --- a/support/procedural-fork/src/pallet/parse/mod.rs +++ b/support/procedural-fork/src/pallet/parse/mod.rs @@ -47,68 +47,68 @@ use syn::spanned::Spanned; /// Parsed definition of a pallet. pub struct Def { - /// The module items. - /// (their order must not be modified because they are registered in individual definitions). - pub item: syn::ItemMod, - pub config: config::ConfigDef, - pub pallet_struct: pallet_struct::PalletStructDef, - pub hooks: Option, - pub call: Option, - pub tasks: Option, - pub task_enum: Option, - pub storages: Vec, - pub error: Option, - pub event: Option, - pub origin: Option, - pub inherent: Option, - pub genesis_config: Option, - pub genesis_build: Option, - pub validate_unsigned: Option, - pub extra_constants: Option, - pub composites: Vec, - pub type_values: Vec, - pub frame_system: syn::Path, - pub frame_support: syn::Path, - pub dev_mode: bool, + /// The module items. + /// (their order must not be modified because they are registered in individual definitions). + pub item: syn::ItemMod, + pub config: config::ConfigDef, + pub pallet_struct: pallet_struct::PalletStructDef, + pub hooks: Option, + pub call: Option, + pub tasks: Option, + pub task_enum: Option, + pub storages: Vec, + pub error: Option, + pub event: Option, + pub origin: Option, + pub inherent: Option, + pub genesis_config: Option, + pub genesis_build: Option, + pub validate_unsigned: Option, + pub extra_constants: Option, + pub composites: Vec, + pub type_values: Vec, + pub frame_system: syn::Path, + pub frame_support: syn::Path, + pub dev_mode: bool, } impl Def { - pub fn try_from(mut item: syn::ItemMod, dev_mode: bool) -> syn::Result { - let frame_system = generate_access_from_frame_or_crate("frame-system")?; - let frame_support = generate_access_from_frame_or_crate("frame-support")?; - - let item_span = item.span(); - let items = &mut item - .content - .as_mut() - .ok_or_else(|| { - let msg = "Invalid pallet definition, expected mod to be inlined."; - syn::Error::new(item_span, msg) - })? - .1; - - let mut config = None; - let mut pallet_struct = None; - let mut hooks = None; - let mut call = None; - let mut tasks = None; - let mut task_enum = None; - let mut error = None; - let mut event = None; - let mut origin = None; - let mut inherent = None; - let mut genesis_config = None; - let mut genesis_build = None; - let mut validate_unsigned = None; - let mut extra_constants = None; - let mut storages = vec![]; - let mut type_values = vec![]; - let mut composites: Vec = vec![]; - - for (index, item) in items.iter_mut().enumerate() { - let pallet_attr: Option = helper::take_first_item_pallet_attr(item)?; - - match pallet_attr { + pub fn try_from(mut item: syn::ItemMod, dev_mode: bool) -> syn::Result { + let frame_system = generate_access_from_frame_or_crate("frame-system")?; + let frame_support = generate_access_from_frame_or_crate("frame-support")?; + + let item_span = item.span(); + let items = &mut item + .content + .as_mut() + .ok_or_else(|| { + let msg = "Invalid pallet definition, expected mod to be inlined."; + syn::Error::new(item_span, msg) + })? + .1; + + let mut config = None; + let mut pallet_struct = None; + let mut hooks = None; + let mut call = None; + let mut tasks = None; + let mut task_enum = None; + let mut error = None; + let mut event = None; + let mut origin = None; + let mut inherent = None; + let mut genesis_config = None; + let mut genesis_build = None; + let mut validate_unsigned = None; + let mut extra_constants = None; + let mut storages = vec![]; + let mut type_values = vec![]; + let mut composites: Vec = vec![]; + + for (index, item) in items.iter_mut().enumerate() { + let pallet_attr: Option = helper::take_first_item_pallet_attr(item)?; + + match pallet_attr { Some(PalletAttr::Config(span, with_default)) if config.is_none() => config = Some(config::ConfigDef::try_from( &frame_system, @@ -212,594 +212,538 @@ impl Def { }, None => (), } - } + } - if genesis_config.is_some() != genesis_build.is_some() { - let msg = format!( - "`#[pallet::genesis_config]` and `#[pallet::genesis_build]` attributes must be \ + if genesis_config.is_some() != genesis_build.is_some() { + let msg = format!( + "`#[pallet::genesis_config]` and `#[pallet::genesis_build]` attributes must be \ either both used or both not used, instead genesis_config is {} and genesis_build \ is {}", - genesis_config.as_ref().map_or("unused", |_| "used"), - genesis_build.as_ref().map_or("unused", |_| "used"), - ); - return Err(syn::Error::new(item_span, msg)); - } - - Self::resolve_tasks(&item_span, &mut tasks, &mut task_enum, items)?; - - let def = Def { - item, - config: config - .ok_or_else(|| syn::Error::new(item_span, "Missing `#[pallet::config]`"))?, - pallet_struct: pallet_struct - .ok_or_else(|| syn::Error::new(item_span, "Missing `#[pallet::pallet]`"))?, - hooks, - call, - tasks, - task_enum, - extra_constants, - genesis_config, - genesis_build, - validate_unsigned, - error, - event, - origin, - inherent, - storages, - composites, - type_values, - frame_system, - frame_support, - dev_mode, - }; - - def.check_instance_usage()?; - def.check_event_usage()?; - - Ok(def) - } - - /// Performs extra logic checks necessary for the `#[pallet::tasks_experimental]` feature. - fn resolve_tasks( - item_span: &proc_macro2::Span, - tasks: &mut Option, - task_enum: &mut Option, - items: &mut Vec, - ) -> syn::Result<()> { - // fallback for manual (without macros) definition of tasks impl - Self::resolve_manual_tasks_impl(tasks, task_enum, items)?; - - // fallback for manual (without macros) definition of task enum - Self::resolve_manual_task_enum(tasks, task_enum, items)?; - - // ensure that if `task_enum` is specified, `tasks` is also specified - match (&task_enum, &tasks) { - (Some(_), None) => { - return Err(syn::Error::new( - *item_span, - "Missing `#[pallet::tasks_experimental]` impl", - )) - } - (None, Some(tasks)) => { - if tasks.tasks_attr.is_none() { - return Err(syn::Error::new( + genesis_config.as_ref().map_or("unused", |_| "used"), + genesis_build.as_ref().map_or("unused", |_| "used"), + ); + return Err(syn::Error::new(item_span, msg)) + } + + Self::resolve_tasks(&item_span, &mut tasks, &mut task_enum, items)?; + + let def = Def { + item, + config: config + .ok_or_else(|| syn::Error::new(item_span, "Missing `#[pallet::config]`"))?, + pallet_struct: pallet_struct + .ok_or_else(|| syn::Error::new(item_span, "Missing `#[pallet::pallet]`"))?, + hooks, + call, + tasks, + task_enum, + extra_constants, + genesis_config, + genesis_build, + validate_unsigned, + error, + event, + origin, + inherent, + storages, + composites, + type_values, + frame_system, + frame_support, + dev_mode, + }; + + def.check_instance_usage()?; + def.check_event_usage()?; + + Ok(def) + } + + /// Performs extra logic checks necessary for the `#[pallet::tasks_experimental]` feature. + fn resolve_tasks( + item_span: &proc_macro2::Span, + tasks: &mut Option, + task_enum: &mut Option, + items: &mut Vec, + ) -> syn::Result<()> { + // fallback for manual (without macros) definition of tasks impl + Self::resolve_manual_tasks_impl(tasks, task_enum, items)?; + + // fallback for manual (without macros) definition of task enum + Self::resolve_manual_task_enum(tasks, task_enum, items)?; + + // ensure that if `task_enum` is specified, `tasks` is also specified + match (&task_enum, &tasks) { + (Some(_), None) => + return Err(syn::Error::new( + *item_span, + "Missing `#[pallet::tasks_experimental]` impl", + )), + (None, Some(tasks)) => + if tasks.tasks_attr.is_none() { + return Err(syn::Error::new( tasks.item_impl.impl_token.span(), "A `#[pallet::tasks_experimental]` attribute must be attached to your `Task` impl if the \ task enum has been omitted", - )); - } - } - _ => (), - } - - Ok(()) - } - - /// Tries to locate task enum based on the tasks impl target if attribute is not specified - /// but impl is present. If one is found, `task_enum` is set appropriately. - fn resolve_manual_task_enum( - tasks: &Option, - task_enum: &mut Option, - items: &mut Vec, - ) -> syn::Result<()> { - let (None, Some(tasks)) = (&task_enum, &tasks) else { - return Ok(()); - }; - let syn::Type::Path(type_path) = &*tasks.item_impl.self_ty else { - return Ok(()); - }; - let type_path = type_path.path.segments.iter().collect::>(); - let (Some(seg), None) = (type_path.first(), type_path.get(1)) else { - return Ok(()); - }; - let mut result = None; - for item in items { - let syn::Item::Enum(item_enum) = item else { - continue; - }; - if item_enum.ident == seg.ident { - result = Some(syn::parse2::( - item_enum.to_token_stream(), - )?); - // replace item with a no-op because it will be handled by the expansion of - // `task_enum`. We use a no-op instead of simply removing it from the vec - // so that any indices collected by `Def::try_from` remain accurate - *item = syn::Item::Verbatim(quote::quote!()); - break; - } - } - *task_enum = result; - Ok(()) - } - - /// Tries to locate a manual tasks impl (an impl implementing a trait whose last path segment is - /// `Task`) in the event that one has not been found already via the attribute macro - pub fn resolve_manual_tasks_impl( - tasks: &mut Option, - task_enum: &Option, - items: &Vec, - ) -> syn::Result<()> { - let None = tasks else { return Ok(()) }; - let mut result = None; - for item in items { - let syn::Item::Impl(item_impl) = item else { - continue; - }; - let Some((_, path, _)) = &item_impl.trait_ else { - continue; - }; - let Some(trait_last_seg) = path.segments.last() else { - continue; - }; - let syn::Type::Path(target_path) = &*item_impl.self_ty else { - continue; - }; - let target_path = target_path.path.segments.iter().collect::>(); - let (Some(target_ident), None) = (target_path.first(), target_path.get(1)) else { - continue; - }; - let matches_task_enum = match task_enum { - Some(task_enum) => task_enum.item_enum.ident == target_ident.ident, - None => true, - }; - if trait_last_seg.ident == "Task" && matches_task_enum { - result = Some(syn::parse2::(item_impl.to_token_stream())?); - break; - } - } - *tasks = result; - Ok(()) - } - - /// Check that usage of trait `Event` is consistent with the definition, i.e. it is declared - /// and trait defines type RuntimeEvent, or not declared and no trait associated type. - fn check_event_usage(&self) -> syn::Result<()> { - match (self.config.has_event_type, self.event.is_some()) { - (true, false) => { - let msg = "Invalid usage of RuntimeEvent, `Config` contains associated type `RuntimeEvent`, \ + )) + } else { + }, + _ => (), + } + + Ok(()) + } + + /// Tries to locate task enum based on the tasks impl target if attribute is not specified + /// but impl is present. If one is found, `task_enum` is set appropriately. + fn resolve_manual_task_enum( + tasks: &Option, + task_enum: &mut Option, + items: &mut Vec, + ) -> syn::Result<()> { + let (None, Some(tasks)) = (&task_enum, &tasks) else { return Ok(()) }; + let syn::Type::Path(type_path) = &*tasks.item_impl.self_ty else { return Ok(()) }; + let type_path = type_path.path.segments.iter().collect::>(); + let (Some(seg), None) = (type_path.get(0), type_path.get(1)) else { return Ok(()) }; + let mut result = None; + for item in items { + let syn::Item::Enum(item_enum) = item else { continue }; + if item_enum.ident == seg.ident { + result = Some(syn::parse2::(item_enum.to_token_stream())?); + // replace item with a no-op because it will be handled by the expansion of + // `task_enum`. We use a no-op instead of simply removing it from the vec + // so that any indices collected by `Def::try_from` remain accurate + *item = syn::Item::Verbatim(quote::quote!()); + break + } + } + *task_enum = result; + Ok(()) + } + + /// Tries to locate a manual tasks impl (an impl implementing a trait whose last path segment is + /// `Task`) in the event that one has not been found already via the attribute macro + pub fn resolve_manual_tasks_impl( + tasks: &mut Option, + task_enum: &Option, + items: &Vec, + ) -> syn::Result<()> { + let None = tasks else { return Ok(()) }; + let mut result = None; + for item in items { + let syn::Item::Impl(item_impl) = item else { continue }; + let Some((_, path, _)) = &item_impl.trait_ else { continue }; + let Some(trait_last_seg) = path.segments.last() else { continue }; + let syn::Type::Path(target_path) = &*item_impl.self_ty else { continue }; + let target_path = target_path.path.segments.iter().collect::>(); + let (Some(target_ident), None) = (target_path.get(0), target_path.get(1)) else { + continue + }; + let matches_task_enum = match task_enum { + Some(task_enum) => task_enum.item_enum.ident == target_ident.ident, + None => true, + }; + if trait_last_seg.ident == "Task" && matches_task_enum { + result = Some(syn::parse2::(item_impl.to_token_stream())?); + break + } + } + *tasks = result; + Ok(()) + } + + /// Check that usage of trait `Event` is consistent with the definition, i.e. it is declared + /// and trait defines type RuntimeEvent, or not declared and no trait associated type. + fn check_event_usage(&self) -> syn::Result<()> { + match (self.config.has_event_type, self.event.is_some()) { + (true, false) => { + let msg = "Invalid usage of RuntimeEvent, `Config` contains associated type `RuntimeEvent`, \ but enum `Event` is not declared (i.e. no use of `#[pallet::event]`). \ Note that type `RuntimeEvent` in trait is reserved to work alongside pallet event."; - Err(syn::Error::new(proc_macro2::Span::call_site(), msg)) - } - (false, true) => { - let msg = "Invalid usage of RuntimeEvent, `Config` contains no associated type \ + Err(syn::Error::new(proc_macro2::Span::call_site(), msg)) + }, + (false, true) => { + let msg = "Invalid usage of RuntimeEvent, `Config` contains no associated type \ `RuntimeEvent`, but enum `Event` is declared (in use of `#[pallet::event]`). \ An RuntimeEvent associated type must be declare on trait `Config`."; - Err(syn::Error::new(proc_macro2::Span::call_site(), msg)) - } - _ => Ok(()), - } - } - - /// Check that usage of trait `Config` is consistent with the definition, i.e. it is used with - /// instance iff it is defined with instance. - fn check_instance_usage(&self) -> syn::Result<()> { - let mut instances = vec![]; - instances.extend_from_slice(&self.pallet_struct.instances[..]); - instances.extend(&mut self.storages.iter().flat_map(|s| s.instances.clone())); - if let Some(call) = &self.call { - instances.extend_from_slice(&call.instances[..]); - } - if let Some(hooks) = &self.hooks { - instances.extend_from_slice(&hooks.instances[..]); - } - if let Some(event) = &self.event { - instances.extend_from_slice(&event.instances[..]); - } - if let Some(error) = &self.error { - instances.extend_from_slice(&error.instances[..]); - } - if let Some(inherent) = &self.inherent { - instances.extend_from_slice(&inherent.instances[..]); - } - if let Some(origin) = &self.origin { - instances.extend_from_slice(&origin.instances[..]); - } - if let Some(genesis_config) = &self.genesis_config { - instances.extend_from_slice(&genesis_config.instances[..]); - } - if let Some(genesis_build) = &self.genesis_build { - if let Some(i) = genesis_build.instances.as_ref() { - instances.extend_from_slice(i) - } - } - if let Some(extra_constants) = &self.extra_constants { - instances.extend_from_slice(&extra_constants.instances[..]); - } - - let mut errors = instances.into_iter().filter_map(|instances| { - if instances.has_instance == self.config.has_instance { - return None; - } - let msg = if self.config.has_instance { - "Invalid generic declaration, trait is defined with instance but generic use none" - } else { - "Invalid generic declaration, trait is defined without instance but generic use \ + Err(syn::Error::new(proc_macro2::Span::call_site(), msg)) + }, + _ => Ok(()), + } + } + + /// Check that usage of trait `Config` is consistent with the definition, i.e. it is used with + /// instance iff it is defined with instance. + fn check_instance_usage(&self) -> syn::Result<()> { + let mut instances = vec![]; + instances.extend_from_slice(&self.pallet_struct.instances[..]); + instances.extend(&mut self.storages.iter().flat_map(|s| s.instances.clone())); + if let Some(call) = &self.call { + instances.extend_from_slice(&call.instances[..]); + } + if let Some(hooks) = &self.hooks { + instances.extend_from_slice(&hooks.instances[..]); + } + if let Some(event) = &self.event { + instances.extend_from_slice(&event.instances[..]); + } + if let Some(error) = &self.error { + instances.extend_from_slice(&error.instances[..]); + } + if let Some(inherent) = &self.inherent { + instances.extend_from_slice(&inherent.instances[..]); + } + if let Some(origin) = &self.origin { + instances.extend_from_slice(&origin.instances[..]); + } + if let Some(genesis_config) = &self.genesis_config { + instances.extend_from_slice(&genesis_config.instances[..]); + } + if let Some(genesis_build) = &self.genesis_build { + genesis_build.instances.as_ref().map(|i| instances.extend_from_slice(&i)); + } + if let Some(extra_constants) = &self.extra_constants { + instances.extend_from_slice(&extra_constants.instances[..]); + } + + let mut errors = instances.into_iter().filter_map(|instances| { + if instances.has_instance == self.config.has_instance { + return None + } + let msg = if self.config.has_instance { + "Invalid generic declaration, trait is defined with instance but generic use none" + } else { + "Invalid generic declaration, trait is defined without instance but generic use \ some" - }; - Some(syn::Error::new(instances.span, msg)) - }); - - if let Some(mut first_error) = errors.next() { - for error in errors { - first_error.combine(error) - } - Err(first_error) - } else { - Ok(()) - } - } - - /// Depending on if pallet is instantiable: - /// * either `T: Config` - /// * or `T: Config, I: 'static` - pub fn type_impl_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { - if self.config.has_instance { - quote::quote_spanned!(span => T: Config, I: 'static) - } else { - quote::quote_spanned!(span => T: Config) - } - } - - /// Depending on if pallet is instantiable: - /// * either `T: Config` - /// * or `T: Config, I: 'static = ()` - pub fn type_decl_bounded_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { - if self.config.has_instance { - quote::quote_spanned!(span => T: Config, I: 'static = ()) - } else { - quote::quote_spanned!(span => T: Config) - } - } - - /// Depending on if pallet is instantiable: - /// * either `T` - /// * or `T, I = ()` - pub fn type_decl_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { - if self.config.has_instance { - quote::quote_spanned!(span => T, I = ()) - } else { - quote::quote_spanned!(span => T) - } - } - - /// Depending on if pallet is instantiable: - /// * either `` - /// * or `` - /// to be used when using pallet trait `Config` - pub fn trait_use_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { - if self.config.has_instance { - quote::quote_spanned!(span => ) - } else { - quote::quote_spanned!(span => ) - } - } - - /// Depending on if pallet is instantiable: - /// * either `T` - /// * or `T, I` - pub fn type_use_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { - if self.config.has_instance { - quote::quote_spanned!(span => T, I) - } else { - quote::quote_spanned!(span => T) - } - } + }; + Some(syn::Error::new(instances.span, msg)) + }); + + if let Some(mut first_error) = errors.next() { + for error in errors { + first_error.combine(error) + } + Err(first_error) + } else { + Ok(()) + } + } + + /// Depending on if pallet is instantiable: + /// * either `T: Config` + /// * or `T: Config, I: 'static` + pub fn type_impl_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { + if self.config.has_instance { + quote::quote_spanned!(span => T: Config, I: 'static) + } else { + quote::quote_spanned!(span => T: Config) + } + } + + /// Depending on if pallet is instantiable: + /// * either `T: Config` + /// * or `T: Config, I: 'static = ()` + pub fn type_decl_bounded_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { + if self.config.has_instance { + quote::quote_spanned!(span => T: Config, I: 'static = ()) + } else { + quote::quote_spanned!(span => T: Config) + } + } + + /// Depending on if pallet is instantiable: + /// * either `T` + /// * or `T, I = ()` + pub fn type_decl_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { + if self.config.has_instance { + quote::quote_spanned!(span => T, I = ()) + } else { + quote::quote_spanned!(span => T) + } + } + + /// Depending on if pallet is instantiable: + /// * either `` + /// * or `` + /// to be used when using pallet trait `Config` + pub fn trait_use_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { + if self.config.has_instance { + quote::quote_spanned!(span => ) + } else { + quote::quote_spanned!(span => ) + } + } + + /// Depending on if pallet is instantiable: + /// * either `T` + /// * or `T, I` + pub fn type_use_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { + if self.config.has_instance { + quote::quote_spanned!(span => T, I) + } else { + quote::quote_spanned!(span => T) + } + } } /// Some generic kind for type which can be not generic, or generic over config, /// or generic over config and instance, but not generic only over instance. pub enum GenericKind { - None, - Config, - ConfigAndInstance, + None, + Config, + ConfigAndInstance, } impl GenericKind { - /// Return Err if it is only generics over instance but not over config. - pub fn from_gens(has_config: bool, has_instance: bool) -> Result { - match (has_config, has_instance) { - (false, false) => Ok(GenericKind::None), - (true, false) => Ok(GenericKind::Config), - (true, true) => Ok(GenericKind::ConfigAndInstance), - (false, true) => Err(()), - } - } - - /// Return the generic to be used when using the type. - /// - /// Depending on its definition it can be: ``, `T` or `T, I` - pub fn type_use_gen(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { - match self { - GenericKind::None => quote::quote!(), - GenericKind::Config => quote::quote_spanned!(span => T), - GenericKind::ConfigAndInstance => quote::quote_spanned!(span => T, I), - } - } - - /// Return the generic to be used in `impl<..>` when implementing on the type. - pub fn type_impl_gen(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { - match self { - GenericKind::None => quote::quote!(), - GenericKind::Config => quote::quote_spanned!(span => T: Config), - GenericKind::ConfigAndInstance => { - quote::quote_spanned!(span => T: Config, I: 'static) - } - } - } - - /// Return whereas the type has some generic. - pub fn is_generic(&self) -> bool { - match self { - GenericKind::None => false, - GenericKind::Config | GenericKind::ConfigAndInstance => true, - } - } + /// Return Err if it is only generics over instance but not over config. + pub fn from_gens(has_config: bool, has_instance: bool) -> Result { + match (has_config, has_instance) { + (false, false) => Ok(GenericKind::None), + (true, false) => Ok(GenericKind::Config), + (true, true) => Ok(GenericKind::ConfigAndInstance), + (false, true) => Err(()), + } + } + + /// Return the generic to be used when using the type. + /// + /// Depending on its definition it can be: ``, `T` or `T, I` + pub fn type_use_gen(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { + match self { + GenericKind::None => quote::quote!(), + GenericKind::Config => quote::quote_spanned!(span => T), + GenericKind::ConfigAndInstance => quote::quote_spanned!(span => T, I), + } + } + + /// Return the generic to be used in `impl<..>` when implementing on the type. + pub fn type_impl_gen(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { + match self { + GenericKind::None => quote::quote!(), + GenericKind::Config => quote::quote_spanned!(span => T: Config), + GenericKind::ConfigAndInstance => { + quote::quote_spanned!(span => T: Config, I: 'static) + }, + } + } + + /// Return whereas the type has some generic. + pub fn is_generic(&self) -> bool { + match self { + GenericKind::None => false, + GenericKind::Config | GenericKind::ConfigAndInstance => true, + } + } } /// List of additional token to be used for parsing. mod keyword { - syn::custom_keyword!(origin); - syn::custom_keyword!(call); - syn::custom_keyword!(tasks_experimental); - syn::custom_keyword!(task_enum); - syn::custom_keyword!(task_list); - syn::custom_keyword!(task_condition); - syn::custom_keyword!(task_index); - syn::custom_keyword!(weight); - syn::custom_keyword!(event); - syn::custom_keyword!(config); - syn::custom_keyword!(with_default); - syn::custom_keyword!(hooks); - syn::custom_keyword!(inherent); - syn::custom_keyword!(error); - syn::custom_keyword!(storage); - syn::custom_keyword!(genesis_build); - syn::custom_keyword!(genesis_config); - syn::custom_keyword!(validate_unsigned); - syn::custom_keyword!(type_value); - syn::custom_keyword!(pallet); - syn::custom_keyword!(extra_constants); - syn::custom_keyword!(composite_enum); + syn::custom_keyword!(origin); + syn::custom_keyword!(call); + syn::custom_keyword!(tasks_experimental); + syn::custom_keyword!(task_enum); + syn::custom_keyword!(task_list); + syn::custom_keyword!(task_condition); + syn::custom_keyword!(task_index); + syn::custom_keyword!(weight); + syn::custom_keyword!(event); + syn::custom_keyword!(config); + syn::custom_keyword!(with_default); + syn::custom_keyword!(hooks); + syn::custom_keyword!(inherent); + syn::custom_keyword!(error); + syn::custom_keyword!(storage); + syn::custom_keyword!(genesis_build); + syn::custom_keyword!(genesis_config); + syn::custom_keyword!(validate_unsigned); + syn::custom_keyword!(type_value); + syn::custom_keyword!(pallet); + syn::custom_keyword!(extra_constants); + syn::custom_keyword!(composite_enum); } /// Parse attributes for item in pallet module /// syntax must be `pallet::` (e.g. `#[pallet::config]`) enum PalletAttr { - Config(proc_macro2::Span, bool), - Pallet(proc_macro2::Span), - Hooks(proc_macro2::Span), - /// A `#[pallet::call]` with optional attributes to specialize the behaviour. - /// - /// # Attributes - /// - /// Each attribute `attr` can take the form of `#[pallet::call(attr = …)]` or - /// `#[pallet::call(attr(…))]`. The possible attributes are: - /// - /// ## `weight` - /// - /// Can be used to reduce the repetitive weight annotation in the trivial case. It accepts one - /// argument that is expected to be an implementation of the `WeightInfo` or something that - /// behaves syntactically equivalent. This allows to annotate a `WeightInfo` for all the calls. - /// Now each call does not need to specify its own `#[pallet::weight]` but can instead use the - /// one from the `#[pallet::call]` definition. So instead of having to write it on each call: - /// - /// ```ignore - /// #[pallet::call] - /// impl Pallet { - /// #[pallet::weight(T::WeightInfo::create())] - /// pub fn create( - /// ``` - /// you can now omit it on the call itself, if the name of the weigh function matches the call: - /// - /// ```ignore - /// #[pallet::call(weight = ::WeightInfo)] - /// impl Pallet { - /// pub fn create( - /// ``` - /// - /// It is possible to use this syntax together with instantiated pallets by using `Config` - /// instead. - /// - /// ### Dev Mode - /// - /// Normally the `dev_mode` sets all weights of calls without a `#[pallet::weight]` annotation - /// to zero. Now when there is a `weight` attribute on the `#[pallet::call]`, then that is used - /// instead of the zero weight. So to say: it works together with `dev_mode`. - RuntimeCall(Option, proc_macro2::Span), - Error(proc_macro2::Span), - Tasks(proc_macro2::Span), - TaskList(proc_macro2::Span), - TaskCondition(proc_macro2::Span), - TaskIndex(proc_macro2::Span), - RuntimeTask(proc_macro2::Span), - RuntimeEvent(proc_macro2::Span), - RuntimeOrigin(proc_macro2::Span), - Inherent(proc_macro2::Span), - Storage(proc_macro2::Span), - GenesisConfig(proc_macro2::Span), - GenesisBuild(proc_macro2::Span), - ValidateUnsigned(proc_macro2::Span), - TypeValue(proc_macro2::Span), - ExtraConstants(proc_macro2::Span), - Composite(proc_macro2::Span), + Config(proc_macro2::Span, bool), + Pallet(proc_macro2::Span), + Hooks(proc_macro2::Span), + /// A `#[pallet::call]` with optional attributes to specialize the behaviour. + /// + /// # Attributes + /// + /// Each attribute `attr` can take the form of `#[pallet::call(attr = …)]` or + /// `#[pallet::call(attr(…))]`. The possible attributes are: + /// + /// ## `weight` + /// + /// Can be used to reduce the repetitive weight annotation in the trivial case. It accepts one + /// argument that is expected to be an implementation of the `WeightInfo` or something that + /// behaves syntactically equivalent. This allows to annotate a `WeightInfo` for all the calls. + /// Now each call does not need to specify its own `#[pallet::weight]` but can instead use the + /// one from the `#[pallet::call]` definition. So instead of having to write it on each call: + /// + /// ```ignore + /// #[pallet::call] + /// impl Pallet { + /// #[pallet::weight(T::WeightInfo::create())] + /// pub fn create( + /// ``` + /// you can now omit it on the call itself, if the name of the weigh function matches the call: + /// + /// ```ignore + /// #[pallet::call(weight = ::WeightInfo)] + /// impl Pallet { + /// pub fn create( + /// ``` + /// + /// It is possible to use this syntax together with instantiated pallets by using `Config` + /// instead. + /// + /// ### Dev Mode + /// + /// Normally the `dev_mode` sets all weights of calls without a `#[pallet::weight]` annotation + /// to zero. Now when there is a `weight` attribute on the `#[pallet::call]`, then that is used + /// instead of the zero weight. So to say: it works together with `dev_mode`. + RuntimeCall(Option, proc_macro2::Span), + Error(proc_macro2::Span), + Tasks(proc_macro2::Span), + TaskList(proc_macro2::Span), + TaskCondition(proc_macro2::Span), + TaskIndex(proc_macro2::Span), + RuntimeTask(proc_macro2::Span), + RuntimeEvent(proc_macro2::Span), + RuntimeOrigin(proc_macro2::Span), + Inherent(proc_macro2::Span), + Storage(proc_macro2::Span), + GenesisConfig(proc_macro2::Span), + GenesisBuild(proc_macro2::Span), + ValidateUnsigned(proc_macro2::Span), + TypeValue(proc_macro2::Span), + ExtraConstants(proc_macro2::Span), + Composite(proc_macro2::Span), } impl PalletAttr { - fn span(&self) -> proc_macro2::Span { - match self { - Self::Config(span, _) => *span, - Self::Pallet(span) => *span, - Self::Hooks(span) => *span, - Self::Tasks(span) => *span, - Self::TaskCondition(span) => *span, - Self::TaskIndex(span) => *span, - Self::TaskList(span) => *span, - Self::Error(span) => *span, - Self::RuntimeTask(span) => *span, - Self::RuntimeCall(_, span) => *span, - Self::RuntimeEvent(span) => *span, - Self::RuntimeOrigin(span) => *span, - Self::Inherent(span) => *span, - Self::Storage(span) => *span, - Self::GenesisConfig(span) => *span, - Self::GenesisBuild(span) => *span, - Self::ValidateUnsigned(span) => *span, - Self::TypeValue(span) => *span, - Self::ExtraConstants(span) => *span, - Self::Composite(span) => *span, - } - } + fn span(&self) -> proc_macro2::Span { + match self { + Self::Config(span, _) => *span, + Self::Pallet(span) => *span, + Self::Hooks(span) => *span, + Self::Tasks(span) => *span, + Self::TaskCondition(span) => *span, + Self::TaskIndex(span) => *span, + Self::TaskList(span) => *span, + Self::Error(span) => *span, + Self::RuntimeTask(span) => *span, + Self::RuntimeCall(_, span) => *span, + Self::RuntimeEvent(span) => *span, + Self::RuntimeOrigin(span) => *span, + Self::Inherent(span) => *span, + Self::Storage(span) => *span, + Self::GenesisConfig(span) => *span, + Self::GenesisBuild(span) => *span, + Self::ValidateUnsigned(span) => *span, + Self::TypeValue(span) => *span, + Self::ExtraConstants(span) => *span, + Self::Composite(span) => *span, + } + } } impl syn::parse::Parse for PalletAttr { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - let content; - syn::bracketed!(content in input); - content.parse::()?; - content.parse::()?; - - let lookahead = content.lookahead1(); - if lookahead.peek(keyword::config) { - let span = content.parse::()?.span(); - let with_default = content.peek(syn::token::Paren); - if with_default { - let inside_config; - let _paren = syn::parenthesized!(inside_config in content); - inside_config.parse::()?; - } - Ok(PalletAttr::Config(span, with_default)) - } else if lookahead.peek(keyword::pallet) { - Ok(PalletAttr::Pallet( - content.parse::()?.span(), - )) - } else if lookahead.peek(keyword::hooks) { - Ok(PalletAttr::Hooks(content.parse::()?.span())) - } else if lookahead.peek(keyword::call) { - let span = content.parse::().expect("peeked").span(); - let attr = match content.is_empty() { - true => None, - false => Some(InheritedCallWeightAttr::parse(&content)?), - }; - Ok(PalletAttr::RuntimeCall(attr, span)) - } else if lookahead.peek(keyword::tasks_experimental) { - Ok(PalletAttr::Tasks( - content.parse::()?.span(), - )) - } else if lookahead.peek(keyword::task_enum) { - Ok(PalletAttr::RuntimeTask( - content.parse::()?.span(), - )) - } else if lookahead.peek(keyword::task_condition) { - Ok(PalletAttr::TaskCondition( - content.parse::()?.span(), - )) - } else if lookahead.peek(keyword::task_index) { - Ok(PalletAttr::TaskIndex( - content.parse::()?.span(), - )) - } else if lookahead.peek(keyword::task_list) { - Ok(PalletAttr::TaskList( - content.parse::()?.span(), - )) - } else if lookahead.peek(keyword::error) { - Ok(PalletAttr::Error(content.parse::()?.span())) - } else if lookahead.peek(keyword::event) { - Ok(PalletAttr::RuntimeEvent( - content.parse::()?.span(), - )) - } else if lookahead.peek(keyword::origin) { - Ok(PalletAttr::RuntimeOrigin( - content.parse::()?.span(), - )) - } else if lookahead.peek(keyword::inherent) { - Ok(PalletAttr::Inherent( - content.parse::()?.span(), - )) - } else if lookahead.peek(keyword::storage) { - Ok(PalletAttr::Storage( - content.parse::()?.span(), - )) - } else if lookahead.peek(keyword::genesis_config) { - Ok(PalletAttr::GenesisConfig( - content.parse::()?.span(), - )) - } else if lookahead.peek(keyword::genesis_build) { - Ok(PalletAttr::GenesisBuild( - content.parse::()?.span(), - )) - } else if lookahead.peek(keyword::validate_unsigned) { - Ok(PalletAttr::ValidateUnsigned( - content.parse::()?.span(), - )) - } else if lookahead.peek(keyword::type_value) { - Ok(PalletAttr::TypeValue( - content.parse::()?.span(), - )) - } else if lookahead.peek(keyword::extra_constants) { - Ok(PalletAttr::ExtraConstants( - content.parse::()?.span(), - )) - } else if lookahead.peek(keyword::composite_enum) { - Ok(PalletAttr::Composite( - content.parse::()?.span(), - )) - } else { - Err(lookahead.error()) - } - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + let lookahead = content.lookahead1(); + if lookahead.peek(keyword::config) { + let span = content.parse::()?.span(); + let with_default = content.peek(syn::token::Paren); + if with_default { + let inside_config; + let _paren = syn::parenthesized!(inside_config in content); + inside_config.parse::()?; + } + Ok(PalletAttr::Config(span, with_default)) + } else if lookahead.peek(keyword::pallet) { + Ok(PalletAttr::Pallet(content.parse::()?.span())) + } else if lookahead.peek(keyword::hooks) { + Ok(PalletAttr::Hooks(content.parse::()?.span())) + } else if lookahead.peek(keyword::call) { + let span = content.parse::().expect("peeked").span(); + let attr = match content.is_empty() { + true => None, + false => Some(InheritedCallWeightAttr::parse(&content)?), + }; + Ok(PalletAttr::RuntimeCall(attr, span)) + } else if lookahead.peek(keyword::tasks_experimental) { + Ok(PalletAttr::Tasks(content.parse::()?.span())) + } else if lookahead.peek(keyword::task_enum) { + Ok(PalletAttr::RuntimeTask(content.parse::()?.span())) + } else if lookahead.peek(keyword::task_condition) { + Ok(PalletAttr::TaskCondition(content.parse::()?.span())) + } else if lookahead.peek(keyword::task_index) { + Ok(PalletAttr::TaskIndex(content.parse::()?.span())) + } else if lookahead.peek(keyword::task_list) { + Ok(PalletAttr::TaskList(content.parse::()?.span())) + } else if lookahead.peek(keyword::error) { + Ok(PalletAttr::Error(content.parse::()?.span())) + } else if lookahead.peek(keyword::event) { + Ok(PalletAttr::RuntimeEvent(content.parse::()?.span())) + } else if lookahead.peek(keyword::origin) { + Ok(PalletAttr::RuntimeOrigin(content.parse::()?.span())) + } else if lookahead.peek(keyword::inherent) { + Ok(PalletAttr::Inherent(content.parse::()?.span())) + } else if lookahead.peek(keyword::storage) { + Ok(PalletAttr::Storage(content.parse::()?.span())) + } else if lookahead.peek(keyword::genesis_config) { + Ok(PalletAttr::GenesisConfig(content.parse::()?.span())) + } else if lookahead.peek(keyword::genesis_build) { + Ok(PalletAttr::GenesisBuild(content.parse::()?.span())) + } else if lookahead.peek(keyword::validate_unsigned) { + Ok(PalletAttr::ValidateUnsigned(content.parse::()?.span())) + } else if lookahead.peek(keyword::type_value) { + Ok(PalletAttr::TypeValue(content.parse::()?.span())) + } else if lookahead.peek(keyword::extra_constants) { + Ok(PalletAttr::ExtraConstants(content.parse::()?.span())) + } else if lookahead.peek(keyword::composite_enum) { + Ok(PalletAttr::Composite(content.parse::()?.span())) + } else { + Err(lookahead.error()) + } + } } /// The optional weight annotation on a `#[pallet::call]` like `#[pallet::call(weight($type))]`. #[derive(Clone)] pub struct InheritedCallWeightAttr { - pub typename: syn::Type, - pub span: proc_macro2::Span, + pub typename: syn::Type, + pub span: proc_macro2::Span, } impl syn::parse::Parse for InheritedCallWeightAttr { - // Parses `(weight($type))` or `(weight = $type)`. - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let content; - syn::parenthesized!(content in input); - content.parse::()?; - let lookahead = content.lookahead1(); - - let buffer = if lookahead.peek(syn::token::Paren) { - let inner; - syn::parenthesized!(inner in content); - inner - } else if lookahead.peek(syn::Token![=]) { - content.parse::().expect("peeked"); - content - } else { - return Err(lookahead.error()); - }; - - Ok(Self { - typename: buffer.parse()?, - span: input.span(), - }) - } + // Parses `(weight($type))` or `(weight = $type)`. + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let content; + syn::parenthesized!(content in input); + content.parse::()?; + let lookahead = content.lookahead1(); + + let buffer = if lookahead.peek(syn::token::Paren) { + let inner; + syn::parenthesized!(inner in content); + inner + } else if lookahead.peek(syn::Token![=]) { + content.parse::().expect("peeked"); + content + } else { + return Err(lookahead.error()) + }; + + Ok(Self { typename: buffer.parse()?, span: input.span() }) + } } diff --git a/support/procedural-fork/src/pallet/parse/origin.rs b/support/procedural-fork/src/pallet/parse/origin.rs index 2dd84c40d..76e2a8841 100644 --- a/support/procedural-fork/src/pallet/parse/origin.rs +++ b/support/procedural-fork/src/pallet/parse/origin.rs @@ -25,56 +25,48 @@ use syn::spanned::Spanned; /// * `struct Origin` /// * `enum Origin` pub struct OriginDef { - /// The index of item in pallet module. - pub index: usize, - pub has_instance: bool, - pub is_generic: bool, - /// A set of usage of instance, must be check for consistency with trait. - pub instances: Vec, + /// The index of item in pallet module. + pub index: usize, + pub has_instance: bool, + pub is_generic: bool, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Vec, } impl OriginDef { - pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { - let item_span = item.span(); - let (vis, ident, generics) = match &item { - syn::Item::Enum(item) => (&item.vis, &item.ident, &item.generics), - syn::Item::Struct(item) => (&item.vis, &item.ident, &item.generics), - syn::Item::Type(item) => (&item.vis, &item.ident, &item.generics), - _ => { - let msg = "Invalid pallet::origin, expected enum or struct or type"; - return Err(syn::Error::new(item.span(), msg)); - } - }; + pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { + let item_span = item.span(); + let (vis, ident, generics) = match &item { + syn::Item::Enum(item) => (&item.vis, &item.ident, &item.generics), + syn::Item::Struct(item) => (&item.vis, &item.ident, &item.generics), + syn::Item::Type(item) => (&item.vis, &item.ident, &item.generics), + _ => { + let msg = "Invalid pallet::origin, expected enum or struct or type"; + return Err(syn::Error::new(item.span(), msg)) + }, + }; - let has_instance = generics.params.len() == 2; - let is_generic = !generics.params.is_empty(); + let has_instance = generics.params.len() == 2; + let is_generic = !generics.params.is_empty(); - let mut instances = vec![]; - if let Some(u) = helper::check_type_def_optional_gen(generics, item.span())? { - instances.push(u); - } else { - // construct_runtime only allow generic event for instantiable pallet. - instances.push(helper::InstanceUsage { - has_instance: false, - span: ident.span(), - }) - } + let mut instances = vec![]; + if let Some(u) = helper::check_type_def_optional_gen(generics, item.span())? { + instances.push(u); + } else { + // construct_runtime only allow generic event for instantiable pallet. + instances.push(helper::InstanceUsage { has_instance: false, span: ident.span() }) + } - if !matches!(vis, syn::Visibility::Public(_)) { - let msg = "Invalid pallet::origin, Origin must be public"; - return Err(syn::Error::new(item_span, msg)); - } + if !matches!(vis, syn::Visibility::Public(_)) { + let msg = "Invalid pallet::origin, Origin must be public"; + return Err(syn::Error::new(item_span, msg)) + } - if ident != "Origin" { - let msg = "Invalid pallet::origin, ident must `Origin`"; - return Err(syn::Error::new(ident.span(), msg)); - } + if ident != "Origin" { + let msg = "Invalid pallet::origin, ident must `Origin`"; + return Err(syn::Error::new(ident.span(), msg)) + } - Ok(OriginDef { - index, - has_instance, - is_generic, - instances, - }) - } + Ok(OriginDef { index, has_instance, is_generic, instances }) + } } diff --git a/support/procedural-fork/src/pallet/parse/pallet_struct.rs b/support/procedural-fork/src/pallet/parse/pallet_struct.rs index 320cf01fa..b64576099 100644 --- a/support/procedural-fork/src/pallet/parse/pallet_struct.rs +++ b/support/procedural-fork/src/pallet/parse/pallet_struct.rs @@ -21,137 +21,129 @@ use syn::spanned::Spanned; /// List of additional token to be used for parsing. mod keyword { - syn::custom_keyword!(pallet); - syn::custom_keyword!(Pallet); - syn::custom_keyword!(without_storage_info); - syn::custom_keyword!(storage_version); + syn::custom_keyword!(pallet); + syn::custom_keyword!(Pallet); + syn::custom_keyword!(without_storage_info); + syn::custom_keyword!(storage_version); } /// Definition of the pallet pallet. pub struct PalletStructDef { - /// The index of item in pallet pallet. - pub index: usize, - /// A set of usage of instance, must be check for consistency with config trait. - pub instances: Vec, - /// The keyword Pallet used (contains span). - pub pallet: keyword::Pallet, - /// The span of the pallet::pallet attribute. - pub attr_span: proc_macro2::Span, - /// Whether to specify the storages max encoded len when implementing `StorageInfoTrait`. - /// Contains the span of the attribute. - pub without_storage_info: Option, - /// The in-code storage version of the pallet. - pub storage_version: Option, + /// The index of item in pallet pallet. + pub index: usize, + /// A set of usage of instance, must be check for consistency with config trait. + pub instances: Vec, + /// The keyword Pallet used (contains span). + pub pallet: keyword::Pallet, + /// The span of the pallet::pallet attribute. + pub attr_span: proc_macro2::Span, + /// Whether to specify the storages max encoded len when implementing `StorageInfoTrait`. + /// Contains the span of the attribute. + pub without_storage_info: Option, + /// The in-code storage version of the pallet. + pub storage_version: Option, } /// Parse for one variant of: /// * `#[pallet::without_storage_info]` /// * `#[pallet::storage_version(STORAGE_VERSION)]` pub enum PalletStructAttr { - WithoutStorageInfoTrait(proc_macro2::Span), - StorageVersion { - storage_version: syn::Path, - span: proc_macro2::Span, - }, + WithoutStorageInfoTrait(proc_macro2::Span), + StorageVersion { storage_version: syn::Path, span: proc_macro2::Span }, } impl PalletStructAttr { - fn span(&self) -> proc_macro2::Span { - match self { - Self::WithoutStorageInfoTrait(span) | Self::StorageVersion { span, .. } => *span, - } - } + fn span(&self) -> proc_macro2::Span { + match self { + Self::WithoutStorageInfoTrait(span) | Self::StorageVersion { span, .. } => *span, + } + } } impl syn::parse::Parse for PalletStructAttr { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - let content; - syn::bracketed!(content in input); - content.parse::()?; - content.parse::()?; - - let lookahead = content.lookahead1(); - if lookahead.peek(keyword::without_storage_info) { - let span = content.parse::()?.span(); - Ok(Self::WithoutStorageInfoTrait(span)) - } else if lookahead.peek(keyword::storage_version) { - let span = content.parse::()?.span(); - - let version_content; - syn::parenthesized!(version_content in content); - let storage_version = version_content.parse::()?; - - Ok(Self::StorageVersion { - storage_version, - span, - }) - } else { - Err(lookahead.error()) - } - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + let lookahead = content.lookahead1(); + if lookahead.peek(keyword::without_storage_info) { + let span = content.parse::()?.span(); + Ok(Self::WithoutStorageInfoTrait(span)) + } else if lookahead.peek(keyword::storage_version) { + let span = content.parse::()?.span(); + + let version_content; + syn::parenthesized!(version_content in content); + let storage_version = version_content.parse::()?; + + Ok(Self::StorageVersion { storage_version, span }) + } else { + Err(lookahead.error()) + } + } } impl PalletStructDef { - pub fn try_from( - attr_span: proc_macro2::Span, - index: usize, - item: &mut syn::Item, - ) -> syn::Result { - let item = if let syn::Item::Struct(item) = item { - item - } else { - let msg = "Invalid pallet::pallet, expected struct definition"; - return Err(syn::Error::new(item.span(), msg)); - }; - - let mut without_storage_info = None; - let mut storage_version_found = None; - - let struct_attrs: Vec = helper::take_item_pallet_attrs(&mut item.attrs)?; - for attr in struct_attrs { - match attr { - PalletStructAttr::WithoutStorageInfoTrait(span) - if without_storage_info.is_none() => - { - without_storage_info = Some(span); - } - PalletStructAttr::StorageVersion { - storage_version, .. - } if storage_version_found.is_none() => { - storage_version_found = Some(storage_version); - } - attr => { - let msg = "Unexpected duplicated attribute"; - return Err(syn::Error::new(attr.span(), msg)); - } - } - } - - let pallet = syn::parse2::(item.ident.to_token_stream())?; - - if !matches!(item.vis, syn::Visibility::Public(_)) { - let msg = "Invalid pallet::pallet, Pallet must be public"; - return Err(syn::Error::new(item.span(), msg)); - } - - if item.generics.where_clause.is_some() { - let msg = "Invalid pallet::pallet, where clause not supported on Pallet declaration"; - return Err(syn::Error::new(item.generics.where_clause.span(), msg)); - } - - let instances = vec![helper::check_type_def_gen_no_bounds( - &item.generics, - item.ident.span(), - )?]; - - Ok(Self { - index, - instances, - pallet, - attr_span, - without_storage_info, - storage_version: storage_version_found, - }) - } + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + ) -> syn::Result { + let item = if let syn::Item::Struct(item) = item { + item + } else { + let msg = "Invalid pallet::pallet, expected struct definition"; + return Err(syn::Error::new(item.span(), msg)) + }; + + let mut without_storage_info = None; + let mut storage_version_found = None; + + let struct_attrs: Vec = helper::take_item_pallet_attrs(&mut item.attrs)?; + for attr in struct_attrs { + match attr { + PalletStructAttr::WithoutStorageInfoTrait(span) + if without_storage_info.is_none() => + { + without_storage_info = Some(span); + }, + PalletStructAttr::StorageVersion { storage_version, .. } + if storage_version_found.is_none() => + { + storage_version_found = Some(storage_version); + }, + attr => { + let msg = "Unexpected duplicated attribute"; + return Err(syn::Error::new(attr.span(), msg)) + }, + } + } + + let pallet = syn::parse2::(item.ident.to_token_stream())?; + + if !matches!(item.vis, syn::Visibility::Public(_)) { + let msg = "Invalid pallet::pallet, Pallet must be public"; + return Err(syn::Error::new(item.span(), msg)) + } + + if item.generics.where_clause.is_some() { + let msg = "Invalid pallet::pallet, where clause not supported on Pallet declaration"; + return Err(syn::Error::new(item.generics.where_clause.span(), msg)) + } + + let instances = + vec![helper::check_type_def_gen_no_bounds(&item.generics, item.ident.span())?]; + + Ok(Self { + index, + instances, + pallet, + attr_span, + without_storage_info, + storage_version: storage_version_found, + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/storage.rs b/support/procedural-fork/src/pallet/parse/storage.rs index dac0782bd..9d96a18b5 100644 --- a/support/procedural-fork/src/pallet/parse/storage.rs +++ b/support/procedural-fork/src/pallet/parse/storage.rs @@ -23,16 +23,16 @@ use syn::spanned::Spanned; /// List of additional token to be used for parsing. mod keyword { - syn::custom_keyword!(Error); - syn::custom_keyword!(pallet); - syn::custom_keyword!(getter); - syn::custom_keyword!(storage_prefix); - syn::custom_keyword!(unbounded); - syn::custom_keyword!(whitelist_storage); - syn::custom_keyword!(disable_try_decode_storage); - syn::custom_keyword!(OptionQuery); - syn::custom_keyword!(ResultQuery); - syn::custom_keyword!(ValueQuery); + syn::custom_keyword!(Error); + syn::custom_keyword!(pallet); + syn::custom_keyword!(getter); + syn::custom_keyword!(storage_prefix); + syn::custom_keyword!(unbounded); + syn::custom_keyword!(whitelist_storage); + syn::custom_keyword!(disable_try_decode_storage); + syn::custom_keyword!(OptionQuery); + syn::custom_keyword!(ResultQuery); + syn::custom_keyword!(ValueQuery); } /// Parse for one of the following: @@ -42,1003 +42,906 @@ mod keyword { /// * `#[pallet::whitelist_storage] /// * `#[pallet::disable_try_decode_storage]` pub enum PalletStorageAttr { - Getter(syn::Ident, proc_macro2::Span), - StorageName(syn::LitStr, proc_macro2::Span), - Unbounded(proc_macro2::Span), - WhitelistStorage(proc_macro2::Span), - DisableTryDecodeStorage(proc_macro2::Span), + Getter(syn::Ident, proc_macro2::Span), + StorageName(syn::LitStr, proc_macro2::Span), + Unbounded(proc_macro2::Span), + WhitelistStorage(proc_macro2::Span), + DisableTryDecodeStorage(proc_macro2::Span), } impl PalletStorageAttr { - fn attr_span(&self) -> proc_macro2::Span { - match self { - Self::Getter(_, span) - | Self::StorageName(_, span) - | Self::Unbounded(span) - | Self::WhitelistStorage(span) => *span, - Self::DisableTryDecodeStorage(span) => *span, - } - } + fn attr_span(&self) -> proc_macro2::Span { + match self { + Self::Getter(_, span) | + Self::StorageName(_, span) | + Self::Unbounded(span) | + Self::WhitelistStorage(span) => *span, + Self::DisableTryDecodeStorage(span) => *span, + } + } } impl syn::parse::Parse for PalletStorageAttr { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - let attr_span = input.span(); - let content; - syn::bracketed!(content in input); - content.parse::()?; - content.parse::()?; - - let lookahead = content.lookahead1(); - if lookahead.peek(keyword::getter) { - content.parse::()?; - - let generate_content; - syn::parenthesized!(generate_content in content); - generate_content.parse::()?; - Ok(Self::Getter( - generate_content.parse::()?, - attr_span, - )) - } else if lookahead.peek(keyword::storage_prefix) { - content.parse::()?; - content.parse::()?; - - let renamed_prefix = content.parse::()?; - // Ensure the renamed prefix is a proper Rust identifier - syn::parse_str::(&renamed_prefix.value()).map_err(|_| { - let msg = format!("`{}` is not a valid identifier", renamed_prefix.value()); - syn::Error::new(renamed_prefix.span(), msg) - })?; - - Ok(Self::StorageName(renamed_prefix, attr_span)) - } else if lookahead.peek(keyword::unbounded) { - content.parse::()?; - - Ok(Self::Unbounded(attr_span)) - } else if lookahead.peek(keyword::whitelist_storage) { - content.parse::()?; - Ok(Self::WhitelistStorage(attr_span)) - } else if lookahead.peek(keyword::disable_try_decode_storage) { - content.parse::()?; - Ok(Self::DisableTryDecodeStorage(attr_span)) - } else { - Err(lookahead.error()) - } - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let attr_span = input.span(); + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + let lookahead = content.lookahead1(); + if lookahead.peek(keyword::getter) { + content.parse::()?; + + let generate_content; + syn::parenthesized!(generate_content in content); + generate_content.parse::()?; + Ok(Self::Getter(generate_content.parse::()?, attr_span)) + } else if lookahead.peek(keyword::storage_prefix) { + content.parse::()?; + content.parse::()?; + + let renamed_prefix = content.parse::()?; + // Ensure the renamed prefix is a proper Rust identifier + syn::parse_str::(&renamed_prefix.value()).map_err(|_| { + let msg = format!("`{}` is not a valid identifier", renamed_prefix.value()); + syn::Error::new(renamed_prefix.span(), msg) + })?; + + Ok(Self::StorageName(renamed_prefix, attr_span)) + } else if lookahead.peek(keyword::unbounded) { + content.parse::()?; + + Ok(Self::Unbounded(attr_span)) + } else if lookahead.peek(keyword::whitelist_storage) { + content.parse::()?; + Ok(Self::WhitelistStorage(attr_span)) + } else if lookahead.peek(keyword::disable_try_decode_storage) { + content.parse::()?; + Ok(Self::DisableTryDecodeStorage(attr_span)) + } else { + Err(lookahead.error()) + } + } } struct PalletStorageAttrInfo { - getter: Option, - rename_as: Option, - unbounded: bool, - whitelisted: bool, - try_decode: bool, + getter: Option, + rename_as: Option, + unbounded: bool, + whitelisted: bool, + try_decode: bool, } impl PalletStorageAttrInfo { - fn from_attrs(attrs: Vec) -> syn::Result { - let mut getter = None; - let mut rename_as = None; - let mut unbounded = false; - let mut whitelisted = false; - let mut disable_try_decode_storage = false; - for attr in attrs { - match attr { - PalletStorageAttr::Getter(ident, ..) if getter.is_none() => getter = Some(ident), - PalletStorageAttr::StorageName(name, ..) if rename_as.is_none() => { - rename_as = Some(name) - } - PalletStorageAttr::Unbounded(..) if !unbounded => unbounded = true, - PalletStorageAttr::WhitelistStorage(..) if !whitelisted => whitelisted = true, - PalletStorageAttr::DisableTryDecodeStorage(..) if !disable_try_decode_storage => { - disable_try_decode_storage = true - } - attr => { - return Err(syn::Error::new( - attr.attr_span(), - "Invalid attribute: Duplicate attribute", - )) - } - } - } - - Ok(PalletStorageAttrInfo { - getter, - rename_as, - unbounded, - whitelisted, - try_decode: !disable_try_decode_storage, - }) - } + fn from_attrs(attrs: Vec) -> syn::Result { + let mut getter = None; + let mut rename_as = None; + let mut unbounded = false; + let mut whitelisted = false; + let mut disable_try_decode_storage = false; + for attr in attrs { + match attr { + PalletStorageAttr::Getter(ident, ..) if getter.is_none() => getter = Some(ident), + PalletStorageAttr::StorageName(name, ..) if rename_as.is_none() => + rename_as = Some(name), + PalletStorageAttr::Unbounded(..) if !unbounded => unbounded = true, + PalletStorageAttr::WhitelistStorage(..) if !whitelisted => whitelisted = true, + PalletStorageAttr::DisableTryDecodeStorage(..) if !disable_try_decode_storage => + disable_try_decode_storage = true, + attr => + return Err(syn::Error::new( + attr.attr_span(), + "Invalid attribute: Duplicate attribute", + )), + } + } + + Ok(PalletStorageAttrInfo { + getter, + rename_as, + unbounded, + whitelisted, + try_decode: !disable_try_decode_storage, + }) + } } /// The value and key types used by storages. Needed to expand metadata. pub enum Metadata { - Value { - value: syn::Type, - }, - Map { - value: syn::Type, - key: syn::Type, - }, - CountedMap { - value: syn::Type, - key: syn::Type, - }, - DoubleMap { - value: syn::Type, - key1: syn::Type, - key2: syn::Type, - }, - NMap { - keys: Vec, - keygen: syn::Type, - value: syn::Type, - }, - CountedNMap { - keys: Vec, - keygen: syn::Type, - value: syn::Type, - }, + Value { value: syn::Type }, + Map { value: syn::Type, key: syn::Type }, + CountedMap { value: syn::Type, key: syn::Type }, + DoubleMap { value: syn::Type, key1: syn::Type, key2: syn::Type }, + NMap { keys: Vec, keygen: syn::Type, value: syn::Type }, + CountedNMap { keys: Vec, keygen: syn::Type, value: syn::Type }, } pub enum QueryKind { - OptionQuery, - ResultQuery(syn::Path, syn::Ident), - ValueQuery, + OptionQuery, + ResultQuery(syn::Path, syn::Ident), + ValueQuery, } /// Definition of a storage, storage is a storage type like /// `type MyStorage = StorageValue` /// The keys and values types are parsed in order to get metadata pub struct StorageDef { - /// The index of storage item in pallet module. - pub index: usize, - /// Visibility of the storage type. - pub vis: syn::Visibility, - /// The type ident, to generate the StoragePrefix for. - pub ident: syn::Ident, - /// The keys and value metadata of the storage. - pub metadata: Metadata, - /// The doc associated to the storage. - pub docs: Vec, - /// A set of usage of instance, must be check for consistency with config. - pub instances: Vec, - /// Optional getter to generate. If some then query_kind is ensured to be some as well. - pub getter: Option, - /// Optional expression that evaluates to a type that can be used as StoragePrefix instead of - /// ident. - pub rename_as: Option, - /// Whereas the querytype of the storage is OptionQuery, ResultQuery or ValueQuery. - /// Note that this is best effort as it can't be determined when QueryKind is generic, and - /// result can be false if user do some unexpected type alias. - pub query_kind: Option, - /// Where clause of type definition. - pub where_clause: Option, - /// The span of the pallet::storage attribute. - pub attr_span: proc_macro2::Span, - /// The `cfg` attributes. - pub cfg_attrs: Vec, - /// If generics are named (e.g. `StorageValue`) then this contains all the - /// generics of the storage. - /// If generics are not named, this is none. - pub named_generics: Option, - /// If the value stored in this storage is unbounded. - pub unbounded: bool, - /// Whether or not reads to this storage key will be ignored by benchmarking - pub whitelisted: bool, - /// Whether or not to try to decode the storage key when running try-runtime checks. - pub try_decode: bool, - /// Whether or not a default hasher is allowed to replace `_` - pub use_default_hasher: bool, + /// The index of storage item in pallet module. + pub index: usize, + /// Visibility of the storage type. + pub vis: syn::Visibility, + /// The type ident, to generate the StoragePrefix for. + pub ident: syn::Ident, + /// The keys and value metadata of the storage. + pub metadata: Metadata, + /// The doc associated to the storage. + pub docs: Vec, + /// A set of usage of instance, must be check for consistency with config. + pub instances: Vec, + /// Optional getter to generate. If some then query_kind is ensured to be some as well. + pub getter: Option, + /// Optional expression that evaluates to a type that can be used as StoragePrefix instead of + /// ident. + pub rename_as: Option, + /// Whereas the querytype of the storage is OptionQuery, ResultQuery or ValueQuery. + /// Note that this is best effort as it can't be determined when QueryKind is generic, and + /// result can be false if user do some unexpected type alias. + pub query_kind: Option, + /// Where clause of type definition. + pub where_clause: Option, + /// The span of the pallet::storage attribute. + pub attr_span: proc_macro2::Span, + /// The `cfg` attributes. + pub cfg_attrs: Vec, + /// If generics are named (e.g. `StorageValue`) then this contains all the + /// generics of the storage. + /// If generics are not named, this is none. + pub named_generics: Option, + /// If the value stored in this storage is unbounded. + pub unbounded: bool, + /// Whether or not reads to this storage key will be ignored by benchmarking + pub whitelisted: bool, + /// Whether or not to try to decode the storage key when running try-runtime checks. + pub try_decode: bool, + /// Whether or not a default hasher is allowed to replace `_` + pub use_default_hasher: bool, } /// The parsed generic from the #[derive(Clone)] pub enum StorageGenerics { - DoubleMap { - hasher1: syn::Type, - key1: syn::Type, - hasher2: syn::Type, - key2: syn::Type, - value: syn::Type, - query_kind: Option, - on_empty: Option, - max_values: Option, - }, - Map { - hasher: syn::Type, - key: syn::Type, - value: syn::Type, - query_kind: Option, - on_empty: Option, - max_values: Option, - }, - CountedMap { - hasher: syn::Type, - key: syn::Type, - value: syn::Type, - query_kind: Option, - on_empty: Option, - max_values: Option, - }, - Value { - value: syn::Type, - query_kind: Option, - on_empty: Option, - }, - NMap { - keygen: syn::Type, - value: syn::Type, - query_kind: Option, - on_empty: Option, - max_values: Option, - }, - CountedNMap { - keygen: syn::Type, - value: syn::Type, - query_kind: Option, - on_empty: Option, - max_values: Option, - }, + DoubleMap { + hasher1: syn::Type, + key1: syn::Type, + hasher2: syn::Type, + key2: syn::Type, + value: syn::Type, + query_kind: Option, + on_empty: Option, + max_values: Option, + }, + Map { + hasher: syn::Type, + key: syn::Type, + value: syn::Type, + query_kind: Option, + on_empty: Option, + max_values: Option, + }, + CountedMap { + hasher: syn::Type, + key: syn::Type, + value: syn::Type, + query_kind: Option, + on_empty: Option, + max_values: Option, + }, + Value { + value: syn::Type, + query_kind: Option, + on_empty: Option, + }, + NMap { + keygen: syn::Type, + value: syn::Type, + query_kind: Option, + on_empty: Option, + max_values: Option, + }, + CountedNMap { + keygen: syn::Type, + value: syn::Type, + query_kind: Option, + on_empty: Option, + max_values: Option, + }, } impl StorageGenerics { - /// Return the metadata from the defined generics - fn metadata(&self) -> syn::Result { - let res = match self.clone() { - Self::DoubleMap { - value, key1, key2, .. - } => Metadata::DoubleMap { value, key1, key2 }, - Self::Map { value, key, .. } => Metadata::Map { value, key }, - Self::CountedMap { value, key, .. } => Metadata::CountedMap { value, key }, - Self::Value { value, .. } => Metadata::Value { value }, - Self::NMap { keygen, value, .. } => Metadata::NMap { - keys: collect_keys(&keygen)?, - keygen, - value, - }, - Self::CountedNMap { keygen, value, .. } => Metadata::CountedNMap { - keys: collect_keys(&keygen)?, - keygen, - value, - }, - }; - - Ok(res) - } - - /// Return the query kind from the defined generics - fn query_kind(&self) -> Option { - match &self { - Self::DoubleMap { query_kind, .. } - | Self::Map { query_kind, .. } - | Self::CountedMap { query_kind, .. } - | Self::Value { query_kind, .. } - | Self::NMap { query_kind, .. } - | Self::CountedNMap { query_kind, .. } => query_kind.clone(), - } - } + /// Return the metadata from the defined generics + fn metadata(&self) -> syn::Result { + let res = match self.clone() { + Self::DoubleMap { value, key1, key2, .. } => Metadata::DoubleMap { value, key1, key2 }, + Self::Map { value, key, .. } => Metadata::Map { value, key }, + Self::CountedMap { value, key, .. } => Metadata::CountedMap { value, key }, + Self::Value { value, .. } => Metadata::Value { value }, + Self::NMap { keygen, value, .. } => + Metadata::NMap { keys: collect_keys(&keygen)?, keygen, value }, + Self::CountedNMap { keygen, value, .. } => + Metadata::CountedNMap { keys: collect_keys(&keygen)?, keygen, value }, + }; + + Ok(res) + } + + /// Return the query kind from the defined generics + fn query_kind(&self) -> Option { + match &self { + Self::DoubleMap { query_kind, .. } | + Self::Map { query_kind, .. } | + Self::CountedMap { query_kind, .. } | + Self::Value { query_kind, .. } | + Self::NMap { query_kind, .. } | + Self::CountedNMap { query_kind, .. } => query_kind.clone(), + } + } } enum StorageKind { - Value, - Map, - CountedMap, - DoubleMap, - NMap, - CountedNMap, + Value, + Map, + CountedMap, + DoubleMap, + NMap, + CountedNMap, } /// Check the generics in the `map` contains the generics in `gen` may contains generics in /// `optional_gen`, and doesn't contains any other. fn check_generics( - map: &HashMap, - mandatory_generics: &[&str], - optional_generics: &[&str], - storage_type_name: &str, - args_span: proc_macro2::Span, + map: &HashMap, + mandatory_generics: &[&str], + optional_generics: &[&str], + storage_type_name: &str, + args_span: proc_macro2::Span, ) -> syn::Result<()> { - let mut errors = vec![]; - - let expectation = { - let mut e = format!( - "`{}` expect generics {}and optional generics {}", - storage_type_name, - mandatory_generics - .iter() - .map(|name| format!("`{}`, ", name)) - .collect::(), - &optional_generics - .iter() - .map(|name| format!("`{}`, ", name)) - .collect::(), - ); - e.pop(); - e.pop(); - e.push('.'); - e - }; - - for (gen_name, gen_binding) in map { - if !mandatory_generics.contains(&gen_name.as_str()) - && !optional_generics.contains(&gen_name.as_str()) - { - let msg = format!( - "Invalid pallet::storage, Unexpected generic `{}` for `{}`. {}", - gen_name, storage_type_name, expectation, - ); - errors.push(syn::Error::new(gen_binding.span(), msg)); - } - } - - for mandatory_generic in mandatory_generics { - if !map.contains_key(&mandatory_generic.to_string()) { - let msg = format!( - "Invalid pallet::storage, cannot find `{}` generic, required for `{}`.", - mandatory_generic, storage_type_name - ); - errors.push(syn::Error::new(args_span, msg)); - } - } - - let mut errors = errors.drain(..); - if let Some(mut error) = errors.next() { - for other_error in errors { - error.combine(other_error); - } - Err(error) - } else { - Ok(()) - } + let mut errors = vec![]; + + let expectation = { + let mut e = format!( + "`{}` expect generics {}and optional generics {}", + storage_type_name, + mandatory_generics + .iter() + .map(|name| format!("`{}`, ", name)) + .collect::(), + &optional_generics.iter().map(|name| format!("`{}`, ", name)).collect::(), + ); + e.pop(); + e.pop(); + e.push('.'); + e + }; + + for (gen_name, gen_binding) in map { + if !mandatory_generics.contains(&gen_name.as_str()) && + !optional_generics.contains(&gen_name.as_str()) + { + let msg = format!( + "Invalid pallet::storage, Unexpected generic `{}` for `{}`. {}", + gen_name, storage_type_name, expectation, + ); + errors.push(syn::Error::new(gen_binding.span(), msg)); + } + } + + for mandatory_generic in mandatory_generics { + if !map.contains_key(&mandatory_generic.to_string()) { + let msg = format!( + "Invalid pallet::storage, cannot find `{}` generic, required for `{}`.", + mandatory_generic, storage_type_name + ); + errors.push(syn::Error::new(args_span, msg)); + } + } + + let mut errors = errors.drain(..); + if let Some(mut error) = errors.next() { + for other_error in errors { + error.combine(other_error); + } + Err(error) + } else { + Ok(()) + } } /// Returns `(named generics, metadata, query kind, use_default_hasher)` fn process_named_generics( - storage: &StorageKind, - args_span: proc_macro2::Span, - args: &[syn::AssocType], - dev_mode: bool, + storage: &StorageKind, + args_span: proc_macro2::Span, + args: &[syn::AssocType], + dev_mode: bool, ) -> syn::Result<(Option, Metadata, Option, bool)> { - let mut parsed = HashMap::::new(); - - // Ensure no duplicate. - for arg in args { - if let Some(other) = parsed.get(&arg.ident.to_string()) { - let msg = "Invalid pallet::storage, Duplicated named generic"; - let mut err = syn::Error::new(arg.ident.span(), msg); - err.combine(syn::Error::new(other.ident.span(), msg)); - return Err(err); - } - parsed.insert(arg.ident.to_string(), arg.clone()); - } - - let mut map_mandatory_generics = vec!["Key", "Value"]; - let mut map_optional_generics = vec!["QueryKind", "OnEmpty", "MaxValues"]; - if dev_mode { - map_optional_generics.push("Hasher"); - } else { - map_mandatory_generics.push("Hasher"); - } - - let generics = match storage { - StorageKind::Value => { - check_generics( - &parsed, - &["Value"], - &["QueryKind", "OnEmpty"], - "StorageValue", - args_span, - )?; - - StorageGenerics::Value { - value: parsed - .remove("Value") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), - on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), - } - } - StorageKind::Map => { - check_generics( - &parsed, - &map_mandatory_generics, - &map_optional_generics, - "StorageMap", - args_span, - )?; - - StorageGenerics::Map { - hasher: parsed - .remove("Hasher") - .map(|binding| binding.ty) - .unwrap_or(syn::parse_quote!(Blake2_128Concat)), - key: parsed - .remove("Key") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - value: parsed - .remove("Value") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), - on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), - max_values: parsed.remove("MaxValues").map(|binding| binding.ty), - } - } - StorageKind::CountedMap => { - check_generics( - &parsed, - &map_mandatory_generics, - &map_optional_generics, - "CountedStorageMap", - args_span, - )?; - - StorageGenerics::CountedMap { - hasher: parsed - .remove("Hasher") - .map(|binding| binding.ty) - .unwrap_or(syn::Type::Verbatim(quote::quote! { Blake2_128Concat })), - key: parsed - .remove("Key") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - value: parsed - .remove("Value") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), - on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), - max_values: parsed.remove("MaxValues").map(|binding| binding.ty), - } - } - StorageKind::DoubleMap => { - let mut double_map_mandatory_generics = vec!["Key1", "Key2", "Value"]; - if dev_mode { - map_optional_generics.extend(["Hasher1", "Hasher2"]); - } else { - double_map_mandatory_generics.extend(["Hasher1", "Hasher2"]); - } - - check_generics( - &parsed, - &double_map_mandatory_generics, - &map_optional_generics, - "StorageDoubleMap", - args_span, - )?; - - StorageGenerics::DoubleMap { - hasher1: parsed - .remove("Hasher1") - .map(|binding| binding.ty) - .unwrap_or(syn::parse_quote!(Blake2_128Concat)), - key1: parsed - .remove("Key1") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - hasher2: parsed - .remove("Hasher2") - .map(|binding| binding.ty) - .unwrap_or(syn::parse_quote!(Blake2_128Concat)), - key2: parsed - .remove("Key2") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - value: parsed - .remove("Value") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), - on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), - max_values: parsed.remove("MaxValues").map(|binding| binding.ty), - } - } - StorageKind::NMap => { - check_generics( - &parsed, - &["Key", "Value"], - &["QueryKind", "OnEmpty", "MaxValues"], - "StorageNMap", - args_span, - )?; - - StorageGenerics::NMap { - keygen: parsed - .remove("Key") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - value: parsed - .remove("Value") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), - on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), - max_values: parsed.remove("MaxValues").map(|binding| binding.ty), - } - } - StorageKind::CountedNMap => { - check_generics( - &parsed, - &["Key", "Value"], - &["QueryKind", "OnEmpty", "MaxValues"], - "CountedStorageNMap", - args_span, - )?; - - StorageGenerics::CountedNMap { - keygen: parsed - .remove("Key") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - value: parsed - .remove("Value") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), - on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), - max_values: parsed.remove("MaxValues").map(|binding| binding.ty), - } - } - }; - - let metadata = generics.metadata()?; - let query_kind = generics.query_kind(); - - Ok((Some(generics), metadata, query_kind, false)) + let mut parsed = HashMap::::new(); + + // Ensure no duplicate. + for arg in args { + if let Some(other) = parsed.get(&arg.ident.to_string()) { + let msg = "Invalid pallet::storage, Duplicated named generic"; + let mut err = syn::Error::new(arg.ident.span(), msg); + err.combine(syn::Error::new(other.ident.span(), msg)); + return Err(err) + } + parsed.insert(arg.ident.to_string(), arg.clone()); + } + + let mut map_mandatory_generics = vec!["Key", "Value"]; + let mut map_optional_generics = vec!["QueryKind", "OnEmpty", "MaxValues"]; + if dev_mode { + map_optional_generics.push("Hasher"); + } else { + map_mandatory_generics.push("Hasher"); + } + + let generics = match storage { + StorageKind::Value => { + check_generics( + &parsed, + &["Value"], + &["QueryKind", "OnEmpty"], + "StorageValue", + args_span, + )?; + + StorageGenerics::Value { + value: parsed + .remove("Value") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), + on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), + } + }, + StorageKind::Map => { + check_generics( + &parsed, + &map_mandatory_generics, + &map_optional_generics, + "StorageMap", + args_span, + )?; + + StorageGenerics::Map { + hasher: parsed + .remove("Hasher") + .map(|binding| binding.ty) + .unwrap_or(syn::parse_quote!(Blake2_128Concat)), + key: parsed + .remove("Key") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + value: parsed + .remove("Value") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), + on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), + max_values: parsed.remove("MaxValues").map(|binding| binding.ty), + } + }, + StorageKind::CountedMap => { + check_generics( + &parsed, + &map_mandatory_generics, + &map_optional_generics, + "CountedStorageMap", + args_span, + )?; + + StorageGenerics::CountedMap { + hasher: parsed + .remove("Hasher") + .map(|binding| binding.ty) + .unwrap_or(syn::Type::Verbatim(quote::quote! { Blake2_128Concat })), + key: parsed + .remove("Key") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + value: parsed + .remove("Value") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), + on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), + max_values: parsed.remove("MaxValues").map(|binding| binding.ty), + } + }, + StorageKind::DoubleMap => { + let mut double_map_mandatory_generics = vec!["Key1", "Key2", "Value"]; + if dev_mode { + map_optional_generics.extend(["Hasher1", "Hasher2"]); + } else { + double_map_mandatory_generics.extend(["Hasher1", "Hasher2"]); + } + + check_generics( + &parsed, + &double_map_mandatory_generics, + &map_optional_generics, + "StorageDoubleMap", + args_span, + )?; + + StorageGenerics::DoubleMap { + hasher1: parsed + .remove("Hasher1") + .map(|binding| binding.ty) + .unwrap_or(syn::parse_quote!(Blake2_128Concat)), + key1: parsed + .remove("Key1") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + hasher2: parsed + .remove("Hasher2") + .map(|binding| binding.ty) + .unwrap_or(syn::parse_quote!(Blake2_128Concat)), + key2: parsed + .remove("Key2") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + value: parsed + .remove("Value") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), + on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), + max_values: parsed.remove("MaxValues").map(|binding| binding.ty), + } + }, + StorageKind::NMap => { + check_generics( + &parsed, + &["Key", "Value"], + &["QueryKind", "OnEmpty", "MaxValues"], + "StorageNMap", + args_span, + )?; + + StorageGenerics::NMap { + keygen: parsed + .remove("Key") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + value: parsed + .remove("Value") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), + on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), + max_values: parsed.remove("MaxValues").map(|binding| binding.ty), + } + }, + StorageKind::CountedNMap => { + check_generics( + &parsed, + &["Key", "Value"], + &["QueryKind", "OnEmpty", "MaxValues"], + "CountedStorageNMap", + args_span, + )?; + + StorageGenerics::CountedNMap { + keygen: parsed + .remove("Key") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + value: parsed + .remove("Value") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), + on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), + max_values: parsed.remove("MaxValues").map(|binding| binding.ty), + } + }, + }; + + let metadata = generics.metadata()?; + let query_kind = generics.query_kind(); + + Ok((Some(generics), metadata, query_kind, false)) } /// Returns `(named generics, metadata, query kind, use_default_hasher)` fn process_unnamed_generics( - storage: &StorageKind, - args_span: proc_macro2::Span, - args: &[syn::Type], - dev_mode: bool, + storage: &StorageKind, + args_span: proc_macro2::Span, + args: &[syn::Type], + dev_mode: bool, ) -> syn::Result<(Option, Metadata, Option, bool)> { - let retrieve_arg = |arg_pos| { - args.get(arg_pos).cloned().ok_or_else(|| { - let msg = format!( - "Invalid pallet::storage, unexpected number of generic argument, \ + let retrieve_arg = |arg_pos| { + args.get(arg_pos).cloned().ok_or_else(|| { + let msg = format!( + "Invalid pallet::storage, unexpected number of generic argument, \ expect at least {} args, found {}.", - arg_pos + 1, - args.len(), - ); - syn::Error::new(args_span, msg) - }) - }; - - let prefix_arg = retrieve_arg(0)?; - syn::parse2::(prefix_arg.to_token_stream()).map_err(|e| { - let msg = "Invalid pallet::storage, for unnamed generic arguments the type \ + arg_pos + 1, + args.len(), + ); + syn::Error::new(args_span, msg) + }) + }; + + let prefix_arg = retrieve_arg(0)?; + syn::parse2::(prefix_arg.to_token_stream()).map_err(|e| { + let msg = "Invalid pallet::storage, for unnamed generic arguments the type \ first generic argument must be `_`, the argument is then replaced by macro."; - let mut err = syn::Error::new(prefix_arg.span(), msg); - err.combine(e); - err - })?; - - let use_default_hasher = |arg_pos| { - let arg = retrieve_arg(arg_pos)?; - if syn::parse2::(arg.to_token_stream()).is_ok() { - if dev_mode { - Ok(true) - } else { - let msg = "`_` can only be used in dev_mode. Please specify an appropriate hasher."; - Err(syn::Error::new(arg.span(), msg)) - } - } else { - Ok(false) - } - }; - - let res = match storage { - StorageKind::Value => ( - None, - Metadata::Value { - value: retrieve_arg(1)?, - }, - retrieve_arg(2).ok(), - false, - ), - StorageKind::Map => ( - None, - Metadata::Map { - key: retrieve_arg(2)?, - value: retrieve_arg(3)?, - }, - retrieve_arg(4).ok(), - use_default_hasher(1)?, - ), - StorageKind::CountedMap => ( - None, - Metadata::CountedMap { - key: retrieve_arg(2)?, - value: retrieve_arg(3)?, - }, - retrieve_arg(4).ok(), - use_default_hasher(1)?, - ), - StorageKind::DoubleMap => ( - None, - Metadata::DoubleMap { - key1: retrieve_arg(2)?, - key2: retrieve_arg(4)?, - value: retrieve_arg(5)?, - }, - retrieve_arg(6).ok(), - use_default_hasher(1)? && use_default_hasher(3)?, - ), - StorageKind::NMap => { - let keygen = retrieve_arg(1)?; - let keys = collect_keys(&keygen)?; - ( - None, - Metadata::NMap { - keys, - keygen, - value: retrieve_arg(2)?, - }, - retrieve_arg(3).ok(), - false, - ) - } - StorageKind::CountedNMap => { - let keygen = retrieve_arg(1)?; - let keys = collect_keys(&keygen)?; - ( - None, - Metadata::CountedNMap { - keys, - keygen, - value: retrieve_arg(2)?, - }, - retrieve_arg(3).ok(), - false, - ) - } - }; - - Ok(res) + let mut err = syn::Error::new(prefix_arg.span(), msg); + err.combine(e); + err + })?; + + let use_default_hasher = |arg_pos| { + let arg = retrieve_arg(arg_pos)?; + if syn::parse2::(arg.to_token_stream()).is_ok() { + if dev_mode { + Ok(true) + } else { + let msg = "`_` can only be used in dev_mode. Please specify an appropriate hasher."; + Err(syn::Error::new(arg.span(), msg)) + } + } else { + Ok(false) + } + }; + + let res = match storage { + StorageKind::Value => + (None, Metadata::Value { value: retrieve_arg(1)? }, retrieve_arg(2).ok(), false), + StorageKind::Map => ( + None, + Metadata::Map { key: retrieve_arg(2)?, value: retrieve_arg(3)? }, + retrieve_arg(4).ok(), + use_default_hasher(1)?, + ), + StorageKind::CountedMap => ( + None, + Metadata::CountedMap { key: retrieve_arg(2)?, value: retrieve_arg(3)? }, + retrieve_arg(4).ok(), + use_default_hasher(1)?, + ), + StorageKind::DoubleMap => ( + None, + Metadata::DoubleMap { + key1: retrieve_arg(2)?, + key2: retrieve_arg(4)?, + value: retrieve_arg(5)?, + }, + retrieve_arg(6).ok(), + use_default_hasher(1)? && use_default_hasher(3)?, + ), + StorageKind::NMap => { + let keygen = retrieve_arg(1)?; + let keys = collect_keys(&keygen)?; + ( + None, + Metadata::NMap { keys, keygen, value: retrieve_arg(2)? }, + retrieve_arg(3).ok(), + false, + ) + }, + StorageKind::CountedNMap => { + let keygen = retrieve_arg(1)?; + let keys = collect_keys(&keygen)?; + ( + None, + Metadata::CountedNMap { keys, keygen, value: retrieve_arg(2)? }, + retrieve_arg(3).ok(), + false, + ) + }, + }; + + Ok(res) } /// Returns `(named generics, metadata, query kind, use_default_hasher)` fn process_generics( - segment: &syn::PathSegment, - dev_mode: bool, + segment: &syn::PathSegment, + dev_mode: bool, ) -> syn::Result<(Option, Metadata, Option, bool)> { - let storage_kind = match &*segment.ident.to_string() { - "StorageValue" => StorageKind::Value, - "StorageMap" => StorageKind::Map, - "CountedStorageMap" => StorageKind::CountedMap, - "StorageDoubleMap" => StorageKind::DoubleMap, - "StorageNMap" => StorageKind::NMap, - "CountedStorageNMap" => StorageKind::CountedNMap, - found => { - let msg = format!( + let storage_kind = match &*segment.ident.to_string() { + "StorageValue" => StorageKind::Value, + "StorageMap" => StorageKind::Map, + "CountedStorageMap" => StorageKind::CountedMap, + "StorageDoubleMap" => StorageKind::DoubleMap, + "StorageNMap" => StorageKind::NMap, + "CountedStorageNMap" => StorageKind::CountedNMap, + found => { + let msg = format!( "Invalid pallet::storage, expected ident: `StorageValue` or \ `StorageMap` or `CountedStorageMap` or `StorageDoubleMap` or `StorageNMap` or `CountedStorageNMap` \ in order to expand metadata, found `{}`.", found, ); - return Err(syn::Error::new(segment.ident.span(), msg)); - } - }; + return Err(syn::Error::new(segment.ident.span(), msg)) + }, + }; - let args_span = segment.arguments.span(); + let args_span = segment.arguments.span(); - let args = match &segment.arguments { - syn::PathArguments::AngleBracketed(args) if !args.args.is_empty() => args, - _ => { - let msg = "Invalid pallet::storage, invalid number of generic generic arguments, \ + let args = match &segment.arguments { + syn::PathArguments::AngleBracketed(args) if !args.args.is_empty() => args, + _ => { + let msg = "Invalid pallet::storage, invalid number of generic generic arguments, \ expect more that 0 generic arguments."; - return Err(syn::Error::new(segment.span(), msg)); - } - }; - - if args - .args - .iter() - .all(|gen| matches!(gen, syn::GenericArgument::Type(_))) - { - let args = args - .args - .iter() - .map(|gen| match gen { - syn::GenericArgument::Type(gen) => gen.clone(), - _ => unreachable!("It is asserted above that all generics are types"), - }) - .collect::>(); - process_unnamed_generics(&storage_kind, args_span, &args, dev_mode) - } else if args - .args - .iter() - .all(|gen| matches!(gen, syn::GenericArgument::AssocType(_))) - { - let args = args - .args - .iter() - .map(|gen| match gen { - syn::GenericArgument::AssocType(gen) => gen.clone(), - _ => unreachable!("It is asserted above that all generics are bindings"), - }) - .collect::>(); - process_named_generics(&storage_kind, args_span, &args, dev_mode) - } else { - let msg = "Invalid pallet::storage, invalid generic declaration for storage. Expect only \ + return Err(syn::Error::new(segment.span(), msg)) + }, + }; + + if args.args.iter().all(|gen| matches!(gen, syn::GenericArgument::Type(_))) { + let args = args + .args + .iter() + .map(|gen| match gen { + syn::GenericArgument::Type(gen) => gen.clone(), + _ => unreachable!("It is asserted above that all generics are types"), + }) + .collect::>(); + process_unnamed_generics(&storage_kind, args_span, &args, dev_mode) + } else if args.args.iter().all(|gen| matches!(gen, syn::GenericArgument::AssocType(_))) { + let args = args + .args + .iter() + .map(|gen| match gen { + syn::GenericArgument::AssocType(gen) => gen.clone(), + _ => unreachable!("It is asserted above that all generics are bindings"), + }) + .collect::>(); + process_named_generics(&storage_kind, args_span, &args, dev_mode) + } else { + let msg = "Invalid pallet::storage, invalid generic declaration for storage. Expect only \ type generics or binding generics, e.g. `` or \ ``."; - Err(syn::Error::new(segment.span(), msg)) - } + Err(syn::Error::new(segment.span(), msg)) + } } /// Parse the 2nd type argument to `StorageNMap` and return its keys. fn collect_keys(keygen: &syn::Type) -> syn::Result> { - if let syn::Type::Tuple(tup) = keygen { - tup.elems - .iter() - .map(extract_key) - .collect::>>() - } else { - Ok(vec![extract_key(keygen)?]) - } + if let syn::Type::Tuple(tup) = keygen { + tup.elems.iter().map(extract_key).collect::>>() + } else { + Ok(vec![extract_key(keygen)?]) + } } /// In `Key`, extract K and return it. fn extract_key(ty: &syn::Type) -> syn::Result { - let typ = if let syn::Type::Path(typ) = ty { - typ - } else { - let msg = "Invalid pallet::storage, expected type path"; - return Err(syn::Error::new(ty.span(), msg)); - }; - - let key_struct = typ.path.segments.last().ok_or_else(|| { - let msg = "Invalid pallet::storage, expected type path with at least one segment"; - syn::Error::new(typ.path.span(), msg) - })?; - if key_struct.ident != "Key" && key_struct.ident != "NMapKey" { - let msg = "Invalid pallet::storage, expected Key or NMapKey struct"; - return Err(syn::Error::new(key_struct.ident.span(), msg)); - } - - let ty_params = if let syn::PathArguments::AngleBracketed(args) = &key_struct.arguments { - args - } else { - let msg = "Invalid pallet::storage, expected angle bracketed arguments"; - return Err(syn::Error::new(key_struct.arguments.span(), msg)); - }; - - if ty_params.args.len() != 2 { - let msg = format!( - "Invalid pallet::storage, unexpected number of generic arguments \ + let typ = if let syn::Type::Path(typ) = ty { + typ + } else { + let msg = "Invalid pallet::storage, expected type path"; + return Err(syn::Error::new(ty.span(), msg)) + }; + + let key_struct = typ.path.segments.last().ok_or_else(|| { + let msg = "Invalid pallet::storage, expected type path with at least one segment"; + syn::Error::new(typ.path.span(), msg) + })?; + if key_struct.ident != "Key" && key_struct.ident != "NMapKey" { + let msg = "Invalid pallet::storage, expected Key or NMapKey struct"; + return Err(syn::Error::new(key_struct.ident.span(), msg)) + } + + let ty_params = if let syn::PathArguments::AngleBracketed(args) = &key_struct.arguments { + args + } else { + let msg = "Invalid pallet::storage, expected angle bracketed arguments"; + return Err(syn::Error::new(key_struct.arguments.span(), msg)) + }; + + if ty_params.args.len() != 2 { + let msg = format!( + "Invalid pallet::storage, unexpected number of generic arguments \ for Key struct, expected 2 args, found {}", - ty_params.args.len() - ); - return Err(syn::Error::new(ty_params.span(), msg)); - } - - let key = match &ty_params.args[1] { - syn::GenericArgument::Type(key_ty) => key_ty.clone(), - _ => { - let msg = "Invalid pallet::storage, expected type"; - return Err(syn::Error::new(ty_params.args[1].span(), msg)); - } - }; - - Ok(key) + ty_params.args.len() + ); + return Err(syn::Error::new(ty_params.span(), msg)) + } + + let key = match &ty_params.args[1] { + syn::GenericArgument::Type(key_ty) => key_ty.clone(), + _ => { + let msg = "Invalid pallet::storage, expected type"; + return Err(syn::Error::new(ty_params.args[1].span(), msg)) + }, + }; + + Ok(key) } impl StorageDef { - /// Return the storage prefix for this storage item - pub fn prefix(&self) -> String { - self.rename_as - .as_ref() - .map(syn::LitStr::value) - .unwrap_or_else(|| self.ident.to_string()) - } - - /// Return either the span of the ident or the span of the literal in the - /// #[storage_prefix] attribute - pub fn prefix_span(&self) -> proc_macro2::Span { - self.rename_as - .as_ref() - .map(syn::LitStr::span) - .unwrap_or_else(|| self.ident.span()) - } - - pub fn try_from( - attr_span: proc_macro2::Span, - index: usize, - item: &mut syn::Item, - dev_mode: bool, - ) -> syn::Result { - let item = if let syn::Item::Type(item) = item { - item - } else { - return Err(syn::Error::new( - item.span(), - "Invalid pallet::storage, expect item type.", - )); - }; - - let attrs: Vec = helper::take_item_pallet_attrs(&mut item.attrs)?; - let PalletStorageAttrInfo { - getter, - rename_as, - mut unbounded, - whitelisted, - try_decode, - } = PalletStorageAttrInfo::from_attrs(attrs)?; - - // set all storages to be unbounded if dev_mode is enabled - unbounded |= dev_mode; - let cfg_attrs = helper::get_item_cfg_attrs(&item.attrs); - - let instances = vec![helper::check_type_def_gen( - &item.generics, - item.ident.span(), - )?]; - - let where_clause = item.generics.where_clause.clone(); - let docs = get_doc_literals(&item.attrs); - - let typ = if let syn::Type::Path(typ) = &*item.ty { - typ - } else { - let msg = "Invalid pallet::storage, expected type path"; - return Err(syn::Error::new(item.ty.span(), msg)); - }; - - if typ.path.segments.len() != 1 { - let msg = "Invalid pallet::storage, expected type path with one segment"; - return Err(syn::Error::new(item.ty.span(), msg)); - } - - let (named_generics, metadata, query_kind, use_default_hasher) = - process_generics(&typ.path.segments[0], dev_mode)?; - - let query_kind = query_kind - .map(|query_kind| { - use syn::{ - AngleBracketedGenericArguments, GenericArgument, Path, PathArguments, Type, - TypePath, - }; - - let result_query = match query_kind { - Type::Path(path) - if path - .path - .segments - .last() - .map_or(false, |s| s.ident == "OptionQuery") => - { - return Ok(Some(QueryKind::OptionQuery)) - } - Type::Path(TypePath { - path: Path { segments, .. }, - .. - }) if segments.last().map_or(false, |s| s.ident == "ResultQuery") => segments - .last() - .expect("segments is checked to have the last value; qed") - .clone(), - Type::Path(path) - if path - .path - .segments - .last() - .map_or(false, |s| s.ident == "ValueQuery") => - { - return Ok(Some(QueryKind::ValueQuery)) - } - _ => return Ok(None), - }; - - let error_type = match result_query.arguments { - PathArguments::AngleBracketed(AngleBracketedGenericArguments { - args, .. - }) => { - if args.len() != 1 { - let msg = format!( - "Invalid pallet::storage, unexpected number of generic arguments \ + /// Return the storage prefix for this storage item + pub fn prefix(&self) -> String { + self.rename_as + .as_ref() + .map(syn::LitStr::value) + .unwrap_or_else(|| self.ident.to_string()) + } + + /// Return either the span of the ident or the span of the literal in the + /// #[storage_prefix] attribute + pub fn prefix_span(&self) -> proc_macro2::Span { + self.rename_as + .as_ref() + .map(syn::LitStr::span) + .unwrap_or_else(|| self.ident.span()) + } + + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + dev_mode: bool, + ) -> syn::Result { + let item = if let syn::Item::Type(item) = item { + item + } else { + return Err(syn::Error::new(item.span(), "Invalid pallet::storage, expect item type.")) + }; + + let attrs: Vec = helper::take_item_pallet_attrs(&mut item.attrs)?; + let PalletStorageAttrInfo { getter, rename_as, mut unbounded, whitelisted, try_decode } = + PalletStorageAttrInfo::from_attrs(attrs)?; + + // set all storages to be unbounded if dev_mode is enabled + unbounded |= dev_mode; + let cfg_attrs = helper::get_item_cfg_attrs(&item.attrs); + + let instances = vec![helper::check_type_def_gen(&item.generics, item.ident.span())?]; + + let where_clause = item.generics.where_clause.clone(); + let docs = get_doc_literals(&item.attrs); + + let typ = if let syn::Type::Path(typ) = &*item.ty { + typ + } else { + let msg = "Invalid pallet::storage, expected type path"; + return Err(syn::Error::new(item.ty.span(), msg)) + }; + + if typ.path.segments.len() != 1 { + let msg = "Invalid pallet::storage, expected type path with one segment"; + return Err(syn::Error::new(item.ty.span(), msg)) + } + + let (named_generics, metadata, query_kind, use_default_hasher) = + process_generics(&typ.path.segments[0], dev_mode)?; + + let query_kind = query_kind + .map(|query_kind| { + use syn::{ + AngleBracketedGenericArguments, GenericArgument, Path, PathArguments, Type, + TypePath, + }; + + let result_query = match query_kind { + Type::Path(path) + if path + .path + .segments + .last() + .map_or(false, |s| s.ident == "OptionQuery") => + return Ok(Some(QueryKind::OptionQuery)), + Type::Path(TypePath { path: Path { segments, .. }, .. }) + if segments.last().map_or(false, |s| s.ident == "ResultQuery") => + segments + .last() + .expect("segments is checked to have the last value; qed") + .clone(), + Type::Path(path) + if path.path.segments.last().map_or(false, |s| s.ident == "ValueQuery") => + return Ok(Some(QueryKind::ValueQuery)), + _ => return Ok(None), + }; + + let error_type = match result_query.arguments { + PathArguments::AngleBracketed(AngleBracketedGenericArguments { + args, .. + }) => { + if args.len() != 1 { + let msg = format!( + "Invalid pallet::storage, unexpected number of generic arguments \ for ResultQuery, expected 1 type argument, found {}", - args.len(), - ); - return Err(syn::Error::new(args.span(), msg)); - } - - args[0].clone() - } - args => { - let msg = format!( - "Invalid pallet::storage, unexpected generic args for ResultQuery, \ + args.len(), + ); + return Err(syn::Error::new(args.span(), msg)) + } + + args[0].clone() + }, + args => { + let msg = format!( + "Invalid pallet::storage, unexpected generic args for ResultQuery, \ expected angle-bracketed arguments, found `{}`", - args.to_token_stream() - ); - return Err(syn::Error::new(args.span(), msg)); - } - }; - - match error_type { - GenericArgument::Type(Type::Path(TypePath { - path: - Path { - segments: err_variant, - leading_colon, - }, - .. - })) => { - if err_variant.len() < 2 { - let msg = format!( - "Invalid pallet::storage, unexpected number of path segments for \ + args.to_token_stream().to_string() + ); + return Err(syn::Error::new(args.span(), msg)) + }, + }; + + match error_type { + GenericArgument::Type(Type::Path(TypePath { + path: Path { segments: err_variant, leading_colon }, + .. + })) => { + if err_variant.len() < 2 { + let msg = format!( + "Invalid pallet::storage, unexpected number of path segments for \ the generics in ResultQuery, expected a path with at least 2 \ segments, found {}", - err_variant.len(), - ); - return Err(syn::Error::new(err_variant.span(), msg)); - } - let mut error = err_variant.clone(); - let err_variant = error - .pop() - .expect("Checked to have at least 2; qed") - .into_value() - .ident; - - // Necessary here to eliminate the last double colon - let last = error - .pop() - .expect("Checked to have at least 2; qed") - .into_value(); - error.push_value(last); - - Ok(Some(QueryKind::ResultQuery( - syn::Path { - leading_colon, - segments: error, - }, - err_variant, - ))) - } - gen_arg => { - let msg = format!( + err_variant.len(), + ); + return Err(syn::Error::new(err_variant.span(), msg)) + } + let mut error = err_variant.clone(); + let err_variant = error + .pop() + .expect("Checked to have at least 2; qed") + .into_value() + .ident; + + // Necessary here to eliminate the last double colon + let last = + error.pop().expect("Checked to have at least 2; qed").into_value(); + error.push_value(last); + + Ok(Some(QueryKind::ResultQuery( + syn::Path { leading_colon, segments: error }, + err_variant, + ))) + }, + gen_arg => { + let msg = format!( "Invalid pallet::storage, unexpected generic argument kind, expected a \ type path to a `PalletError` enum variant, found `{}`", - gen_arg.to_token_stream(), + gen_arg.to_token_stream().to_string(), ); - Err(syn::Error::new(gen_arg.span(), msg)) - } - } - }) - .transpose()? - .unwrap_or(Some(QueryKind::OptionQuery)); - - if let (None, Some(getter)) = (query_kind.as_ref(), getter.as_ref()) { - let msg = "Invalid pallet::storage, cannot generate getter because QueryKind is not \ + Err(syn::Error::new(gen_arg.span(), msg)) + }, + } + }) + .transpose()? + .unwrap_or(Some(QueryKind::OptionQuery)); + + if let (None, Some(getter)) = (query_kind.as_ref(), getter.as_ref()) { + let msg = "Invalid pallet::storage, cannot generate getter because QueryKind is not \ identifiable. QueryKind must be `OptionQuery`, `ResultQuery`, `ValueQuery`, or default \ one to be identifiable."; - return Err(syn::Error::new(getter.span(), msg)); - } - - Ok(StorageDef { - attr_span, - index, - vis: item.vis.clone(), - ident: item.ident.clone(), - instances, - metadata, - docs, - getter, - rename_as, - query_kind, - where_clause, - cfg_attrs, - named_generics, - unbounded, - whitelisted, - try_decode, - use_default_hasher, - }) - } + return Err(syn::Error::new(getter.span(), msg)) + } + + Ok(StorageDef { + attr_span, + index, + vis: item.vis.clone(), + ident: item.ident.clone(), + instances, + metadata, + docs, + getter, + rename_as, + query_kind, + where_clause, + cfg_attrs, + named_generics, + unbounded, + whitelisted, + try_decode, + use_default_hasher, + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/tasks.rs b/support/procedural-fork/src/pallet/parse/tasks.rs index 66ee1a7ef..6405bb415 100644 --- a/support/procedural-fork/src/pallet/parse/tasks.rs +++ b/support/procedural-fork/src/pallet/parse/tasks.rs @@ -30,103 +30,96 @@ use frame_support_procedural_tools::generate_access_from_frame_or_crate; use proc_macro2::TokenStream as TokenStream2; use quote::{quote, ToTokens}; use syn::{ - parse::ParseStream, - parse2, - spanned::Spanned, - token::{Bracket, Paren, PathSep, Pound}, - Attribute, Error, Expr, Ident, ImplItem, ImplItemFn, ItemEnum, ItemImpl, LitInt, Path, - PathArguments, Result, TypePath, + parse::ParseStream, + parse2, + spanned::Spanned, + token::{Bracket, Paren, PathSep, Pound}, + Attribute, Error, Expr, Ident, ImplItem, ImplItemFn, ItemEnum, ItemImpl, LitInt, Path, + PathArguments, Result, TypePath, }; pub mod keywords { - use syn::custom_keyword; + use syn::custom_keyword; - custom_keyword!(tasks_experimental); - custom_keyword!(task_enum); - custom_keyword!(task_list); - custom_keyword!(task_condition); - custom_keyword!(task_index); - custom_keyword!(task_weight); - custom_keyword!(pallet); + custom_keyword!(tasks_experimental); + custom_keyword!(task_enum); + custom_keyword!(task_list); + custom_keyword!(task_condition); + custom_keyword!(task_index); + custom_keyword!(task_weight); + custom_keyword!(pallet); } /// Represents the `#[pallet::tasks_experimental]` attribute and its attached item. Also includes /// metadata about the linked [`TaskEnumDef`] if applicable. #[derive(Clone, Debug)] pub struct TasksDef { - pub tasks_attr: Option, - pub tasks: Vec, - pub item_impl: ItemImpl, - /// Path to `frame_support` - pub scrate: Path, - pub enum_ident: Ident, - pub enum_arguments: PathArguments, + pub tasks_attr: Option, + pub tasks: Vec, + pub item_impl: ItemImpl, + /// Path to `frame_support` + pub scrate: Path, + pub enum_ident: Ident, + pub enum_arguments: PathArguments, } impl syn::parse::Parse for TasksDef { - fn parse(input: ParseStream) -> Result { - let item_impl: ItemImpl = input.parse()?; - let (tasks_attrs, normal_attrs) = partition_tasks_attrs(&item_impl); - let tasks_attr = match tasks_attrs.first() { - Some(attr) => Some(parse2::(attr.to_token_stream())?), - None => None, - }; - if let Some(extra_tasks_attr) = tasks_attrs.get(1) { - return Err(Error::new( - extra_tasks_attr.span(), - "unexpected extra `#[pallet::tasks_experimental]` attribute", - )); - } - let tasks: Vec = if tasks_attr.is_some() { - item_impl - .items - .clone() - .into_iter() - .filter(|impl_item| matches!(impl_item, ImplItem::Fn(_))) - .map(|item| parse2::(item.to_token_stream())) - .collect::>()? - } else { - Vec::new() - }; - let mut task_indices = HashSet::::new(); - for task in tasks.iter() { - let task_index = &task.index_attr.meta.index; - if !task_indices.insert(task_index.clone()) { - return Err(Error::new( - task_index.span(), - format!("duplicate task index `{}`", task_index), - )); - } - } - let mut item_impl = item_impl; - item_impl.attrs = normal_attrs; - - // we require the path on the impl to be a TypePath - let enum_path = parse2::(item_impl.self_ty.to_token_stream())?; - let segments = enum_path.path.segments.iter().collect::>(); - let (Some(last_seg), None) = (segments.first(), segments.get(1)) else { - return Err(Error::new( - enum_path.span(), - "if specified manually, the task enum must be defined locally in this \ + fn parse(input: ParseStream) -> Result { + let item_impl: ItemImpl = input.parse()?; + let (tasks_attrs, normal_attrs) = partition_tasks_attrs(&item_impl); + let tasks_attr = match tasks_attrs.first() { + Some(attr) => Some(parse2::(attr.to_token_stream())?), + None => None, + }; + if let Some(extra_tasks_attr) = tasks_attrs.get(1) { + return Err(Error::new( + extra_tasks_attr.span(), + "unexpected extra `#[pallet::tasks_experimental]` attribute", + )) + } + let tasks: Vec = if tasks_attr.is_some() { + item_impl + .items + .clone() + .into_iter() + .filter(|impl_item| matches!(impl_item, ImplItem::Fn(_))) + .map(|item| parse2::(item.to_token_stream())) + .collect::>()? + } else { + Vec::new() + }; + let mut task_indices = HashSet::::new(); + for task in tasks.iter() { + let task_index = &task.index_attr.meta.index; + if !task_indices.insert(task_index.clone()) { + return Err(Error::new( + task_index.span(), + format!("duplicate task index `{}`", task_index), + )) + } + } + let mut item_impl = item_impl; + item_impl.attrs = normal_attrs; + + // we require the path on the impl to be a TypePath + let enum_path = parse2::(item_impl.self_ty.to_token_stream())?; + let segments = enum_path.path.segments.iter().collect::>(); + let (Some(last_seg), None) = (segments.get(0), segments.get(1)) else { + return Err(Error::new( + enum_path.span(), + "if specified manually, the task enum must be defined locally in this \ pallet and cannot be a re-export", - )); - }; - let enum_ident = last_seg.ident.clone(); - let enum_arguments = last_seg.arguments.clone(); - - // We do this here because it would be improper to do something fallible like this at - // the expansion phase. Fallible stuff should happen during parsing. - let scrate = generate_access_from_frame_or_crate("frame-support")?; - - Ok(TasksDef { - tasks_attr, - item_impl, - tasks, - scrate, - enum_ident, - enum_arguments, - }) - } + )) + }; + let enum_ident = last_seg.ident.clone(); + let enum_arguments = last_seg.arguments.clone(); + + // We do this here because it would be improper to do something fallible like this at + // the expansion phase. Fallible stuff should happen during parsing. + let scrate = generate_access_from_frame_or_crate("frame-support")?; + + Ok(TasksDef { tasks_attr, item_impl, tasks, scrate, enum_ident, enum_arguments }) + } } /// Parsing for a `#[pallet::tasks_experimental]` attr. @@ -155,843 +148,821 @@ pub type PalletTaskEnumAttr = PalletTaskAttr; /// attached `#[pallet::task_enum]` attribute. #[derive(Clone, Debug)] pub struct TaskEnumDef { - pub attr: Option, - pub item_enum: ItemEnum, - pub scrate: Path, - pub type_use_generics: TokenStream2, + pub attr: Option, + pub item_enum: ItemEnum, + pub scrate: Path, + pub type_use_generics: TokenStream2, } impl syn::parse::Parse for TaskEnumDef { - fn parse(input: ParseStream) -> Result { - let mut item_enum = input.parse::()?; - let attr = extract_pallet_attr(&mut item_enum)?; - let attr = match attr { - Some(attr) => Some(parse2(attr)?), - None => None, - }; + fn parse(input: ParseStream) -> Result { + let mut item_enum = input.parse::()?; + let attr = extract_pallet_attr(&mut item_enum)?; + let attr = match attr { + Some(attr) => Some(parse2(attr)?), + None => None, + }; - // We do this here because it would be improper to do something fallible like this at - // the expansion phase. Fallible stuff should happen during parsing. - let scrate = generate_access_from_frame_or_crate("frame-support")?; + // We do this here because it would be improper to do something fallible like this at + // the expansion phase. Fallible stuff should happen during parsing. + let scrate = generate_access_from_frame_or_crate("frame-support")?; - let type_use_generics = quote!(T); + let type_use_generics = quote!(T); - Ok(TaskEnumDef { - attr, - item_enum, - scrate, - type_use_generics, - }) - } + Ok(TaskEnumDef { attr, item_enum, scrate, type_use_generics }) + } } /// Represents an individual tasks within a [`TasksDef`]. #[derive(Debug, Clone)] pub struct TaskDef { - pub index_attr: TaskIndexAttr, - pub condition_attr: TaskConditionAttr, - pub list_attr: TaskListAttr, - pub weight_attr: TaskWeightAttr, - pub normal_attrs: Vec, - pub item: ImplItemFn, - pub arg_names: Vec, + pub index_attr: TaskIndexAttr, + pub condition_attr: TaskConditionAttr, + pub list_attr: TaskListAttr, + pub weight_attr: TaskWeightAttr, + pub normal_attrs: Vec, + pub item: ImplItemFn, + pub arg_names: Vec, } impl syn::parse::Parse for TaskDef { - fn parse(input: ParseStream) -> Result { - let item = input.parse::()?; - // we only want to activate TaskAttrType parsing errors for tasks-related attributes, - // so we filter them here - let (task_attrs, normal_attrs) = partition_task_attrs(&item); - - let task_attrs: Vec = task_attrs - .into_iter() - .map(|attr| parse2(attr.to_token_stream())) - .collect::>()?; - - let Some(index_attr) = task_attrs - .iter() - .find(|attr| matches!(attr.meta, TaskAttrMeta::TaskIndex(_))) - .cloned() - else { - return Err(Error::new( - item.sig.ident.span(), - "missing `#[pallet::task_index(..)]` attribute", - )); - }; - - let Some(condition_attr) = task_attrs - .iter() - .find(|attr| matches!(attr.meta, TaskAttrMeta::TaskCondition(_))) - .cloned() - else { - return Err(Error::new( - item.sig.ident.span(), - "missing `#[pallet::task_condition(..)]` attribute", - )); - }; - - let Some(list_attr) = task_attrs - .iter() - .find(|attr| matches!(attr.meta, TaskAttrMeta::TaskList(_))) - .cloned() - else { - return Err(Error::new( - item.sig.ident.span(), - "missing `#[pallet::task_list(..)]` attribute", - )); - }; - - let Some(weight_attr) = task_attrs - .iter() - .find(|attr| matches!(attr.meta, TaskAttrMeta::TaskWeight(_))) - .cloned() - else { - return Err(Error::new( - item.sig.ident.span(), - "missing `#[pallet::task_weight(..)]` attribute", - )); - }; - - if let Some(duplicate) = task_attrs - .iter() - .filter(|attr| matches!(attr.meta, TaskAttrMeta::TaskCondition(_))) - .collect::>() - .get(1) - { - return Err(Error::new( - duplicate.span(), - "unexpected extra `#[pallet::task_condition(..)]` attribute", - )); - } - - if let Some(duplicate) = task_attrs - .iter() - .filter(|attr| matches!(attr.meta, TaskAttrMeta::TaskList(_))) - .collect::>() - .get(1) - { - return Err(Error::new( - duplicate.span(), - "unexpected extra `#[pallet::task_list(..)]` attribute", - )); - } - - if let Some(duplicate) = task_attrs - .iter() - .filter(|attr| matches!(attr.meta, TaskAttrMeta::TaskIndex(_))) - .collect::>() - .get(1) - { - return Err(Error::new( - duplicate.span(), - "unexpected extra `#[pallet::task_index(..)]` attribute", - )); - } - - let mut arg_names = vec![]; - for input in item.sig.inputs.iter() { - match input { - syn::FnArg::Typed(pat_type) => match &*pat_type.pat { - syn::Pat::Ident(ident) => arg_names.push(ident.ident.clone()), - _ => return Err(Error::new(input.span(), "unexpected pattern type")), - }, - _ => { - return Err(Error::new( - input.span(), - "unexpected function argument type", - )) - } - } - } - - let index_attr = index_attr.try_into().expect("we check the type above; QED"); - let condition_attr = condition_attr - .try_into() - .expect("we check the type above; QED"); - let list_attr = list_attr.try_into().expect("we check the type above; QED"); - let weight_attr = weight_attr - .try_into() - .expect("we check the type above; QED"); - - Ok(TaskDef { - index_attr, - condition_attr, - list_attr, - weight_attr, - normal_attrs, - item, - arg_names, - }) - } + fn parse(input: ParseStream) -> Result { + let item = input.parse::()?; + // we only want to activate TaskAttrType parsing errors for tasks-related attributes, + // so we filter them here + let (task_attrs, normal_attrs) = partition_task_attrs(&item); + + let task_attrs: Vec = task_attrs + .into_iter() + .map(|attr| parse2(attr.to_token_stream())) + .collect::>()?; + + let Some(index_attr) = task_attrs + .iter() + .find(|attr| matches!(attr.meta, TaskAttrMeta::TaskIndex(_))) + .cloned() + else { + return Err(Error::new( + item.sig.ident.span(), + "missing `#[pallet::task_index(..)]` attribute", + )) + }; + + let Some(condition_attr) = task_attrs + .iter() + .find(|attr| matches!(attr.meta, TaskAttrMeta::TaskCondition(_))) + .cloned() + else { + return Err(Error::new( + item.sig.ident.span(), + "missing `#[pallet::task_condition(..)]` attribute", + )) + }; + + let Some(list_attr) = task_attrs + .iter() + .find(|attr| matches!(attr.meta, TaskAttrMeta::TaskList(_))) + .cloned() + else { + return Err(Error::new( + item.sig.ident.span(), + "missing `#[pallet::task_list(..)]` attribute", + )) + }; + + let Some(weight_attr) = task_attrs + .iter() + .find(|attr| matches!(attr.meta, TaskAttrMeta::TaskWeight(_))) + .cloned() + else { + return Err(Error::new( + item.sig.ident.span(), + "missing `#[pallet::task_weight(..)]` attribute", + )) + }; + + if let Some(duplicate) = task_attrs + .iter() + .filter(|attr| matches!(attr.meta, TaskAttrMeta::TaskCondition(_))) + .collect::>() + .get(1) + { + return Err(Error::new( + duplicate.span(), + "unexpected extra `#[pallet::task_condition(..)]` attribute", + )) + } + + if let Some(duplicate) = task_attrs + .iter() + .filter(|attr| matches!(attr.meta, TaskAttrMeta::TaskList(_))) + .collect::>() + .get(1) + { + return Err(Error::new( + duplicate.span(), + "unexpected extra `#[pallet::task_list(..)]` attribute", + )) + } + + if let Some(duplicate) = task_attrs + .iter() + .filter(|attr| matches!(attr.meta, TaskAttrMeta::TaskIndex(_))) + .collect::>() + .get(1) + { + return Err(Error::new( + duplicate.span(), + "unexpected extra `#[pallet::task_index(..)]` attribute", + )) + } + + let mut arg_names = vec![]; + for input in item.sig.inputs.iter() { + match input { + syn::FnArg::Typed(pat_type) => match &*pat_type.pat { + syn::Pat::Ident(ident) => arg_names.push(ident.ident.clone()), + _ => return Err(Error::new(input.span(), "unexpected pattern type")), + }, + _ => return Err(Error::new(input.span(), "unexpected function argument type")), + } + } + + let index_attr = index_attr.try_into().expect("we check the type above; QED"); + let condition_attr = condition_attr.try_into().expect("we check the type above; QED"); + let list_attr = list_attr.try_into().expect("we check the type above; QED"); + let weight_attr = weight_attr.try_into().expect("we check the type above; QED"); + + Ok(TaskDef { + index_attr, + condition_attr, + list_attr, + weight_attr, + normal_attrs, + item, + arg_names, + }) + } } /// The contents of a [`TasksDef`]-related attribute. #[derive(Parse, Debug, Clone)] pub enum TaskAttrMeta { - #[peek(keywords::task_list, name = "#[pallet::task_list(..)]")] - TaskList(TaskListAttrMeta), - #[peek(keywords::task_index, name = "#[pallet::task_index(..)")] - TaskIndex(TaskIndexAttrMeta), - #[peek(keywords::task_condition, name = "#[pallet::task_condition(..)")] - TaskCondition(TaskConditionAttrMeta), - #[peek(keywords::task_weight, name = "#[pallet::task_weight(..)")] - TaskWeight(TaskWeightAttrMeta), + #[peek(keywords::task_list, name = "#[pallet::task_list(..)]")] + TaskList(TaskListAttrMeta), + #[peek(keywords::task_index, name = "#[pallet::task_index(..)")] + TaskIndex(TaskIndexAttrMeta), + #[peek(keywords::task_condition, name = "#[pallet::task_condition(..)")] + TaskCondition(TaskConditionAttrMeta), + #[peek(keywords::task_weight, name = "#[pallet::task_weight(..)")] + TaskWeight(TaskWeightAttrMeta), } /// The contents of a `#[pallet::task_list]` attribute. #[derive(Parse, Debug, Clone)] pub struct TaskListAttrMeta { - pub task_list: keywords::task_list, - #[paren] - _paren: Paren, - #[inside(_paren)] - pub expr: Expr, + pub task_list: keywords::task_list, + #[paren] + _paren: Paren, + #[inside(_paren)] + pub expr: Expr, } /// The contents of a `#[pallet::task_index]` attribute. #[derive(Parse, Debug, Clone)] pub struct TaskIndexAttrMeta { - pub task_index: keywords::task_index, - #[paren] - _paren: Paren, - #[inside(_paren)] - pub index: LitInt, + pub task_index: keywords::task_index, + #[paren] + _paren: Paren, + #[inside(_paren)] + pub index: LitInt, } /// The contents of a `#[pallet::task_condition]` attribute. #[derive(Parse, Debug, Clone)] pub struct TaskConditionAttrMeta { - pub task_condition: keywords::task_condition, - #[paren] - _paren: Paren, - #[inside(_paren)] - pub expr: Expr, + pub task_condition: keywords::task_condition, + #[paren] + _paren: Paren, + #[inside(_paren)] + pub expr: Expr, } /// The contents of a `#[pallet::task_weight]` attribute. #[derive(Parse, Debug, Clone)] pub struct TaskWeightAttrMeta { - pub task_weight: keywords::task_weight, - #[paren] - _paren: Paren, - #[inside(_paren)] - pub expr: Expr, + pub task_weight: keywords::task_weight, + #[paren] + _paren: Paren, + #[inside(_paren)] + pub expr: Expr, } /// The contents of a `#[pallet::task]` attribute. #[derive(Parse, Debug, Clone)] pub struct PalletTaskAttr { - pub pound: Pound, - #[bracket] - _bracket: Bracket, - #[inside(_bracket)] - pub pallet: keywords::pallet, - #[inside(_bracket)] - pub colons: PathSep, - #[inside(_bracket)] - pub meta: T, + pub pound: Pound, + #[bracket] + _bracket: Bracket, + #[inside(_bracket)] + pub pallet: keywords::pallet, + #[inside(_bracket)] + pub colons: PathSep, + #[inside(_bracket)] + pub meta: T, } impl ToTokens for TaskListAttrMeta { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let task_list = self.task_list; - let expr = &self.expr; - tokens.extend(quote!(#task_list(#expr))); - } + fn to_tokens(&self, tokens: &mut TokenStream2) { + let task_list = self.task_list; + let expr = &self.expr; + tokens.extend(quote!(#task_list(#expr))); + } } impl ToTokens for TaskConditionAttrMeta { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let task_condition = self.task_condition; - let expr = &self.expr; - tokens.extend(quote!(#task_condition(#expr))); - } + fn to_tokens(&self, tokens: &mut TokenStream2) { + let task_condition = self.task_condition; + let expr = &self.expr; + tokens.extend(quote!(#task_condition(#expr))); + } } impl ToTokens for TaskWeightAttrMeta { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let task_weight = self.task_weight; - let expr = &self.expr; - tokens.extend(quote!(#task_weight(#expr))); - } + fn to_tokens(&self, tokens: &mut TokenStream2) { + let task_weight = self.task_weight; + let expr = &self.expr; + tokens.extend(quote!(#task_weight(#expr))); + } } impl ToTokens for TaskIndexAttrMeta { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let task_index = self.task_index; - let index = &self.index; - tokens.extend(quote!(#task_index(#index))) - } + fn to_tokens(&self, tokens: &mut TokenStream2) { + let task_index = self.task_index; + let index = &self.index; + tokens.extend(quote!(#task_index(#index))) + } } impl ToTokens for TaskAttrMeta { - fn to_tokens(&self, tokens: &mut TokenStream2) { - match self { - TaskAttrMeta::TaskList(list) => tokens.extend(list.to_token_stream()), - TaskAttrMeta::TaskIndex(index) => tokens.extend(index.to_token_stream()), - TaskAttrMeta::TaskCondition(condition) => tokens.extend(condition.to_token_stream()), - TaskAttrMeta::TaskWeight(weight) => tokens.extend(weight.to_token_stream()), - } - } + fn to_tokens(&self, tokens: &mut TokenStream2) { + match self { + TaskAttrMeta::TaskList(list) => tokens.extend(list.to_token_stream()), + TaskAttrMeta::TaskIndex(index) => tokens.extend(index.to_token_stream()), + TaskAttrMeta::TaskCondition(condition) => tokens.extend(condition.to_token_stream()), + TaskAttrMeta::TaskWeight(weight) => tokens.extend(weight.to_token_stream()), + } + } } impl ToTokens for PalletTaskAttr { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let pound = self.pound; - let pallet = self.pallet; - let colons = self.colons; - let meta = &self.meta; - tokens.extend(quote!(#pound[#pallet #colons #meta])); - } + fn to_tokens(&self, tokens: &mut TokenStream2) { + let pound = self.pound; + let pallet = self.pallet; + let colons = self.colons; + let meta = &self.meta; + tokens.extend(quote!(#pound[#pallet #colons #meta])); + } } impl TryFrom> for TaskIndexAttr { - type Error = syn::Error; - - fn try_from(value: PalletTaskAttr) -> Result { - let pound = value.pound; - let pallet = value.pallet; - let colons = value.colons; - match value.meta { - TaskAttrMeta::TaskIndex(meta) => parse2(quote!(#pound[#pallet #colons #meta])), - _ => Err(Error::new( - value.span(), - format!( - "`{:?}` cannot be converted to a `TaskIndexAttr`", - value.meta - ), - )), - } - } + type Error = syn::Error; + + fn try_from(value: PalletTaskAttr) -> Result { + let pound = value.pound; + let pallet = value.pallet; + let colons = value.colons; + match value.meta { + TaskAttrMeta::TaskIndex(meta) => parse2(quote!(#pound[#pallet #colons #meta])), + _ => + return Err(Error::new( + value.span(), + format!("`{:?}` cannot be converted to a `TaskIndexAttr`", value.meta), + )), + } + } } impl TryFrom> for TaskConditionAttr { - type Error = syn::Error; - - fn try_from(value: PalletTaskAttr) -> Result { - let pound = value.pound; - let pallet = value.pallet; - let colons = value.colons; - match value.meta { - TaskAttrMeta::TaskCondition(meta) => parse2(quote!(#pound[#pallet #colons #meta])), - _ => Err(Error::new( - value.span(), - format!( - "`{:?}` cannot be converted to a `TaskConditionAttr`", - value.meta - ), - )), - } - } + type Error = syn::Error; + + fn try_from(value: PalletTaskAttr) -> Result { + let pound = value.pound; + let pallet = value.pallet; + let colons = value.colons; + match value.meta { + TaskAttrMeta::TaskCondition(meta) => parse2(quote!(#pound[#pallet #colons #meta])), + _ => + return Err(Error::new( + value.span(), + format!("`{:?}` cannot be converted to a `TaskConditionAttr`", value.meta), + )), + } + } } impl TryFrom> for TaskWeightAttr { - type Error = syn::Error; - - fn try_from(value: PalletTaskAttr) -> Result { - let pound = value.pound; - let pallet = value.pallet; - let colons = value.colons; - match value.meta { - TaskAttrMeta::TaskWeight(meta) => parse2(quote!(#pound[#pallet #colons #meta])), - _ => Err(Error::new( - value.span(), - format!( - "`{:?}` cannot be converted to a `TaskWeightAttr`", - value.meta - ), - )), - } - } + type Error = syn::Error; + + fn try_from(value: PalletTaskAttr) -> Result { + let pound = value.pound; + let pallet = value.pallet; + let colons = value.colons; + match value.meta { + TaskAttrMeta::TaskWeight(meta) => parse2(quote!(#pound[#pallet #colons #meta])), + _ => + return Err(Error::new( + value.span(), + format!("`{:?}` cannot be converted to a `TaskWeightAttr`", value.meta), + )), + } + } } impl TryFrom> for TaskListAttr { - type Error = syn::Error; - - fn try_from(value: PalletTaskAttr) -> Result { - let pound = value.pound; - let pallet = value.pallet; - let colons = value.colons; - match value.meta { - TaskAttrMeta::TaskList(meta) => parse2(quote!(#pound[#pallet #colons #meta])), - _ => Err(Error::new( - value.span(), - format!("`{:?}` cannot be converted to a `TaskListAttr`", value.meta), - )), - } - } + type Error = syn::Error; + + fn try_from(value: PalletTaskAttr) -> Result { + let pound = value.pound; + let pallet = value.pallet; + let colons = value.colons; + match value.meta { + TaskAttrMeta::TaskList(meta) => parse2(quote!(#pound[#pallet #colons #meta])), + _ => + return Err(Error::new( + value.span(), + format!("`{:?}` cannot be converted to a `TaskListAttr`", value.meta), + )), + } + } } fn extract_pallet_attr(item_enum: &mut ItemEnum) -> Result> { - let mut duplicate = None; - let mut attr = None; - item_enum.attrs = item_enum - .attrs - .iter() - .filter(|found_attr| { - let segs = found_attr - .path() - .segments - .iter() - .map(|seg| seg.ident.clone()) - .collect::>(); - let (Some(seg1), Some(_), None) = (segs.first(), segs.get(1), segs.get(2)) else { - return true; - }; - if seg1 != "pallet" { - return true; - } - if attr.is_some() { - duplicate = Some(found_attr.span()); - } - attr = Some(found_attr.to_token_stream()); - false - }) - .cloned() - .collect(); - if let Some(span) = duplicate { - return Err(Error::new( - span, - "only one `#[pallet::_]` attribute is supported on this item", - )); - } - Ok(attr) + let mut duplicate = None; + let mut attr = None; + item_enum.attrs = item_enum + .attrs + .iter() + .filter(|found_attr| { + let segs = found_attr + .path() + .segments + .iter() + .map(|seg| seg.ident.clone()) + .collect::>(); + let (Some(seg1), Some(_), None) = (segs.get(0), segs.get(1), segs.get(2)) else { + return true + }; + if seg1 != "pallet" { + return true + } + if attr.is_some() { + duplicate = Some(found_attr.span()); + } + attr = Some(found_attr.to_token_stream()); + false + }) + .cloned() + .collect(); + if let Some(span) = duplicate { + return Err(Error::new(span, "only one `#[pallet::_]` attribute is supported on this item")) + } + Ok(attr) } fn partition_tasks_attrs(item_impl: &ItemImpl) -> (Vec, Vec) { - item_impl.attrs.clone().into_iter().partition(|attr| { - let mut path_segs = attr.path().segments.iter(); - let (Some(prefix), Some(suffix), None) = - (path_segs.next(), path_segs.next(), path_segs.next()) - else { - return false; - }; - prefix.ident == "pallet" && suffix.ident == "tasks_experimental" - }) + item_impl.attrs.clone().into_iter().partition(|attr| { + let mut path_segs = attr.path().segments.iter(); + let (Some(prefix), Some(suffix), None) = + (path_segs.next(), path_segs.next(), path_segs.next()) + else { + return false + }; + prefix.ident == "pallet" && suffix.ident == "tasks_experimental" + }) } fn partition_task_attrs(item: &ImplItemFn) -> (Vec, Vec) { - item.attrs.clone().into_iter().partition(|attr| { - let mut path_segs = attr.path().segments.iter(); - let (Some(prefix), Some(suffix)) = (path_segs.next(), path_segs.next()) else { - return false; - }; - // N.B: the `PartialEq` impl between `Ident` and `&str` is more efficient than - // parsing and makes no stack or heap allocations - prefix.ident == "pallet" - && (suffix.ident == "tasks_experimental" - || suffix.ident == "task_list" - || suffix.ident == "task_condition" - || suffix.ident == "task_weight" - || suffix.ident == "task_index") - }) + item.attrs.clone().into_iter().partition(|attr| { + let mut path_segs = attr.path().segments.iter(); + let (Some(prefix), Some(suffix)) = (path_segs.next(), path_segs.next()) else { + return false + }; + // N.B: the `PartialEq` impl between `Ident` and `&str` is more efficient than + // parsing and makes no stack or heap allocations + prefix.ident == "pallet" && + (suffix.ident == "tasks_experimental" || + suffix.ident == "task_list" || + suffix.ident == "task_condition" || + suffix.ident == "task_weight" || + suffix.ident == "task_index") + }) } #[test] fn test_parse_task_list_() { - parse2::(quote!(#[pallet::task_list(Something::iter())])).unwrap(); - parse2::(quote!(#[pallet::task_list(Numbers::::iter_keys())])).unwrap(); - parse2::(quote!(#[pallet::task_list(iter())])).unwrap(); - assert_parse_error_matches!( - parse2::(quote!(#[pallet::task_list()])), - "expected an expression" - ); - assert_parse_error_matches!( - parse2::(quote!(#[pallet::task_list])), - "expected parentheses" - ); + parse2::(quote!(#[pallet::task_list(Something::iter())])).unwrap(); + parse2::(quote!(#[pallet::task_list(Numbers::::iter_keys())])).unwrap(); + parse2::(quote!(#[pallet::task_list(iter())])).unwrap(); + assert_parse_error_matches!( + parse2::(quote!(#[pallet::task_list()])), + "expected an expression" + ); + assert_parse_error_matches!( + parse2::(quote!(#[pallet::task_list])), + "expected parentheses" + ); } #[test] fn test_parse_task_index() { - parse2::(quote!(#[pallet::task_index(3)])).unwrap(); - parse2::(quote!(#[pallet::task_index(0)])).unwrap(); - parse2::(quote!(#[pallet::task_index(17)])).unwrap(); - assert_parse_error_matches!( - parse2::(quote!(#[pallet::task_index])), - "expected parentheses" - ); - assert_parse_error_matches!( - parse2::(quote!(#[pallet::task_index("hey")])), - "expected integer literal" - ); - assert_parse_error_matches!( - parse2::(quote!(#[pallet::task_index(0.3)])), - "expected integer literal" - ); + parse2::(quote!(#[pallet::task_index(3)])).unwrap(); + parse2::(quote!(#[pallet::task_index(0)])).unwrap(); + parse2::(quote!(#[pallet::task_index(17)])).unwrap(); + assert_parse_error_matches!( + parse2::(quote!(#[pallet::task_index])), + "expected parentheses" + ); + assert_parse_error_matches!( + parse2::(quote!(#[pallet::task_index("hey")])), + "expected integer literal" + ); + assert_parse_error_matches!( + parse2::(quote!(#[pallet::task_index(0.3)])), + "expected integer literal" + ); } #[test] fn test_parse_task_condition() { - parse2::(quote!(#[pallet::task_condition(|x| x.is_some())])).unwrap(); - parse2::(quote!(#[pallet::task_condition(|_x| some_expr())])).unwrap(); - parse2::(quote!(#[pallet::task_condition(|| some_expr())])).unwrap(); - parse2::(quote!(#[pallet::task_condition(some_expr())])).unwrap(); + parse2::(quote!(#[pallet::task_condition(|x| x.is_some())])).unwrap(); + parse2::(quote!(#[pallet::task_condition(|_x| some_expr())])).unwrap(); + parse2::(quote!(#[pallet::task_condition(|| some_expr())])).unwrap(); + parse2::(quote!(#[pallet::task_condition(some_expr())])).unwrap(); } #[test] fn test_parse_tasks_attr() { - parse2::(quote!(#[pallet::tasks_experimental])).unwrap(); - assert_parse_error_matches!( - parse2::(quote!(#[pallet::taskss])), - "expected `tasks_experimental`" - ); - assert_parse_error_matches!( - parse2::(quote!(#[pallet::tasks_])), - "expected `tasks_experimental`" - ); - assert_parse_error_matches!( - parse2::(quote!(#[pal::tasks])), - "expected `pallet`" - ); - assert_parse_error_matches!( - parse2::(quote!(#[pallet::tasks_experimental()])), - "unexpected token" - ); + parse2::(quote!(#[pallet::tasks_experimental])).unwrap(); + assert_parse_error_matches!( + parse2::(quote!(#[pallet::taskss])), + "expected `tasks_experimental`" + ); + assert_parse_error_matches!( + parse2::(quote!(#[pallet::tasks_])), + "expected `tasks_experimental`" + ); + assert_parse_error_matches!( + parse2::(quote!(#[pal::tasks])), + "expected `pallet`" + ); + assert_parse_error_matches!( + parse2::(quote!(#[pallet::tasks_experimental()])), + "unexpected token" + ); } #[test] fn test_parse_tasks_def_basic() { - simulate_manifest_dir("../../pallets/subtensor", || { - let parsed = parse2::(quote! { - #[pallet::tasks_experimental] - impl, I: 'static> Pallet { - /// Add a pair of numbers into the totals and remove them. - #[pallet::task_list(Numbers::::iter_keys())] - #[pallet::task_condition(|i| Numbers::::contains_key(i))] - #[pallet::task_index(0)] - #[pallet::task_weight(0)] - pub fn add_number_into_total(i: u32) -> DispatchResult { - let v = Numbers::::take(i).ok_or(Error::::NotFound)?; - Total::::mutate(|(total_keys, total_values)| { - *total_keys += i; - *total_values += v; - }); - Ok(()) - } - } - }) - .unwrap(); - assert_eq!(parsed.tasks.len(), 1); - }); + simulate_manifest_dir("../../examples/basic", || { + let parsed = parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + /// Add a pair of numbers into the totals and remove them. + #[pallet::task_list(Numbers::::iter_keys())] + #[pallet::task_condition(|i| Numbers::::contains_key(i))] + #[pallet::task_index(0)] + #[pallet::task_weight(0)] + pub fn add_number_into_total(i: u32) -> DispatchResult { + let v = Numbers::::take(i).ok_or(Error::::NotFound)?; + Total::::mutate(|(total_keys, total_values)| { + *total_keys += i; + *total_values += v; + }); + Ok(()) + } + } + }) + .unwrap(); + assert_eq!(parsed.tasks.len(), 1); + }); } #[test] fn test_parse_tasks_def_basic_increment_decrement() { - simulate_manifest_dir("../../pallets/subtensor", || { - let parsed = parse2::(quote! { - #[pallet::tasks_experimental] - impl, I: 'static> Pallet { - /// Get the value and check if it can be incremented - #[pallet::task_index(0)] - #[pallet::task_condition(|| { - let value = Value::::get().unwrap(); - value < 255 - })] - #[pallet::task_list(Vec::>::new())] - #[pallet::task_weight(0)] - fn increment() -> DispatchResult { - let value = Value::::get().unwrap_or_default(); - if value >= 255 { - Err(Error::::ValueOverflow.into()) - } else { - let new_val = value.checked_add(1).ok_or(Error::::ValueOverflow)?; - Value::::put(new_val); - Pallet::::deposit_event(Event::Incremented { new_val }); - Ok(()) - } - } - - // Get the value and check if it can be decremented - #[pallet::task_index(1)] - #[pallet::task_condition(|| { - let value = Value::::get().unwrap(); - value > 0 - })] - #[pallet::task_list(Vec::>::new())] - #[pallet::task_weight(0)] - fn decrement() -> DispatchResult { - let value = Value::::get().unwrap_or_default(); - if value == 0 { - Err(Error::::ValueUnderflow.into()) - } else { - let new_val = value.checked_sub(1).ok_or(Error::::ValueUnderflow)?; - Value::::put(new_val); - Pallet::::deposit_event(Event::Decremented { new_val }); - Ok(()) - } - } - } - }) - .unwrap(); - assert_eq!(parsed.tasks.len(), 2); - }); + simulate_manifest_dir("../../examples/basic", || { + let parsed = parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + /// Get the value and check if it can be incremented + #[pallet::task_index(0)] + #[pallet::task_condition(|| { + let value = Value::::get().unwrap(); + value < 255 + })] + #[pallet::task_list(Vec::>::new())] + #[pallet::task_weight(0)] + fn increment() -> DispatchResult { + let value = Value::::get().unwrap_or_default(); + if value >= 255 { + Err(Error::::ValueOverflow.into()) + } else { + let new_val = value.checked_add(1).ok_or(Error::::ValueOverflow)?; + Value::::put(new_val); + Pallet::::deposit_event(Event::Incremented { new_val }); + Ok(()) + } + } + + // Get the value and check if it can be decremented + #[pallet::task_index(1)] + #[pallet::task_condition(|| { + let value = Value::::get().unwrap(); + value > 0 + })] + #[pallet::task_list(Vec::>::new())] + #[pallet::task_weight(0)] + fn decrement() -> DispatchResult { + let value = Value::::get().unwrap_or_default(); + if value == 0 { + Err(Error::::ValueUnderflow.into()) + } else { + let new_val = value.checked_sub(1).ok_or(Error::::ValueUnderflow)?; + Value::::put(new_val); + Pallet::::deposit_event(Event::Decremented { new_val }); + Ok(()) + } + } + } + }) + .unwrap(); + assert_eq!(parsed.tasks.len(), 2); + }); } #[test] fn test_parse_tasks_def_duplicate_index() { - simulate_manifest_dir("../../pallets/subtensor", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::tasks_experimental] - impl, I: 'static> Pallet { - #[pallet::task_list(Something::iter())] - #[pallet::task_condition(|i| i % 2 == 0)] - #[pallet::task_index(0)] - #[pallet::task_weight(0)] - pub fn foo(i: u32) -> DispatchResult { - Ok(()) - } - - #[pallet::task_list(Numbers::::iter_keys())] - #[pallet::task_condition(|i| Numbers::::contains_key(i))] - #[pallet::task_index(0)] - #[pallet::task_weight(0)] - pub fn bar(i: u32) -> DispatchResult { - Ok(()) - } - } - }), - "duplicate task index `0`" - ); - }); + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + #[pallet::task_list(Something::iter())] + #[pallet::task_condition(|i| i % 2 == 0)] + #[pallet::task_index(0)] + #[pallet::task_weight(0)] + pub fn foo(i: u32) -> DispatchResult { + Ok(()) + } + + #[pallet::task_list(Numbers::::iter_keys())] + #[pallet::task_condition(|i| Numbers::::contains_key(i))] + #[pallet::task_index(0)] + #[pallet::task_weight(0)] + pub fn bar(i: u32) -> DispatchResult { + Ok(()) + } + } + }), + "duplicate task index `0`" + ); + }); } #[test] fn test_parse_tasks_def_missing_task_list() { - simulate_manifest_dir("../../pallets/subtensor", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::tasks_experimental] - impl, I: 'static> Pallet { - #[pallet::task_condition(|i| i % 2 == 0)] - #[pallet::task_index(0)] - pub fn foo(i: u32) -> DispatchResult { - Ok(()) - } - } - }), - r"missing `#\[pallet::task_list\(\.\.\)\]`" - ); - }); + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + #[pallet::task_condition(|i| i % 2 == 0)] + #[pallet::task_index(0)] + pub fn foo(i: u32) -> DispatchResult { + Ok(()) + } + } + }), + r"missing `#\[pallet::task_list\(\.\.\)\]`" + ); + }); } #[test] fn test_parse_tasks_def_missing_task_condition() { - simulate_manifest_dir("../../pallets/subtensor", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::tasks_experimental] - impl, I: 'static> Pallet { - #[pallet::task_list(Something::iter())] - #[pallet::task_index(0)] - pub fn foo(i: u32) -> DispatchResult { - Ok(()) - } - } - }), - r"missing `#\[pallet::task_condition\(\.\.\)\]`" - ); - }); + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + #[pallet::task_list(Something::iter())] + #[pallet::task_index(0)] + pub fn foo(i: u32) -> DispatchResult { + Ok(()) + } + } + }), + r"missing `#\[pallet::task_condition\(\.\.\)\]`" + ); + }); } #[test] fn test_parse_tasks_def_missing_task_index() { - simulate_manifest_dir("../../pallets/subtensor", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::tasks_experimental] - impl, I: 'static> Pallet { - #[pallet::task_condition(|i| i % 2 == 0)] - #[pallet::task_list(Something::iter())] - pub fn foo(i: u32) -> DispatchResult { - Ok(()) - } - } - }), - r"missing `#\[pallet::task_index\(\.\.\)\]`" - ); - }); + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + #[pallet::task_condition(|i| i % 2 == 0)] + #[pallet::task_list(Something::iter())] + pub fn foo(i: u32) -> DispatchResult { + Ok(()) + } + } + }), + r"missing `#\[pallet::task_index\(\.\.\)\]`" + ); + }); } #[test] fn test_parse_tasks_def_missing_task_weight() { - simulate_manifest_dir("../../pallets/subtensor", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::tasks_experimental] - impl, I: 'static> Pallet { - #[pallet::task_condition(|i| i % 2 == 0)] - #[pallet::task_list(Something::iter())] - #[pallet::task_index(0)] - pub fn foo(i: u32) -> DispatchResult { - Ok(()) - } - } - }), - r"missing `#\[pallet::task_weight\(\.\.\)\]`" - ); - }); + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + #[pallet::task_condition(|i| i % 2 == 0)] + #[pallet::task_list(Something::iter())] + #[pallet::task_index(0)] + pub fn foo(i: u32) -> DispatchResult { + Ok(()) + } + } + }), + r"missing `#\[pallet::task_weight\(\.\.\)\]`" + ); + }); } #[test] fn test_parse_tasks_def_unexpected_extra_task_list_attr() { - simulate_manifest_dir("../../pallets/subtensor", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::tasks_experimental] - impl, I: 'static> Pallet { - #[pallet::task_condition(|i| i % 2 == 0)] - #[pallet::task_index(0)] - #[pallet::task_weight(0)] - #[pallet::task_list(Something::iter())] - #[pallet::task_list(SomethingElse::iter())] - pub fn foo(i: u32) -> DispatchResult { - Ok(()) - } - } - }), - r"unexpected extra `#\[pallet::task_list\(\.\.\)\]`" - ); - }); + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + #[pallet::task_condition(|i| i % 2 == 0)] + #[pallet::task_index(0)] + #[pallet::task_weight(0)] + #[pallet::task_list(Something::iter())] + #[pallet::task_list(SomethingElse::iter())] + pub fn foo(i: u32) -> DispatchResult { + Ok(()) + } + } + }), + r"unexpected extra `#\[pallet::task_list\(\.\.\)\]`" + ); + }); } #[test] fn test_parse_tasks_def_unexpected_extra_task_condition_attr() { - simulate_manifest_dir("../../pallets/subtensor", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::tasks_experimental] - impl, I: 'static> Pallet { - #[pallet::task_condition(|i| i % 2 == 0)] - #[pallet::task_condition(|i| i % 4 == 0)] - #[pallet::task_index(0)] - #[pallet::task_list(Something::iter())] - #[pallet::task_weight(0)] - pub fn foo(i: u32) -> DispatchResult { - Ok(()) - } - } - }), - r"unexpected extra `#\[pallet::task_condition\(\.\.\)\]`" - ); - }); + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + #[pallet::task_condition(|i| i % 2 == 0)] + #[pallet::task_condition(|i| i % 4 == 0)] + #[pallet::task_index(0)] + #[pallet::task_list(Something::iter())] + #[pallet::task_weight(0)] + pub fn foo(i: u32) -> DispatchResult { + Ok(()) + } + } + }), + r"unexpected extra `#\[pallet::task_condition\(\.\.\)\]`" + ); + }); } #[test] fn test_parse_tasks_def_unexpected_extra_task_index_attr() { - simulate_manifest_dir("../../pallets/subtensor", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::tasks_experimental] - impl, I: 'static> Pallet { - #[pallet::task_condition(|i| i % 2 == 0)] - #[pallet::task_index(0)] - #[pallet::task_index(0)] - #[pallet::task_list(Something::iter())] - #[pallet::task_weight(0)] - pub fn foo(i: u32) -> DispatchResult { - Ok(()) - } - } - }), - r"unexpected extra `#\[pallet::task_index\(\.\.\)\]`" - ); - }); + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + #[pallet::task_condition(|i| i % 2 == 0)] + #[pallet::task_index(0)] + #[pallet::task_index(0)] + #[pallet::task_list(Something::iter())] + #[pallet::task_weight(0)] + pub fn foo(i: u32) -> DispatchResult { + Ok(()) + } + } + }), + r"unexpected extra `#\[pallet::task_index\(\.\.\)\]`" + ); + }); } #[test] fn test_parse_tasks_def_extra_tasks_attribute() { - simulate_manifest_dir("../../pallets/subtensor", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::tasks_experimental] - #[pallet::tasks_experimental] - impl, I: 'static> Pallet {} - }), - r"unexpected extra `#\[pallet::tasks_experimental\]` attribute" - ); - }); + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + #[pallet::tasks_experimental] + impl, I: 'static> Pallet {} + }), + r"unexpected extra `#\[pallet::tasks_experimental\]` attribute" + ); + }); } #[test] fn test_parse_task_enum_def_basic() { - simulate_manifest_dir("../../pallets/subtensor", || { - parse2::(quote! { - #[pallet::task_enum] - pub enum Task { - Increment, - Decrement, - } - }) - .unwrap(); - }); + simulate_manifest_dir("../../examples/basic", || { + parse2::(quote! { + #[pallet::task_enum] + pub enum Task { + Increment, + Decrement, + } + }) + .unwrap(); + }); } #[test] fn test_parse_task_enum_def_non_task_name() { - simulate_manifest_dir("../../pallets/subtensor", || { - parse2::(quote! { - #[pallet::task_enum] - pub enum Something { - Foo - } - }) - .unwrap(); - }); + simulate_manifest_dir("../../examples/basic", || { + parse2::(quote! { + #[pallet::task_enum] + pub enum Something { + Foo + } + }) + .unwrap(); + }); } #[test] fn test_parse_task_enum_def_missing_attr_allowed() { - simulate_manifest_dir("../../pallets/subtensor", || { - parse2::(quote! { - pub enum Task { - Increment, - Decrement, - } - }) - .unwrap(); - }); + simulate_manifest_dir("../../examples/basic", || { + parse2::(quote! { + pub enum Task { + Increment, + Decrement, + } + }) + .unwrap(); + }); } #[test] fn test_parse_task_enum_def_missing_attr_alternate_name_allowed() { - simulate_manifest_dir("../../pallets/subtensor", || { - parse2::(quote! { - pub enum Foo { - Red, - } - }) - .unwrap(); - }); + simulate_manifest_dir("../../examples/basic", || { + parse2::(quote! { + pub enum Foo { + Red, + } + }) + .unwrap(); + }); } #[test] fn test_parse_task_enum_def_wrong_attr() { - simulate_manifest_dir("../../pallets/subtensor", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::something] - pub enum Task { - Increment, - Decrement, - } - }), - "expected `task_enum`" - ); - }); + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::something] + pub enum Task { + Increment, + Decrement, + } + }), + "expected `task_enum`" + ); + }); } #[test] fn test_parse_task_enum_def_wrong_item() { - simulate_manifest_dir("../../pallets/subtensor", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::task_enum] - pub struct Something; - }), - "expected `enum`" - ); - }); + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::task_enum] + pub struct Something; + }), + "expected `enum`" + ); + }); } diff --git a/support/procedural-fork/src/pallet/parse/tests/mod.rs b/support/procedural-fork/src/pallet/parse/tests/mod.rs index 7206a20bf..a3661f307 100644 --- a/support/procedural-fork/src/pallet/parse/tests/mod.rs +++ b/support/procedural-fork/src/pallet/parse/tests/mod.rs @@ -20,7 +20,7 @@ use syn::parse_quote; #[doc(hidden)] pub mod __private { - pub use regex; + pub use regex; } /// Allows you to assert that the input expression resolves to an error whose string @@ -63,22 +63,22 @@ pub mod __private { /// enough that it will work with any error with a reasonable [`core::fmt::Display`] impl. #[macro_export] macro_rules! assert_parse_error_matches { - ($expr:expr, $reg:literal) => { - match $expr { - Ok(_) => panic!("Expected an `Error(..)`, but got Ok(..)"), - Err(e) => { - let error_message = e.to_string(); - let re = $crate::pallet::parse::tests::__private::regex::Regex::new($reg) - .expect("Invalid regex pattern"); - assert!( - re.is_match(&error_message), - "Error message \"{}\" does not match the pattern \"{}\"", - error_message, - $reg - ); - } - } - }; + ($expr:expr, $reg:literal) => { + match $expr { + Ok(_) => panic!("Expected an `Error(..)`, but got Ok(..)"), + Err(e) => { + let error_message = e.to_string(); + let re = $crate::pallet::parse::tests::__private::regex::Regex::new($reg) + .expect("Invalid regex pattern"); + assert!( + re.is_match(&error_message), + "Error message \"{}\" does not match the pattern \"{}\"", + error_message, + $reg + ); + }, + } + }; } /// Allows you to assert that an entire pallet parses successfully. A custom syntax is used for @@ -88,7 +88,7 @@ macro_rules! assert_parse_error_matches { /// /// ```ignore /// assert_pallet_parses! { -/// #[manifest_dir("../../pallets/subtensor")] +/// #[manifest_dir("../../examples/basic")] /// #[frame_support::pallet] /// pub mod pallet { /// #[pallet::config] @@ -142,7 +142,7 @@ macro_rules! assert_pallet_parses { /// /// ``` /// assert_pallet_parse_error! { -/// #[manifest_dir("../../pallets/subtensor")] +/// #[manifest_dir("../../examples/basic")] /// #[error_regex("Missing `\\#\\[pallet::pallet\\]`")] /// #[frame_support::pallet] /// pub mod pallet { @@ -183,82 +183,82 @@ macro_rules! assert_pallet_parse_error { /// This function uses a [`Mutex`] to avoid a race condition created when multiple tests try to /// modify and then restore the `CARGO_MANIFEST_DIR` ENV var in an overlapping way. pub fn simulate_manifest_dir, F: FnOnce() + std::panic::UnwindSafe>( - path: P, - closure: F, + path: P, + closure: F, ) { - use std::{env::*, path::*}; + use std::{env::*, path::*}; - /// Ensures that only one thread can modify/restore the `CARGO_MANIFEST_DIR` ENV var at a time, - /// avoiding a race condition because `cargo test` runs tests in parallel. - /// - /// Although this forces all tests that use [`simulate_manifest_dir`] to run sequentially with - /// respect to each other, this is still several orders of magnitude faster than using UI - /// tests, even if they are run in parallel. - static MANIFEST_DIR_LOCK: Mutex<()> = Mutex::new(()); + /// Ensures that only one thread can modify/restore the `CARGO_MANIFEST_DIR` ENV var at a time, + /// avoiding a race condition because `cargo test` runs tests in parallel. + /// + /// Although this forces all tests that use [`simulate_manifest_dir`] to run sequentially with + /// respect to each other, this is still several orders of magnitude faster than using UI + /// tests, even if they are run in parallel. + static MANIFEST_DIR_LOCK: Mutex<()> = Mutex::new(()); - // avoid race condition when swapping out `CARGO_MANIFEST_DIR` - let guard = MANIFEST_DIR_LOCK.lock().unwrap(); + // avoid race condition when swapping out `CARGO_MANIFEST_DIR` + let guard = MANIFEST_DIR_LOCK.lock().unwrap(); - // obtain the current/original `CARGO_MANIFEST_DIR` - let orig = PathBuf::from( - var("CARGO_MANIFEST_DIR").expect("failed to read ENV var `CARGO_MANIFEST_DIR`"), - ); + // obtain the current/original `CARGO_MANIFEST_DIR` + let orig = PathBuf::from( + var("CARGO_MANIFEST_DIR").expect("failed to read ENV var `CARGO_MANIFEST_DIR`"), + ); - // set `CARGO_MANIFEST_DIR` to the provided path, relative to current working dir - set_var("CARGO_MANIFEST_DIR", orig.join(path.as_ref())); + // set `CARGO_MANIFEST_DIR` to the provided path, relative to current working dir + set_var("CARGO_MANIFEST_DIR", orig.join(path.as_ref())); - // safely run closure catching any panics - let result = panic::catch_unwind(closure); + // safely run closure catching any panics + let result = panic::catch_unwind(closure); - // restore original `CARGO_MANIFEST_DIR` before unwinding - set_var("CARGO_MANIFEST_DIR", &orig); + // restore original `CARGO_MANIFEST_DIR` before unwinding + set_var("CARGO_MANIFEST_DIR", &orig); - // unlock the mutex so we don't poison it if there is a panic - drop(guard); + // unlock the mutex so we don't poison it if there is a panic + drop(guard); - // unwind any panics originally encountered when running closure - result.unwrap(); + // unwind any panics originally encountered when running closure + result.unwrap(); } mod tasks; #[test] fn test_parse_minimal_pallet() { - assert_pallet_parses! { - #[manifest_dir("../../pallets/subtensor")] - #[frame_support::pallet] - pub mod pallet { - #[pallet::config] - pub trait Config: frame_system::Config {} + assert_pallet_parses! { + #[manifest_dir("../../examples/basic")] + #[frame_support::pallet] + pub mod pallet { + #[pallet::config] + pub trait Config: frame_system::Config {} - #[pallet::pallet] - pub struct Pallet(_); - } - }; + #[pallet::pallet] + pub struct Pallet(_); + } + }; } #[test] fn test_parse_pallet_missing_pallet() { - assert_pallet_parse_error! { - #[manifest_dir("../../pallets/subtensor")] - #[error_regex("Missing `\\#\\[pallet::pallet\\]`")] - #[frame_support::pallet] - pub mod pallet { - #[pallet::config] - pub trait Config: frame_system::Config {} - } - } + assert_pallet_parse_error! { + #[manifest_dir("../../examples/basic")] + #[error_regex("Missing `\\#\\[pallet::pallet\\]`")] + #[frame_support::pallet] + pub mod pallet { + #[pallet::config] + pub trait Config: frame_system::Config {} + } + } } #[test] fn test_parse_pallet_missing_config() { - assert_pallet_parse_error! { - #[manifest_dir("../../pallets/subtensor")] - #[error_regex("Missing `\\#\\[pallet::config\\]`")] - #[frame_support::pallet] - pub mod pallet { - #[pallet::pallet] - pub struct Pallet(_); - } - } + assert_pallet_parse_error! { + #[manifest_dir("../../examples/basic")] + #[error_regex("Missing `\\#\\[pallet::config\\]`")] + #[frame_support::pallet] + pub mod pallet { + #[pallet::pallet] + pub struct Pallet(_); + } + } } diff --git a/support/procedural-fork/src/pallet/parse/tests/tasks.rs b/support/procedural-fork/src/pallet/parse/tests/tasks.rs index 22a757c8f..9f1436284 100644 --- a/support/procedural-fork/src/pallet/parse/tests/tasks.rs +++ b/support/procedural-fork/src/pallet/parse/tests/tasks.rs @@ -19,222 +19,222 @@ use syn::parse_quote; #[test] fn test_parse_pallet_with_task_enum_missing_impl() { - assert_pallet_parse_error! { - #[manifest_dir("../../pallets/subtensor")] - #[error_regex("Missing `\\#\\[pallet::tasks_experimental\\]` impl")] - #[frame_support::pallet] - pub mod pallet { - #[pallet::task_enum] - pub enum Task { - Something, - } - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(_); - } - } + assert_pallet_parse_error! { + #[manifest_dir("../../examples/basic")] + #[error_regex("Missing `\\#\\[pallet::tasks_experimental\\]` impl")] + #[frame_support::pallet] + pub mod pallet { + #[pallet::task_enum] + pub enum Task { + Something, + } + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + } } #[test] fn test_parse_pallet_with_task_enum_wrong_attribute() { - assert_pallet_parse_error! { - #[manifest_dir("../../pallets/subtensor")] - #[error_regex("expected one of")] - #[frame_support::pallet] - pub mod pallet { - #[pallet::wrong_attribute] - pub enum Task { - Something, - } - - #[pallet::task_list] - impl frame_support::traits::Task for Task - where - T: TypeInfo, - {} - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(_); - } - } + assert_pallet_parse_error! { + #[manifest_dir("../../examples/basic")] + #[error_regex("expected one of")] + #[frame_support::pallet] + pub mod pallet { + #[pallet::wrong_attribute] + pub enum Task { + Something, + } + + #[pallet::task_list] + impl frame_support::traits::Task for Task + where + T: TypeInfo, + {} + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + } } #[test] fn test_parse_pallet_missing_task_enum() { - assert_pallet_parses! { - #[manifest_dir("../../pallets/subtensor")] - #[frame_support::pallet] - pub mod pallet { - #[pallet::tasks_experimental] - #[cfg(test)] // aha, this means it's being eaten - impl frame_support::traits::Task for Task - where - T: TypeInfo, - {} - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(_); - } - }; + assert_pallet_parses! { + #[manifest_dir("../../examples/basic")] + #[frame_support::pallet] + pub mod pallet { + #[pallet::tasks_experimental] + #[cfg(test)] // aha, this means it's being eaten + impl frame_support::traits::Task for Task + where + T: TypeInfo, + {} + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + }; } #[test] fn test_parse_pallet_task_list_in_wrong_place() { - assert_pallet_parse_error! { - #[manifest_dir("../../pallets/subtensor")] - #[error_regex("can only be used on items within an `impl` statement.")] - #[frame_support::pallet] - pub mod pallet { - pub enum MyCustomTaskEnum { - Something, - } - - #[pallet::task_list] - pub fn something() { - println!("hey"); - } - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(_); - } - } + assert_pallet_parse_error! { + #[manifest_dir("../../examples/basic")] + #[error_regex("can only be used on items within an `impl` statement.")] + #[frame_support::pallet] + pub mod pallet { + pub enum MyCustomTaskEnum { + Something, + } + + #[pallet::task_list] + pub fn something() { + println!("hey"); + } + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + } } #[test] fn test_parse_pallet_manual_tasks_impl_without_manual_tasks_enum() { - assert_pallet_parse_error! { - #[manifest_dir("../../pallets/subtensor")] - #[error_regex(".*attribute must be attached to your.*")] - #[frame_support::pallet] - pub mod pallet { - - impl frame_support::traits::Task for Task - where - T: TypeInfo, - { - type Enumeration = sp_std::vec::IntoIter>; - - fn iter() -> Self::Enumeration { - sp_std::vec![Task::increment, Task::decrement].into_iter() - } - } - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(_); - } - } + assert_pallet_parse_error! { + #[manifest_dir("../../examples/basic")] + #[error_regex(".*attribute must be attached to your.*")] + #[frame_support::pallet] + pub mod pallet { + + impl frame_support::traits::Task for Task + where + T: TypeInfo, + { + type Enumeration = sp_std::vec::IntoIter>; + + fn iter() -> Self::Enumeration { + sp_std::vec![Task::increment, Task::decrement].into_iter() + } + } + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + } } #[test] fn test_parse_pallet_manual_task_enum_non_manual_impl() { - assert_pallet_parses! { - #[manifest_dir("../../pallets/subtensor")] - #[frame_support::pallet] - pub mod pallet { - pub enum MyCustomTaskEnum { - Something, - } - - #[pallet::tasks_experimental] - impl frame_support::traits::Task for MyCustomTaskEnum - where - T: TypeInfo, - {} - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(_); - } - }; + assert_pallet_parses! { + #[manifest_dir("../../examples/basic")] + #[frame_support::pallet] + pub mod pallet { + pub enum MyCustomTaskEnum { + Something, + } + + #[pallet::tasks_experimental] + impl frame_support::traits::Task for MyCustomTaskEnum + where + T: TypeInfo, + {} + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + }; } #[test] fn test_parse_pallet_non_manual_task_enum_manual_impl() { - assert_pallet_parses! { - #[manifest_dir("../../pallets/subtensor")] - #[frame_support::pallet] - pub mod pallet { - #[pallet::task_enum] - pub enum MyCustomTaskEnum { - Something, - } - - impl frame_support::traits::Task for MyCustomTaskEnum - where - T: TypeInfo, - {} - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(_); - } - }; + assert_pallet_parses! { + #[manifest_dir("../../examples/basic")] + #[frame_support::pallet] + pub mod pallet { + #[pallet::task_enum] + pub enum MyCustomTaskEnum { + Something, + } + + impl frame_support::traits::Task for MyCustomTaskEnum + where + T: TypeInfo, + {} + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + }; } #[test] fn test_parse_pallet_manual_task_enum_manual_impl() { - assert_pallet_parses! { - #[manifest_dir("../../pallets/subtensor")] - #[frame_support::pallet] - pub mod pallet { - pub enum MyCustomTaskEnum { - Something, - } - - impl frame_support::traits::Task for MyCustomTaskEnum - where - T: TypeInfo, - {} - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(_); - } - }; + assert_pallet_parses! { + #[manifest_dir("../../examples/basic")] + #[frame_support::pallet] + pub mod pallet { + pub enum MyCustomTaskEnum { + Something, + } + + impl frame_support::traits::Task for MyCustomTaskEnum + where + T: TypeInfo, + {} + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + }; } #[test] fn test_parse_pallet_manual_task_enum_mismatch_ident() { - assert_pallet_parses! { - #[manifest_dir("../../pallets/subtensor")] - #[frame_support::pallet] - pub mod pallet { - pub enum WrongIdent { - Something, - } - - #[pallet::tasks_experimental] - impl frame_support::traits::Task for MyCustomTaskEnum - where - T: TypeInfo, - {} - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(_); - } - }; + assert_pallet_parses! { + #[manifest_dir("../../examples/basic")] + #[frame_support::pallet] + pub mod pallet { + pub enum WrongIdent { + Something, + } + + #[pallet::tasks_experimental] + impl frame_support::traits::Task for MyCustomTaskEnum + where + T: TypeInfo, + {} + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + }; } diff --git a/support/procedural-fork/src/pallet/parse/type_value.rs b/support/procedural-fork/src/pallet/parse/type_value.rs index d5c85248f..4d9db30b3 100644 --- a/support/procedural-fork/src/pallet/parse/type_value.rs +++ b/support/procedural-fork/src/pallet/parse/type_value.rs @@ -20,104 +20,104 @@ use syn::spanned::Spanned; /// Definition of type value. Just a function which is expanded to a struct implementing `Get`. pub struct TypeValueDef { - /// The index of error item in pallet module. - pub index: usize, - /// Visibility of the struct to generate. - pub vis: syn::Visibility, - /// Ident of the struct to generate. - pub ident: syn::Ident, - /// The type return by Get. - pub type_: Box, - /// The block returning the value to get - pub block: Box, - /// If type value is generic over `T` (or `T` and `I` for instantiable pallet) - pub is_generic: bool, - /// A set of usage of instance, must be check for consistency with config. - pub instances: Vec, - /// The where clause of the function. - pub where_clause: Option, - /// The span of the pallet::type_value attribute. - pub attr_span: proc_macro2::Span, - /// Docs on the item. - pub docs: Vec, + /// The index of error item in pallet module. + pub index: usize, + /// Visibility of the struct to generate. + pub vis: syn::Visibility, + /// Ident of the struct to generate. + pub ident: syn::Ident, + /// The type return by Get. + pub type_: Box, + /// The block returning the value to get + pub block: Box, + /// If type value is generic over `T` (or `T` and `I` for instantiable pallet) + pub is_generic: bool, + /// A set of usage of instance, must be check for consistency with config. + pub instances: Vec, + /// The where clause of the function. + pub where_clause: Option, + /// The span of the pallet::type_value attribute. + pub attr_span: proc_macro2::Span, + /// Docs on the item. + pub docs: Vec, } impl TypeValueDef { - pub fn try_from( - attr_span: proc_macro2::Span, - index: usize, - item: &mut syn::Item, - ) -> syn::Result { - let item = if let syn::Item::Fn(item) = item { - item - } else { - let msg = "Invalid pallet::type_value, expected item fn"; - return Err(syn::Error::new(item.span(), msg)); - }; + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + ) -> syn::Result { + let item = if let syn::Item::Fn(item) = item { + item + } else { + let msg = "Invalid pallet::type_value, expected item fn"; + return Err(syn::Error::new(item.span(), msg)) + }; - let mut docs = vec![]; - for attr in &item.attrs { - if let syn::Meta::NameValue(meta) = &attr.meta { - if meta.path.get_ident().map_or(false, |ident| ident == "doc") { - docs.push(meta.value.clone()); - continue; - } - } + let mut docs = vec![]; + for attr in &item.attrs { + if let syn::Meta::NameValue(meta) = &attr.meta { + if meta.path.get_ident().map_or(false, |ident| ident == "doc") { + docs.push(meta.value.clone()); + continue + } + } - let msg = "Invalid pallet::type_value, unexpected attribute, only doc attribute are \ + let msg = "Invalid pallet::type_value, unexpected attribute, only doc attribute are \ allowed"; - return Err(syn::Error::new(attr.span(), msg)); - } + return Err(syn::Error::new(attr.span(), msg)) + } - if let Some(span) = item - .sig - .constness - .as_ref() - .map(|t| t.span()) - .or_else(|| item.sig.asyncness.as_ref().map(|t| t.span())) - .or_else(|| item.sig.unsafety.as_ref().map(|t| t.span())) - .or_else(|| item.sig.abi.as_ref().map(|t| t.span())) - .or_else(|| item.sig.variadic.as_ref().map(|t| t.span())) - { - let msg = "Invalid pallet::type_value, unexpected token"; - return Err(syn::Error::new(span, msg)); - } + if let Some(span) = item + .sig + .constness + .as_ref() + .map(|t| t.span()) + .or_else(|| item.sig.asyncness.as_ref().map(|t| t.span())) + .or_else(|| item.sig.unsafety.as_ref().map(|t| t.span())) + .or_else(|| item.sig.abi.as_ref().map(|t| t.span())) + .or_else(|| item.sig.variadic.as_ref().map(|t| t.span())) + { + let msg = "Invalid pallet::type_value, unexpected token"; + return Err(syn::Error::new(span, msg)) + } - if !item.sig.inputs.is_empty() { - let msg = "Invalid pallet::type_value, unexpected argument"; - return Err(syn::Error::new(item.sig.inputs[0].span(), msg)); - } + if !item.sig.inputs.is_empty() { + let msg = "Invalid pallet::type_value, unexpected argument"; + return Err(syn::Error::new(item.sig.inputs[0].span(), msg)) + } - let vis = item.vis.clone(); - let ident = item.sig.ident.clone(); - let block = item.block.clone(); - let type_ = match item.sig.output.clone() { - syn::ReturnType::Type(_, type_) => type_, - syn::ReturnType::Default => { - let msg = "Invalid pallet::type_value, expected return type"; - return Err(syn::Error::new(item.sig.span(), msg)); - } - }; + let vis = item.vis.clone(); + let ident = item.sig.ident.clone(); + let block = item.block.clone(); + let type_ = match item.sig.output.clone() { + syn::ReturnType::Type(_, type_) => type_, + syn::ReturnType::Default => { + let msg = "Invalid pallet::type_value, expected return type"; + return Err(syn::Error::new(item.sig.span(), msg)) + }, + }; - let mut instances = vec![]; - if let Some(usage) = helper::check_type_value_gen(&item.sig.generics, item.sig.span())? { - instances.push(usage); - } + let mut instances = vec![]; + if let Some(usage) = helper::check_type_value_gen(&item.sig.generics, item.sig.span())? { + instances.push(usage); + } - let is_generic = item.sig.generics.type_params().count() > 0; - let where_clause = item.sig.generics.where_clause.clone(); + let is_generic = item.sig.generics.type_params().count() > 0; + let where_clause = item.sig.generics.where_clause.clone(); - Ok(TypeValueDef { - attr_span, - index, - is_generic, - vis, - ident, - block, - type_, - instances, - where_clause, - docs, - }) - } + Ok(TypeValueDef { + attr_span, + index, + is_generic, + vis, + ident, + block, + type_, + instances, + where_clause, + docs, + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/validate_unsigned.rs b/support/procedural-fork/src/pallet/parse/validate_unsigned.rs index 6e5109a74..2bf0a1b6c 100644 --- a/support/procedural-fork/src/pallet/parse/validate_unsigned.rs +++ b/support/procedural-fork/src/pallet/parse/validate_unsigned.rs @@ -20,43 +20,43 @@ use syn::spanned::Spanned; /// The definition of the pallet validate unsigned implementation. pub struct ValidateUnsignedDef { - /// The index of validate unsigned item in pallet module. - pub index: usize, - /// A set of usage of instance, must be check for consistency with config. - pub instances: Vec, + /// The index of validate unsigned item in pallet module. + pub index: usize, + /// A set of usage of instance, must be check for consistency with config. + pub instances: Vec, } impl ValidateUnsignedDef { - pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { - let item = if let syn::Item::Impl(item) = item { - item - } else { - let msg = "Invalid pallet::validate_unsigned, expected item impl"; - return Err(syn::Error::new(item.span(), msg)); - }; - - if item.trait_.is_none() { - let msg = "Invalid pallet::validate_unsigned, expected impl<..> ValidateUnsigned for \ + pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { + let item = if let syn::Item::Impl(item) = item { + item + } else { + let msg = "Invalid pallet::validate_unsigned, expected item impl"; + return Err(syn::Error::new(item.span(), msg)) + }; + + if item.trait_.is_none() { + let msg = "Invalid pallet::validate_unsigned, expected impl<..> ValidateUnsigned for \ Pallet<..>"; - return Err(syn::Error::new(item.span(), msg)); - } - - if let Some(last) = item.trait_.as_ref().unwrap().1.segments.last() { - if last.ident != "ValidateUnsigned" { - let msg = "Invalid pallet::validate_unsigned, expected trait ValidateUnsigned"; - return Err(syn::Error::new(last.span(), msg)); - } - } else { - let msg = "Invalid pallet::validate_unsigned, expected impl<..> ValidateUnsigned for \ + return Err(syn::Error::new(item.span(), msg)) + } + + if let Some(last) = item.trait_.as_ref().unwrap().1.segments.last() { + if last.ident != "ValidateUnsigned" { + let msg = "Invalid pallet::validate_unsigned, expected trait ValidateUnsigned"; + return Err(syn::Error::new(last.span(), msg)) + } + } else { + let msg = "Invalid pallet::validate_unsigned, expected impl<..> ValidateUnsigned for \ Pallet<..>"; - return Err(syn::Error::new(item.span(), msg)); - } + return Err(syn::Error::new(item.span(), msg)) + } - let instances = vec![ - helper::check_pallet_struct_usage(&item.self_ty)?, - helper::check_impl_gen(&item.generics, item.impl_token.span())?, - ]; + let instances = vec![ + helper::check_pallet_struct_usage(&item.self_ty)?, + helper::check_impl_gen(&item.generics, item.impl_token.span())?, + ]; - Ok(ValidateUnsignedDef { index, instances }) - } + Ok(ValidateUnsignedDef { index, instances }) + } } diff --git a/support/procedural-fork/src/pallet_error.rs b/support/procedural-fork/src/pallet_error.rs index e78844c63..693a1e982 100644 --- a/support/procedural-fork/src/pallet_error.rs +++ b/support/procedural-fork/src/pallet_error.rs @@ -20,172 +20,159 @@ use quote::ToTokens; // Derive `PalletError` pub fn derive_pallet_error(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let syn::DeriveInput { - ident: name, - generics, - data, - .. - } = match syn::parse(input) { - Ok(input) => input, - Err(e) => return e.to_compile_error().into(), - }; - - let frame_support = match generate_access_from_frame_or_crate("frame-support") { - Ok(c) => c, - Err(e) => return e.into_compile_error().into(), - }; - let frame_support = &frame_support; - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - - let max_encoded_size = match data { - syn::Data::Struct(syn::DataStruct { fields, .. }) => match fields { - syn::Fields::Named(syn::FieldsNamed { named: fields, .. }) - | syn::Fields::Unnamed(syn::FieldsUnnamed { - unnamed: fields, .. - }) => { - let maybe_field_tys = fields - .iter() - .map(|f| generate_field_types(f, frame_support)) - .collect::>>(); - let field_tys = match maybe_field_tys { - Ok(tys) => tys.into_iter().flatten(), - Err(e) => return e.into_compile_error().into(), - }; - quote::quote! { - 0_usize - #( - .saturating_add(< - #field_tys as #frame_support::traits::PalletError - >::MAX_ENCODED_SIZE) - )* - } - } - syn::Fields::Unit => quote::quote!(0), - }, - syn::Data::Enum(syn::DataEnum { variants, .. }) => { - let field_tys = variants - .iter() - .map(|variant| generate_variant_field_types(variant, frame_support)) - .collect::>>, syn::Error>>(); - - let field_tys = match field_tys { - Ok(tys) => tys.into_iter().flatten().collect::>(), - Err(e) => return e.to_compile_error().into(), - }; - - // We start with `1`, because the discriminant of an enum is stored as u8 - if field_tys.is_empty() { - quote::quote!(1) - } else { - let variant_sizes = field_tys.into_iter().map(|variant_field_tys| { - quote::quote! { - 1_usize - #(.saturating_add(< - #variant_field_tys as #frame_support::traits::PalletError - >::MAX_ENCODED_SIZE))* - } - }); - - quote::quote! {{ - let mut size = 1_usize; - let mut tmp = 0_usize; - #( - tmp = #variant_sizes; - size = if tmp > size { tmp } else { size }; - tmp = 0_usize; - )* - size - }} - } - } - syn::Data::Union(syn::DataUnion { union_token, .. }) => { - let msg = "Cannot derive `PalletError` for union; please implement it directly"; - return syn::Error::new(union_token.span, msg) - .into_compile_error() - .into(); - } - }; - - quote::quote!( - const _: () = { - impl #impl_generics #frame_support::traits::PalletError - for #name #ty_generics #where_clause - { - const MAX_ENCODED_SIZE: usize = #max_encoded_size; - } - }; - ) - .into() + let syn::DeriveInput { ident: name, generics, data, .. } = match syn::parse(input) { + Ok(input) => input, + Err(e) => return e.to_compile_error().into(), + }; + + let frame_support = match generate_access_from_frame_or_crate("frame-support") { + Ok(c) => c, + Err(e) => return e.into_compile_error().into(), + }; + let frame_support = &frame_support; + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + let max_encoded_size = match data { + syn::Data::Struct(syn::DataStruct { fields, .. }) => match fields { + syn::Fields::Named(syn::FieldsNamed { named: fields, .. }) | + syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed: fields, .. }) => { + let maybe_field_tys = fields + .iter() + .map(|f| generate_field_types(f, &frame_support)) + .collect::>>(); + let field_tys = match maybe_field_tys { + Ok(tys) => tys.into_iter().flatten(), + Err(e) => return e.into_compile_error().into(), + }; + quote::quote! { + 0_usize + #( + .saturating_add(< + #field_tys as #frame_support::traits::PalletError + >::MAX_ENCODED_SIZE) + )* + } + }, + syn::Fields::Unit => quote::quote!(0), + }, + syn::Data::Enum(syn::DataEnum { variants, .. }) => { + let field_tys = variants + .iter() + .map(|variant| generate_variant_field_types(variant, &frame_support)) + .collect::>>, syn::Error>>(); + + let field_tys = match field_tys { + Ok(tys) => tys.into_iter().flatten().collect::>(), + Err(e) => return e.to_compile_error().into(), + }; + + // We start with `1`, because the discriminant of an enum is stored as u8 + if field_tys.is_empty() { + quote::quote!(1) + } else { + let variant_sizes = field_tys.into_iter().map(|variant_field_tys| { + quote::quote! { + 1_usize + #(.saturating_add(< + #variant_field_tys as #frame_support::traits::PalletError + >::MAX_ENCODED_SIZE))* + } + }); + + quote::quote! {{ + let mut size = 1_usize; + let mut tmp = 0_usize; + #( + tmp = #variant_sizes; + size = if tmp > size { tmp } else { size }; + tmp = 0_usize; + )* + size + }} + } + }, + syn::Data::Union(syn::DataUnion { union_token, .. }) => { + let msg = "Cannot derive `PalletError` for union; please implement it directly"; + return syn::Error::new(union_token.span, msg).into_compile_error().into() + }, + }; + + quote::quote!( + const _: () = { + impl #impl_generics #frame_support::traits::PalletError + for #name #ty_generics #where_clause + { + const MAX_ENCODED_SIZE: usize = #max_encoded_size; + } + }; + ) + .into() } fn generate_field_types( - field: &syn::Field, - scrate: &syn::Path, + field: &syn::Field, + scrate: &syn::Path, ) -> syn::Result> { - let attrs = &field.attrs; - - for attr in attrs { - if attr.path().is_ident("codec") { - let mut res = None; - - attr.parse_nested_meta(|meta| { - if meta.path.is_ident("skip") { - res = Some(None); - } else if meta.path.is_ident("compact") { - let field_ty = &field.ty; - res = Some(Some( - quote::quote!(#scrate::__private::codec::Compact<#field_ty>), - )); - } else if meta.path.is_ident("compact") { - res = Some(Some(meta.value()?.parse()?)); - } - - Ok(()) - })?; - - if let Some(v) = res { - return Ok(v); - } - } - } - - Ok(Some(field.ty.to_token_stream())) + let attrs = &field.attrs; + + for attr in attrs { + if attr.path().is_ident("codec") { + let mut res = None; + + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("skip") { + res = Some(None); + } else if meta.path.is_ident("compact") { + let field_ty = &field.ty; + res = Some(Some(quote::quote!(#scrate::__private::codec::Compact<#field_ty>))); + } else if meta.path.is_ident("compact") { + res = Some(Some(meta.value()?.parse()?)); + } + + Ok(()) + })?; + + if let Some(v) = res { + return Ok(v) + } + } + } + + Ok(Some(field.ty.to_token_stream())) } fn generate_variant_field_types( - variant: &syn::Variant, - scrate: &syn::Path, + variant: &syn::Variant, + scrate: &syn::Path, ) -> syn::Result>> { - let attrs = &variant.attrs; - - for attr in attrs { - if attr.path().is_ident("codec") { - let mut skip = false; - - // We ignore the error intentionally as this isn't `codec(skip)` when - // `parse_nested_meta` fails. - let _ = attr.parse_nested_meta(|meta| { - skip = meta.path.is_ident("skip"); - Ok(()) - }); - - if skip { - return Ok(None); - } - } - } - - match &variant.fields { - syn::Fields::Named(syn::FieldsNamed { named: fields, .. }) - | syn::Fields::Unnamed(syn::FieldsUnnamed { - unnamed: fields, .. - }) => { - let field_tys = fields - .iter() - .map(|field| generate_field_types(field, scrate)) - .collect::>>()?; - Ok(Some(field_tys.into_iter().flatten().collect())) - } - syn::Fields::Unit => Ok(None), - } + let attrs = &variant.attrs; + + for attr in attrs { + if attr.path().is_ident("codec") { + let mut skip = false; + + // We ignore the error intentionally as this isn't `codec(skip)` when + // `parse_nested_meta` fails. + let _ = attr.parse_nested_meta(|meta| { + skip = meta.path.is_ident("skip"); + Ok(()) + }); + + if skip { + return Ok(None) + } + } + } + + match &variant.fields { + syn::Fields::Named(syn::FieldsNamed { named: fields, .. }) | + syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed: fields, .. }) => { + let field_tys = fields + .iter() + .map(|field| generate_field_types(field, scrate)) + .collect::>>()?; + Ok(Some(field_tys.into_iter().flatten().collect())) + }, + syn::Fields::Unit => Ok(None), + } } diff --git a/support/procedural-fork/src/runtime/expand/mod.rs b/support/procedural-fork/src/runtime/expand/mod.rs index 38d40964b..93c88fce9 100644 --- a/support/procedural-fork/src/runtime/expand/mod.rs +++ b/support/procedural-fork/src/runtime/expand/mod.rs @@ -17,20 +17,20 @@ use super::parse::runtime_types::RuntimeType; use crate::{ - construct_runtime::{ - check_pallet_number, decl_all_pallets, decl_integrity_test, decl_pallet_runtime_setup, - decl_static_assertions, expand, - }, - runtime::{ - parse::{ - AllPalletsDeclaration, ExplicitAllPalletsDeclaration, ImplicitAllPalletsDeclaration, - }, - Def, - }, + construct_runtime::{ + check_pallet_number, decl_all_pallets, decl_integrity_test, decl_pallet_runtime_setup, + decl_static_assertions, expand, + }, + runtime::{ + parse::{ + AllPalletsDeclaration, ExplicitAllPalletsDeclaration, ImplicitAllPalletsDeclaration, + }, + Def, + }, }; use cfg_expr::Predicate; use frame_support_procedural_tools::{ - generate_access_from_frame_or_crate, generate_crate_access, generate_hidden_includes, + generate_access_from_frame_or_crate, generate_crate_access, generate_hidden_includes, }; use proc_macro2::TokenStream as TokenStream2; use quote::quote; @@ -41,296 +41,280 @@ use syn::{Ident, Result}; const SYSTEM_PALLET_NAME: &str = "System"; pub fn expand(def: Def, legacy_ordering: bool) -> TokenStream2 { - let input = def.input; - - let (check_pallet_number_res, res) = match def.pallets { - AllPalletsDeclaration::Implicit(ref decl) => ( - check_pallet_number(input.clone(), decl.pallet_count), - construct_runtime_implicit_to_explicit(input, decl.clone(), legacy_ordering), - ), - AllPalletsDeclaration::Explicit(ref decl) => ( - check_pallet_number(input, decl.pallets.len()), - construct_runtime_final_expansion( - def.runtime_struct.ident.clone(), - decl.clone(), - def.runtime_types.clone(), - legacy_ordering, - ), - ), - }; - - let res = res.unwrap_or_else(|e| e.to_compile_error()); - - // We want to provide better error messages to the user and thus, handle the error here - // separately. If there is an error, we print the error and still generate all of the code to - // get in overall less errors for the user. - let res = if let Err(error) = check_pallet_number_res { - let error = error.to_compile_error(); - - quote! { - #error - - #res - } - } else { - res - }; - - expander::Expander::new("construct_runtime") - .dry(std::env::var("FRAME_EXPAND").is_err()) - .verbose(true) - .write_to_out_dir(res) - .expect("Does not fail because of IO in OUT_DIR; qed") + let input = def.input; + + let (check_pallet_number_res, res) = match def.pallets { + AllPalletsDeclaration::Implicit(ref decl) => ( + check_pallet_number(input.clone(), decl.pallet_count), + construct_runtime_implicit_to_explicit(input.into(), decl.clone(), legacy_ordering), + ), + AllPalletsDeclaration::Explicit(ref decl) => ( + check_pallet_number(input, decl.pallets.len()), + construct_runtime_final_expansion( + def.runtime_struct.ident.clone(), + decl.clone(), + def.runtime_types.clone(), + legacy_ordering, + ), + ), + }; + + let res = res.unwrap_or_else(|e| e.to_compile_error()); + + // We want to provide better error messages to the user and thus, handle the error here + // separately. If there is an error, we print the error and still generate all of the code to + // get in overall less errors for the user. + let res = if let Err(error) = check_pallet_number_res { + let error = error.to_compile_error(); + + quote! { + #error + + #res + } + } else { + res + }; + + let res = expander::Expander::new("construct_runtime") + .dry(std::env::var("FRAME_EXPAND").is_err()) + .verbose(true) + .write_to_out_dir(res) + .expect("Does not fail because of IO in OUT_DIR; qed"); + + res.into() } fn construct_runtime_implicit_to_explicit( - input: TokenStream2, - definition: ImplicitAllPalletsDeclaration, - legacy_ordering: bool, + input: TokenStream2, + definition: ImplicitAllPalletsDeclaration, + legacy_ordering: bool, ) -> Result { - let frame_support = generate_access_from_frame_or_crate("frame-support")?; - let attr = if legacy_ordering { - quote!((legacy_ordering)) - } else { - quote!() - }; - let mut expansion = quote::quote!( - #[frame_support::runtime #attr] - #input - ); - for pallet in definition.pallet_decls.iter() { - let pallet_path = &pallet.path; - let pallet_name = &pallet.name; - let pallet_instance = pallet - .instance - .as_ref() - .map(|instance| quote::quote!(<#instance>)); - expansion = quote::quote!( - #frame_support::__private::tt_call! { - macro = [{ #pallet_path::tt_default_parts_v2 }] - frame_support = [{ #frame_support }] - ~~> #frame_support::match_and_insert! { - target = [{ #expansion }] - pattern = [{ #pallet_name = #pallet_path #pallet_instance }] - } - } - ); - } - - Ok(expansion) + let frame_support = generate_access_from_frame_or_crate("frame-support")?; + let attr = if legacy_ordering { quote!((legacy_ordering)) } else { quote!() }; + let mut expansion = quote::quote!( + #[frame_support::runtime #attr] + #input + ); + for pallet in definition.pallet_decls.iter() { + let pallet_path = &pallet.path; + let pallet_name = &pallet.name; + let pallet_instance = pallet.instance.as_ref().map(|instance| quote::quote!(<#instance>)); + expansion = quote::quote!( + #frame_support::__private::tt_call! { + macro = [{ #pallet_path::tt_default_parts_v2 }] + frame_support = [{ #frame_support }] + ~~> #frame_support::match_and_insert! { + target = [{ #expansion }] + pattern = [{ #pallet_name = #pallet_path #pallet_instance }] + } + } + ); + } + + Ok(expansion) } fn construct_runtime_final_expansion( - name: Ident, - definition: ExplicitAllPalletsDeclaration, - runtime_types: Vec, - legacy_ordering: bool, + name: Ident, + definition: ExplicitAllPalletsDeclaration, + runtime_types: Vec, + legacy_ordering: bool, ) -> Result { - let ExplicitAllPalletsDeclaration { - mut pallets, - name: pallets_name, - } = definition; - - if !legacy_ordering { - // Ensure that order of hooks is based on the pallet index - pallets.sort_by_key(|p| p.index); - } - - let system_pallet = pallets - .iter() - .find(|decl| decl.name == SYSTEM_PALLET_NAME) - .ok_or_else(|| { - syn::Error::new( - pallets_name.span(), - "`System` pallet declaration is missing. \ + let ExplicitAllPalletsDeclaration { mut pallets, name: pallets_name } = definition; + + if !legacy_ordering { + // Ensure that order of hooks is based on the pallet index + pallets.sort_by_key(|p| p.index); + } + + let system_pallet = + pallets.iter().find(|decl| decl.name == SYSTEM_PALLET_NAME).ok_or_else(|| { + syn::Error::new( + pallets_name.span(), + "`System` pallet declaration is missing. \ Please add this line: `pub type System = frame_system;`", - ) - })?; - if !system_pallet.cfg_pattern.is_empty() { - return Err(syn::Error::new( - system_pallet.name.span(), - "`System` pallet declaration is feature gated, please remove any `#[cfg]` attributes", - )); - } - - let features = pallets - .iter() - .filter(|&decl| (!decl.cfg_pattern.is_empty())) - .flat_map(|decl| { - decl.cfg_pattern.iter().flat_map(|attr| { - attr.predicates().filter_map(|pred| match pred { - Predicate::Feature(feat) => Some(feat), - Predicate::Test => Some("test"), - _ => None, - }) - }) - }) - .collect::>(); - - 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 frame_system = generate_access_from_frame_or_crate("frame-system")?; - let block = quote!(<#name as #frame_system::Config>::Block); - let unchecked_extrinsic = quote!(<#block as #scrate::sp_runtime::traits::Block>::Extrinsic); - - let mut dispatch = None; - let mut outer_event = None; - let mut outer_error = None; - let mut outer_origin = None; - let mut freeze_reason = None; - let mut hold_reason = None; - let mut slash_reason = None; - let mut lock_id = None; - let mut task = None; - - for runtime_type in runtime_types.iter() { - match runtime_type { - RuntimeType::RuntimeCall(_) => { - dispatch = Some(expand::expand_outer_dispatch( - &name, - system_pallet, - &pallets, - &scrate, - )); - } - RuntimeType::RuntimeEvent(_) => { - outer_event = Some(expand::expand_outer_enum( - &name, - &pallets, - &scrate, - expand::OuterEnumType::Event, - )?); - } - RuntimeType::RuntimeError(_) => { - outer_error = Some(expand::expand_outer_enum( - &name, - &pallets, - &scrate, - expand::OuterEnumType::Error, - )?); - } - RuntimeType::RuntimeOrigin(_) => { - outer_origin = Some(expand::expand_outer_origin( - &name, - system_pallet, - &pallets, - &scrate, - )?); - } - RuntimeType::RuntimeFreezeReason(_) => { - freeze_reason = Some(expand::expand_outer_freeze_reason(&pallets, &scrate)); - } - RuntimeType::RuntimeHoldReason(_) => { - hold_reason = Some(expand::expand_outer_hold_reason(&pallets, &scrate)); - } - RuntimeType::RuntimeSlashReason(_) => { - slash_reason = Some(expand::expand_outer_slash_reason(&pallets, &scrate)); - } - RuntimeType::RuntimeLockId(_) => { - lock_id = Some(expand::expand_outer_lock_id(&pallets, &scrate)); - } - RuntimeType::RuntimeTask(_) => { - task = Some(expand::expand_outer_task(&name, &pallets, &scrate)); - } - } - } - - let all_pallets = decl_all_pallets(&name, pallets.iter(), &features); - let pallet_to_index = decl_pallet_runtime_setup(&name, &pallets, &scrate); - - let metadata = expand::expand_runtime_metadata( - &name, - &pallets, - &scrate, - &unchecked_extrinsic, - &system_pallet.path, - ); - let outer_config = expand::expand_outer_config(&name, &pallets, &scrate); - let inherent = - expand::expand_outer_inherent(&name, &block, &unchecked_extrinsic, &pallets, &scrate); - let validate_unsigned = expand::expand_outer_validate_unsigned(&name, &pallets, &scrate); - let integrity_test = decl_integrity_test(&scrate); - let static_assertions = decl_static_assertions(&name, &pallets, &scrate); - - let res = quote!( - #scrate_decl - - // Prevent UncheckedExtrinsic to print unused warning. - const _: () = { - #[allow(unused)] - type __hidden_use_of_unchecked_extrinsic = #unchecked_extrinsic; - }; - - #[derive( - Clone, Copy, PartialEq, Eq, #scrate::sp_runtime::RuntimeDebug, - #scrate::__private::scale_info::TypeInfo - )] - pub struct #name; - impl #scrate::sp_runtime::traits::GetRuntimeBlockType for #name { - type RuntimeBlock = #block; - } - - // Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata. - // The function is implemented by calling `impl_runtime_apis!`. - // - // However, the `runtime` may be used without calling `impl_runtime_apis!`. - // Rely on the `Deref` trait to differentiate between a runtime that implements - // APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro runtime). - // - // Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function. - // `InternalConstructRuntime` is implemented by the `runtime` for Runtime references (`& Runtime`), - // while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`). - // - // Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!` - // when both macros are called; and will resolve an empty `runtime_metadata` when only the `runtime` - // is used. - - #[doc(hidden)] - trait InternalConstructRuntime { - #[inline(always)] - fn runtime_metadata(&self) -> #scrate::__private::sp_std::vec::Vec<#scrate::__private::metadata_ir::RuntimeApiMetadataIR> { - Default::default() - } - } - #[doc(hidden)] - impl InternalConstructRuntime for &#name {} - - #outer_event - - #outer_error - - #outer_origin - - #all_pallets - - #pallet_to_index - - #dispatch - - #task - - #metadata - - #outer_config - - #inherent - - #validate_unsigned - - #freeze_reason - - #hold_reason - - #lock_id - - #slash_reason - - #integrity_test - - #static_assertions - ); + ) + })?; + if !system_pallet.cfg_pattern.is_empty() { + return Err(syn::Error::new( + system_pallet.name.span(), + "`System` pallet declaration is feature gated, please remove any `#[cfg]` attributes", + )) + } + + let features = pallets + .iter() + .filter_map(|decl| { + (!decl.cfg_pattern.is_empty()).then(|| { + decl.cfg_pattern.iter().flat_map(|attr| { + attr.predicates().filter_map(|pred| match pred { + Predicate::Feature(feat) => Some(feat), + Predicate::Test => Some("test"), + _ => None, + }) + }) + }) + }) + .flatten() + .collect::>(); + + 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 frame_system = generate_access_from_frame_or_crate("frame-system")?; + let block = quote!(<#name as #frame_system::Config>::Block); + let unchecked_extrinsic = quote!(<#block as #scrate::sp_runtime::traits::Block>::Extrinsic); + + let mut dispatch = None; + let mut outer_event = None; + let mut outer_error = None; + let mut outer_origin = None; + let mut freeze_reason = None; + let mut hold_reason = None; + let mut slash_reason = None; + let mut lock_id = None; + let mut task = None; + + for runtime_type in runtime_types.iter() { + match runtime_type { + RuntimeType::RuntimeCall(_) => { + dispatch = + Some(expand::expand_outer_dispatch(&name, system_pallet, &pallets, &scrate)); + }, + RuntimeType::RuntimeEvent(_) => { + outer_event = Some(expand::expand_outer_enum( + &name, + &pallets, + &scrate, + expand::OuterEnumType::Event, + )?); + }, + RuntimeType::RuntimeError(_) => { + outer_error = Some(expand::expand_outer_enum( + &name, + &pallets, + &scrate, + expand::OuterEnumType::Error, + )?); + }, + RuntimeType::RuntimeOrigin(_) => { + outer_origin = + Some(expand::expand_outer_origin(&name, system_pallet, &pallets, &scrate)?); + }, + RuntimeType::RuntimeFreezeReason(_) => { + freeze_reason = Some(expand::expand_outer_freeze_reason(&pallets, &scrate)); + }, + RuntimeType::RuntimeHoldReason(_) => { + hold_reason = Some(expand::expand_outer_hold_reason(&pallets, &scrate)); + }, + RuntimeType::RuntimeSlashReason(_) => { + slash_reason = Some(expand::expand_outer_slash_reason(&pallets, &scrate)); + }, + RuntimeType::RuntimeLockId(_) => { + lock_id = Some(expand::expand_outer_lock_id(&pallets, &scrate)); + }, + RuntimeType::RuntimeTask(_) => { + task = Some(expand::expand_outer_task(&name, &pallets, &scrate)); + }, + } + } + + let all_pallets = decl_all_pallets(&name, pallets.iter(), &features); + let pallet_to_index = decl_pallet_runtime_setup(&name, &pallets, &scrate); + + let metadata = expand::expand_runtime_metadata( + &name, + &pallets, + &scrate, + &unchecked_extrinsic, + &system_pallet.path, + ); + let outer_config = expand::expand_outer_config(&name, &pallets, &scrate); + let inherent = + expand::expand_outer_inherent(&name, &block, &unchecked_extrinsic, &pallets, &scrate); + let validate_unsigned = expand::expand_outer_validate_unsigned(&name, &pallets, &scrate); + let integrity_test = decl_integrity_test(&scrate); + let static_assertions = decl_static_assertions(&name, &pallets, &scrate); + + let res = quote!( + #scrate_decl + + // Prevent UncheckedExtrinsic to print unused warning. + const _: () = { + #[allow(unused)] + type __hidden_use_of_unchecked_extrinsic = #unchecked_extrinsic; + }; + + #[derive( + Clone, Copy, PartialEq, Eq, #scrate::sp_runtime::RuntimeDebug, + #scrate::__private::scale_info::TypeInfo + )] + pub struct #name; + impl #scrate::sp_runtime::traits::GetRuntimeBlockType for #name { + type RuntimeBlock = #block; + } + + // Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata. + // The function is implemented by calling `impl_runtime_apis!`. + // + // However, the `runtime` may be used without calling `impl_runtime_apis!`. + // Rely on the `Deref` trait to differentiate between a runtime that implements + // APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro runtime). + // + // Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function. + // `InternalConstructRuntime` is implemented by the `runtime` for Runtime references (`& Runtime`), + // while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`). + // + // Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!` + // when both macros are called; and will resolve an empty `runtime_metadata` when only the `runtime` + // is used. + + #[doc(hidden)] + trait InternalConstructRuntime { + #[inline(always)] + fn runtime_metadata(&self) -> #scrate::__private::sp_std::vec::Vec<#scrate::__private::metadata_ir::RuntimeApiMetadataIR> { + Default::default() + } + } + #[doc(hidden)] + impl InternalConstructRuntime for &#name {} + + #outer_event + + #outer_error + + #outer_origin + + #all_pallets + + #pallet_to_index + + #dispatch + + #task + + #metadata + + #outer_config + + #inherent + + #validate_unsigned + + #freeze_reason + + #hold_reason + + #lock_id + + #slash_reason + + #integrity_test + + #static_assertions + ); - Ok(res) + Ok(res) } diff --git a/support/procedural-fork/src/runtime/mod.rs b/support/procedural-fork/src/runtime/mod.rs index 888a15e11..aaae579eb 100644 --- a/support/procedural-fork/src/runtime/mod.rs +++ b/support/procedural-fork/src/runtime/mod.rs @@ -210,27 +210,27 @@ mod expand; mod parse; mod keyword { - syn::custom_keyword!(legacy_ordering); + syn::custom_keyword!(legacy_ordering); } pub fn runtime(attr: TokenStream, tokens: TokenStream) -> TokenStream { - let mut legacy_ordering = false; - if !attr.is_empty() { - if syn::parse::(attr.clone()).is_ok() { - legacy_ordering = true; - } else { - let msg = "Invalid runtime macro call: unexpected attribute. Macro call must be \ + let mut legacy_ordering = false; + if !attr.is_empty() { + if let Ok(_) = syn::parse::(attr.clone()) { + legacy_ordering = true; + } else { + let msg = "Invalid runtime macro call: unexpected attribute. Macro call must be \ bare, such as `#[frame_support::runtime]` or `#[runtime]`, or must specify the \ `legacy_ordering` attribute, such as `#[frame_support::runtime(legacy_ordering)]` or \ #[runtime(legacy_ordering)]."; - let span = proc_macro2::TokenStream::from(attr).span(); - return syn::Error::new(span, msg).to_compile_error().into(); - } - } + let span = proc_macro2::TokenStream::from(attr).span(); + return syn::Error::new(span, msg).to_compile_error().into() + } + } - let item = syn::parse_macro_input!(tokens as syn::ItemMod); - match parse::Def::try_from(item) { - Ok(def) => expand::expand(def, legacy_ordering).into(), - Err(e) => e.to_compile_error().into(), - } + let item = syn::parse_macro_input!(tokens as syn::ItemMod); + match parse::Def::try_from(item) { + Ok(def) => expand::expand(def, legacy_ordering).into(), + Err(e) => e.to_compile_error().into(), + } } diff --git a/support/procedural-fork/src/runtime/parse/helper.rs b/support/procedural-fork/src/runtime/parse/helper.rs index 17e362410..f05395f9b 100644 --- a/support/procedural-fork/src/runtime/parse/helper.rs +++ b/support/procedural-fork/src/runtime/parse/helper.rs @@ -19,26 +19,19 @@ use crate::pallet::parse::helper::MutItemAttrs; use quote::ToTokens; pub(crate) fn take_first_item_runtime_attr( - item: &mut impl MutItemAttrs, + item: &mut impl MutItemAttrs, ) -> syn::Result> where - Attr: syn::parse::Parse, + Attr: syn::parse::Parse, { - let attrs = if let Some(attrs) = item.mut_item_attrs() { - attrs - } else { - return Ok(None); - }; + let attrs = if let Some(attrs) = item.mut_item_attrs() { attrs } else { return Ok(None) }; - if let Some(index) = attrs.iter().position(|attr| { - attr.path() - .segments - .first() - .map_or(false, |segment| segment.ident == "runtime") - }) { - let runtime_attr = attrs.remove(index); - Ok(Some(syn::parse2(runtime_attr.into_token_stream())?)) - } else { - Ok(None) - } + if let Some(index) = attrs.iter().position(|attr| { + attr.path().segments.first().map_or(false, |segment| segment.ident == "runtime") + }) { + let runtime_attr = attrs.remove(index); + Ok(Some(syn::parse2(runtime_attr.into_token_stream())?)) + } else { + Ok(None) + } } diff --git a/support/procedural-fork/src/runtime/parse/mod.rs b/support/procedural-fork/src/runtime/parse/mod.rs index c2b33fd76..893cb4726 100644 --- a/support/procedural-fork/src/runtime/parse/mod.rs +++ b/support/procedural-fork/src/runtime/parse/mod.rs @@ -32,229 +32,220 @@ use frame_support_procedural_tools::syn_ext as ext; use runtime_types::RuntimeType; mod keyword { - use syn::custom_keyword; + use syn::custom_keyword; - custom_keyword!(runtime); - custom_keyword!(derive); - custom_keyword!(pallet_index); - custom_keyword!(disable_call); - custom_keyword!(disable_unsigned); + custom_keyword!(runtime); + custom_keyword!(derive); + custom_keyword!(pallet_index); + custom_keyword!(disable_call); + custom_keyword!(disable_unsigned); } enum RuntimeAttr { - Runtime(proc_macro2::Span), - Derive(proc_macro2::Span, Vec), - PalletIndex(proc_macro2::Span, u8), - DisableCall(proc_macro2::Span), - DisableUnsigned(proc_macro2::Span), + Runtime(proc_macro2::Span), + Derive(proc_macro2::Span, Vec), + PalletIndex(proc_macro2::Span, u8), + DisableCall(proc_macro2::Span), + DisableUnsigned(proc_macro2::Span), } impl RuntimeAttr { - fn span(&self) -> proc_macro2::Span { - match self { - Self::Runtime(span) => *span, - Self::Derive(span, _) => *span, - Self::PalletIndex(span, _) => *span, - Self::DisableCall(span) => *span, - Self::DisableUnsigned(span) => *span, - } - } + fn span(&self) -> proc_macro2::Span { + match self { + Self::Runtime(span) => *span, + Self::Derive(span, _) => *span, + Self::PalletIndex(span, _) => *span, + Self::DisableCall(span) => *span, + Self::DisableUnsigned(span) => *span, + } + } } impl syn::parse::Parse for RuntimeAttr { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - let content; - syn::bracketed!(content in input); - content.parse::()?; - content.parse::()?; + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; - let lookahead = content.lookahead1(); - if lookahead.peek(keyword::runtime) { - Ok(RuntimeAttr::Runtime( - content.parse::()?.span(), - )) - } else if lookahead.peek(keyword::derive) { - let _ = content.parse::(); - let derive_content; - syn::parenthesized!(derive_content in content); - let runtime_types = - derive_content.parse::>()?; - let runtime_types = runtime_types.inner.into_iter().collect(); - Ok(RuntimeAttr::Derive(derive_content.span(), runtime_types)) - } else if lookahead.peek(keyword::pallet_index) { - let _ = content.parse::(); - let pallet_index_content; - syn::parenthesized!(pallet_index_content in content); - let pallet_index = pallet_index_content.parse::()?; - if !pallet_index.suffix().is_empty() { - let msg = "Number literal must not have a suffix"; - return Err(syn::Error::new(pallet_index.span(), msg)); - } - Ok(RuntimeAttr::PalletIndex( - pallet_index.span(), - pallet_index.base10_parse()?, - )) - } else if lookahead.peek(keyword::disable_call) { - Ok(RuntimeAttr::DisableCall( - content.parse::()?.span(), - )) - } else if lookahead.peek(keyword::disable_unsigned) { - Ok(RuntimeAttr::DisableUnsigned( - content.parse::()?.span(), - )) - } else { - Err(lookahead.error()) - } - } + let lookahead = content.lookahead1(); + if lookahead.peek(keyword::runtime) { + Ok(RuntimeAttr::Runtime(content.parse::()?.span())) + } else if lookahead.peek(keyword::derive) { + let _ = content.parse::(); + let derive_content; + syn::parenthesized!(derive_content in content); + let runtime_types = + derive_content.parse::>()?; + let runtime_types = runtime_types.inner.into_iter().collect(); + Ok(RuntimeAttr::Derive(derive_content.span(), runtime_types)) + } else if lookahead.peek(keyword::pallet_index) { + let _ = content.parse::(); + let pallet_index_content; + syn::parenthesized!(pallet_index_content in content); + let pallet_index = pallet_index_content.parse::()?; + if !pallet_index.suffix().is_empty() { + let msg = "Number literal must not have a suffix"; + return Err(syn::Error::new(pallet_index.span(), msg)) + } + Ok(RuntimeAttr::PalletIndex(pallet_index.span(), pallet_index.base10_parse()?)) + } else if lookahead.peek(keyword::disable_call) { + Ok(RuntimeAttr::DisableCall(content.parse::()?.span())) + } else if lookahead.peek(keyword::disable_unsigned) { + Ok(RuntimeAttr::DisableUnsigned(content.parse::()?.span())) + } else { + Err(lookahead.error()) + } + } } #[derive(Debug, Clone)] pub enum AllPalletsDeclaration { - Implicit(ImplicitAllPalletsDeclaration), - Explicit(ExplicitAllPalletsDeclaration), + Implicit(ImplicitAllPalletsDeclaration), + Explicit(ExplicitAllPalletsDeclaration), } /// Declaration of a runtime with some pallet with implicit declaration of parts. #[derive(Debug, Clone)] pub struct ImplicitAllPalletsDeclaration { - pub name: Ident, - pub pallet_decls: Vec, - pub pallet_count: usize, + pub name: Ident, + pub pallet_decls: Vec, + pub pallet_count: usize, } /// Declaration of a runtime with all pallet having explicit declaration of parts. #[derive(Debug, Clone)] pub struct ExplicitAllPalletsDeclaration { - pub name: Ident, - pub pallets: Vec, + pub name: Ident, + pub pallets: Vec, } pub struct Def { - pub input: TokenStream2, - pub item: syn::ItemMod, - pub runtime_struct: runtime_struct::RuntimeStructDef, - pub pallets: AllPalletsDeclaration, - pub runtime_types: Vec, + pub input: TokenStream2, + pub item: syn::ItemMod, + pub runtime_struct: runtime_struct::RuntimeStructDef, + pub pallets: AllPalletsDeclaration, + pub runtime_types: Vec, } impl Def { - pub fn try_from(mut item: syn::ItemMod) -> syn::Result { - let input: TokenStream2 = item.to_token_stream(); - let item_span = item.span(); - let items = &mut item - .content - .as_mut() - .ok_or_else(|| { - let msg = "Invalid runtime definition, expected mod to be inlined."; - syn::Error::new(item_span, msg) - })? - .1; + pub fn try_from(mut item: syn::ItemMod) -> syn::Result { + let input: TokenStream2 = item.to_token_stream().into(); + let item_span = item.span(); + let items = &mut item + .content + .as_mut() + .ok_or_else(|| { + let msg = "Invalid runtime definition, expected mod to be inlined."; + syn::Error::new(item_span, msg) + })? + .1; - let mut runtime_struct = None; - let mut runtime_types = None; + let mut runtime_struct = None; + let mut runtime_types = None; - let mut indices = HashMap::new(); - let mut names = HashMap::new(); + let mut indices = HashMap::new(); + let mut names = HashMap::new(); - let mut pallet_decls = vec![]; - let mut pallets = vec![]; + let mut pallet_decls = vec![]; + let mut pallets = vec![]; - for item in items.iter_mut() { - let mut pallet_item = None; - let mut pallet_index = 0; + for item in items.iter_mut() { + let mut pallet_item = None; + let mut pallet_index = 0; - let mut disable_call = false; - let mut disable_unsigned = false; + let mut disable_call = false; + let mut disable_unsigned = false; - while let Some(runtime_attr) = - helper::take_first_item_runtime_attr::(item)? - { - match runtime_attr { - RuntimeAttr::Runtime(span) if runtime_struct.is_none() => { - let p = runtime_struct::RuntimeStructDef::try_from(span, item)?; - runtime_struct = Some(p); - } - RuntimeAttr::Derive(_, types) if runtime_types.is_none() => { - runtime_types = Some(types); - } - RuntimeAttr::PalletIndex(span, index) => { - pallet_index = index; - pallet_item = if let syn::Item::Type(item) = item { - Some(item.clone()) - } else { - let msg = "Invalid runtime::pallet_index, expected type definition"; - return Err(syn::Error::new(span, msg)); - }; - } - RuntimeAttr::DisableCall(_) => disable_call = true, - RuntimeAttr::DisableUnsigned(_) => disable_unsigned = true, - attr => { - let msg = "Invalid duplicated attribute"; - return Err(syn::Error::new(attr.span(), msg)); - } - } - } + while let Some(runtime_attr) = + helper::take_first_item_runtime_attr::(item)? + { + match runtime_attr { + RuntimeAttr::Runtime(span) if runtime_struct.is_none() => { + let p = runtime_struct::RuntimeStructDef::try_from(span, item)?; + runtime_struct = Some(p); + }, + RuntimeAttr::Derive(_, types) if runtime_types.is_none() => { + runtime_types = Some(types); + }, + RuntimeAttr::PalletIndex(span, index) => { + pallet_index = index; + pallet_item = if let syn::Item::Type(item) = item { + Some(item.clone()) + } else { + let msg = "Invalid runtime::pallet_index, expected type definition"; + return Err(syn::Error::new(span, msg)) + }; + }, + RuntimeAttr::DisableCall(_) => disable_call = true, + RuntimeAttr::DisableUnsigned(_) => disable_unsigned = true, + attr => { + let msg = "Invalid duplicated attribute"; + return Err(syn::Error::new(attr.span(), msg)) + }, + } + } - if let Some(pallet_item) = pallet_item { - match *pallet_item.ty.clone() { - syn::Type::Path(ref path) => { - let pallet_decl = - PalletDeclaration::try_from(item.span(), &pallet_item, path)?; + if let Some(pallet_item) = pallet_item { + match *pallet_item.ty.clone() { + syn::Type::Path(ref path) => { + let pallet_decl = + PalletDeclaration::try_from(item.span(), &pallet_item, path)?; - if let Some(used_pallet) = - names.insert(pallet_decl.name.clone(), pallet_decl.name.span()) - { - let msg = "Two pallets with the same name!"; + if let Some(used_pallet) = + names.insert(pallet_decl.name.clone(), pallet_decl.name.span()) + { + let msg = "Two pallets with the same name!"; - let mut err = syn::Error::new(used_pallet, msg); - err.combine(syn::Error::new(pallet_decl.name.span(), msg)); - return Err(err); - } + let mut err = syn::Error::new(used_pallet, &msg); + err.combine(syn::Error::new(pallet_decl.name.span(), &msg)); + return Err(err) + } - pallet_decls.push(pallet_decl); - } - syn::Type::TraitObject(syn::TypeTraitObject { bounds, .. }) => { - let pallet = Pallet::try_from( - item.span(), - &pallet_item, - pallet_index, - disable_call, - disable_unsigned, - &bounds, - )?; + pallet_decls.push(pallet_decl); + }, + syn::Type::TraitObject(syn::TypeTraitObject { bounds, .. }) => { + let pallet = Pallet::try_from( + item.span(), + &pallet_item, + pallet_index, + disable_call, + disable_unsigned, + &bounds, + )?; - if let Some(used_pallet) = indices.insert(pallet.index, pallet.name.clone()) - { - let msg = format!( + if let Some(used_pallet) = indices.insert(pallet.index, pallet.name.clone()) + { + let msg = format!( "Pallet indices are conflicting: Both pallets {} and {} are at index {}", used_pallet, pallet.name, pallet.index, ); - let mut err = syn::Error::new(used_pallet.span(), &msg); - err.combine(syn::Error::new(pallet.name.span(), msg)); - return Err(err); - } + let mut err = syn::Error::new(used_pallet.span(), &msg); + err.combine(syn::Error::new(pallet.name.span(), msg)); + return Err(err) + } - pallets.push(pallet); - } - _ => continue, - } - } - } + pallets.push(pallet); + }, + _ => continue, + } + } + } - let name = item.ident.clone(); - let decl_count = pallet_decls.len(); - let pallets = if decl_count > 0 { - AllPalletsDeclaration::Implicit(ImplicitAllPalletsDeclaration { - name, - pallet_decls, - pallet_count: decl_count.saturating_add(pallets.len()), - }) - } else { - AllPalletsDeclaration::Explicit(ExplicitAllPalletsDeclaration { name, pallets }) - }; + let name = item.ident.clone(); + let decl_count = pallet_decls.len(); + let pallets = if decl_count > 0 { + AllPalletsDeclaration::Implicit(ImplicitAllPalletsDeclaration { + name, + pallet_decls, + pallet_count: decl_count.saturating_add(pallets.len()), + }) + } else { + AllPalletsDeclaration::Explicit(ExplicitAllPalletsDeclaration { name, pallets }) + }; - let def = Def { + let def = Def { input, item, runtime_struct: runtime_struct.ok_or_else(|| { @@ -270,6 +261,6 @@ impl Def { })?, }; - Ok(def) - } + Ok(def) + } } diff --git a/support/procedural-fork/src/runtime/parse/pallet.rs b/support/procedural-fork/src/runtime/parse/pallet.rs index 976aba764..d2f1857fb 100644 --- a/support/procedural-fork/src/runtime/parse/pallet.rs +++ b/support/procedural-fork/src/runtime/parse/pallet.rs @@ -20,85 +20,80 @@ use quote::ToTokens; use syn::{punctuated::Punctuated, spanned::Spanned, token, Error, Ident, PathArguments}; impl Pallet { - pub fn try_from( - attr_span: proc_macro2::Span, - item: &syn::ItemType, - pallet_index: u8, - disable_call: bool, - disable_unsigned: bool, - bounds: &Punctuated, - ) -> syn::Result { - let name = item.ident.clone(); + pub fn try_from( + attr_span: proc_macro2::Span, + item: &syn::ItemType, + pallet_index: u8, + disable_call: bool, + disable_unsigned: bool, + bounds: &Punctuated, + ) -> syn::Result { + let name = item.ident.clone(); - let mut pallet_path = None; - let mut pallet_parts = vec![]; + let mut pallet_path = None; + let mut pallet_parts = vec![]; - for (index, bound) in bounds.into_iter().enumerate() { - if let syn::TypeParamBound::Trait(syn::TraitBound { path, .. }) = bound { - if index == 0 { - pallet_path = Some(PalletPath { - inner: path.clone(), - }); - } else { - let pallet_part = syn::parse2::(bound.into_token_stream())?; - pallet_parts.push(pallet_part); - } - } else { - return Err(Error::new( - attr_span, - "Invalid pallet declaration, expected a path or a trait object", - )); - }; - } + for (index, bound) in bounds.into_iter().enumerate() { + if let syn::TypeParamBound::Trait(syn::TraitBound { path, .. }) = bound { + if index == 0 { + pallet_path = Some(PalletPath { inner: path.clone() }); + } else { + let pallet_part = syn::parse2::(bound.into_token_stream())?; + pallet_parts.push(pallet_part); + } + } else { + return Err(Error::new( + attr_span, + "Invalid pallet declaration, expected a path or a trait object", + )) + }; + } - let mut path = pallet_path.ok_or(Error::new( - attr_span, - "Invalid pallet declaration, expected a path or a trait object", - ))?; + let mut path = pallet_path.ok_or(Error::new( + attr_span, + "Invalid pallet declaration, expected a path or a trait object", + ))?; - let mut instance = None; - if let Some(segment) = path - .inner - .segments - .iter_mut() - .find(|seg| !seg.arguments.is_empty()) - { - if let PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { - args, .. - }) = segment.arguments.clone() - { - if let Some(syn::GenericArgument::Type(syn::Type::Path(arg_path))) = args.first() { - instance = Some(Ident::new( - &arg_path.to_token_stream().to_string(), - arg_path.span(), - )); - segment.arguments = PathArguments::None; - } - } - } + let mut instance = None; + if let Some(segment) = path.inner.segments.iter_mut().find(|seg| !seg.arguments.is_empty()) + { + if let PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { + args, .. + }) = segment.arguments.clone() + { + if let Some(syn::GenericArgument::Type(syn::Type::Path(arg_path))) = args.first() { + instance = + Some(Ident::new(&arg_path.to_token_stream().to_string(), arg_path.span())); + segment.arguments = PathArguments::None; + } + } + } - pallet_parts.retain(|part| { - if let (true, &PalletPartKeyword::Call(_)) = (disable_call, &part.keyword) { - false - } else if let (true, &PalletPartKeyword::ValidateUnsigned(_)) = - (disable_unsigned, &part.keyword) - { - false - } else { - true - } - }); + pallet_parts = pallet_parts + .into_iter() + .filter(|part| { + if let (true, &PalletPartKeyword::Call(_)) = (disable_call, &part.keyword) { + false + } else if let (true, &PalletPartKeyword::ValidateUnsigned(_)) = + (disable_unsigned, &part.keyword) + { + false + } else { + true + } + }) + .collect(); - let cfg_pattern = vec![]; + let cfg_pattern = vec![]; - Ok(Pallet { - is_expanded: true, - name, - index: pallet_index, - path, - instance, - cfg_pattern, - pallet_parts, - }) - } + Ok(Pallet { + is_expanded: true, + name, + index: pallet_index, + path, + instance, + cfg_pattern, + pallet_parts, + }) + } } diff --git a/support/procedural-fork/src/runtime/parse/pallet_decl.rs b/support/procedural-fork/src/runtime/parse/pallet_decl.rs index bb1246606..437a163cf 100644 --- a/support/procedural-fork/src/runtime/parse/pallet_decl.rs +++ b/support/procedural-fork/src/runtime/parse/pallet_decl.rs @@ -21,51 +21,40 @@ use syn::{spanned::Spanned, Attribute, Ident, PathArguments}; /// The declaration of a pallet. #[derive(Debug, Clone)] pub struct PalletDeclaration { - /// The name of the pallet, e.g.`System` in `System: frame_system`. - pub name: Ident, - /// Optional attributes tagged right above a pallet declaration. - pub attrs: Vec, - /// The path of the pallet, e.g. `frame_system` in `System: frame_system`. - pub path: syn::Path, - /// The instance of the pallet, e.g. `Instance1` in `Council: pallet_collective::`. - pub instance: Option, + /// The name of the pallet, e.g.`System` in `System: frame_system`. + pub name: Ident, + /// Optional attributes tagged right above a pallet declaration. + pub attrs: Vec, + /// The path of the pallet, e.g. `frame_system` in `System: frame_system`. + pub path: syn::Path, + /// The instance of the pallet, e.g. `Instance1` in `Council: pallet_collective::`. + pub instance: Option, } impl PalletDeclaration { - pub fn try_from( - _attr_span: proc_macro2::Span, - item: &syn::ItemType, - path: &syn::TypePath, - ) -> syn::Result { - let name = item.ident.clone(); - - let mut path = path.path.clone(); - - let mut instance = None; - if let Some(segment) = path - .segments - .iter_mut() - .find(|seg| !seg.arguments.is_empty()) - { - if let PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { - args, .. - }) = segment.arguments.clone() - { - if let Some(syn::GenericArgument::Type(syn::Type::Path(arg_path))) = args.first() { - instance = Some(Ident::new( - &arg_path.to_token_stream().to_string(), - arg_path.span(), - )); - segment.arguments = PathArguments::None; - } - } - } - - Ok(Self { - name, - path, - instance, - attrs: item.attrs.clone(), - }) - } + pub fn try_from( + _attr_span: proc_macro2::Span, + item: &syn::ItemType, + path: &syn::TypePath, + ) -> syn::Result { + let name = item.ident.clone(); + + let mut path = path.path.clone(); + + let mut instance = None; + if let Some(segment) = path.segments.iter_mut().find(|seg| !seg.arguments.is_empty()) { + if let PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { + args, .. + }) = segment.arguments.clone() + { + if let Some(syn::GenericArgument::Type(syn::Type::Path(arg_path))) = args.first() { + instance = + Some(Ident::new(&arg_path.to_token_stream().to_string(), arg_path.span())); + segment.arguments = PathArguments::None; + } + } + } + + Ok(Self { name, path, instance, attrs: item.attrs.clone() }) + } } diff --git a/support/procedural-fork/src/runtime/parse/runtime_struct.rs b/support/procedural-fork/src/runtime/parse/runtime_struct.rs index 7ddbdcfeb..8fa746ee8 100644 --- a/support/procedural-fork/src/runtime/parse/runtime_struct.rs +++ b/support/procedural-fork/src/runtime/parse/runtime_struct.rs @@ -17,22 +17,19 @@ use syn::spanned::Spanned; pub struct RuntimeStructDef { - pub ident: syn::Ident, - pub attr_span: proc_macro2::Span, + pub ident: syn::Ident, + pub attr_span: proc_macro2::Span, } impl RuntimeStructDef { - pub fn try_from(attr_span: proc_macro2::Span, item: &mut syn::Item) -> syn::Result { - let item = if let syn::Item::Struct(item) = item { - item - } else { - let msg = "Invalid runtime::runtime, expected struct definition"; - return Err(syn::Error::new(item.span(), msg)); - }; + pub fn try_from(attr_span: proc_macro2::Span, item: &mut syn::Item) -> syn::Result { + let item = if let syn::Item::Struct(item) = item { + item + } else { + let msg = "Invalid runtime::runtime, expected struct definition"; + return Err(syn::Error::new(item.span(), msg)) + }; - Ok(Self { - ident: item.ident.clone(), - attr_span, - }) - } + Ok(Self { ident: item.ident.clone(), attr_span }) + } } diff --git a/support/procedural-fork/src/runtime/parse/runtime_types.rs b/support/procedural-fork/src/runtime/parse/runtime_types.rs index 4d8c8358c..a4480e2a1 100644 --- a/support/procedural-fork/src/runtime/parse/runtime_types.rs +++ b/support/procedural-fork/src/runtime/parse/runtime_types.rs @@ -16,61 +16,61 @@ // limitations under the License. use syn::{ - parse::{Parse, ParseStream}, - Result, + parse::{Parse, ParseStream}, + Result, }; mod keyword { - use syn::custom_keyword; + use syn::custom_keyword; - custom_keyword!(RuntimeCall); - custom_keyword!(RuntimeEvent); - custom_keyword!(RuntimeError); - custom_keyword!(RuntimeOrigin); - custom_keyword!(RuntimeFreezeReason); - custom_keyword!(RuntimeHoldReason); - custom_keyword!(RuntimeSlashReason); - custom_keyword!(RuntimeLockId); - custom_keyword!(RuntimeTask); + custom_keyword!(RuntimeCall); + custom_keyword!(RuntimeEvent); + custom_keyword!(RuntimeError); + custom_keyword!(RuntimeOrigin); + custom_keyword!(RuntimeFreezeReason); + custom_keyword!(RuntimeHoldReason); + custom_keyword!(RuntimeSlashReason); + custom_keyword!(RuntimeLockId); + custom_keyword!(RuntimeTask); } #[derive(Debug, Clone, PartialEq)] pub enum RuntimeType { - RuntimeCall(keyword::RuntimeCall), - RuntimeEvent(keyword::RuntimeEvent), - RuntimeError(keyword::RuntimeError), - RuntimeOrigin(keyword::RuntimeOrigin), - RuntimeFreezeReason(keyword::RuntimeFreezeReason), - RuntimeHoldReason(keyword::RuntimeHoldReason), - RuntimeSlashReason(keyword::RuntimeSlashReason), - RuntimeLockId(keyword::RuntimeLockId), - RuntimeTask(keyword::RuntimeTask), + RuntimeCall(keyword::RuntimeCall), + RuntimeEvent(keyword::RuntimeEvent), + RuntimeError(keyword::RuntimeError), + RuntimeOrigin(keyword::RuntimeOrigin), + RuntimeFreezeReason(keyword::RuntimeFreezeReason), + RuntimeHoldReason(keyword::RuntimeHoldReason), + RuntimeSlashReason(keyword::RuntimeSlashReason), + RuntimeLockId(keyword::RuntimeLockId), + RuntimeTask(keyword::RuntimeTask), } impl Parse for RuntimeType { - fn parse(input: ParseStream) -> Result { - let lookahead = input.lookahead1(); + fn parse(input: ParseStream) -> Result { + let lookahead = input.lookahead1(); - if lookahead.peek(keyword::RuntimeCall) { - Ok(Self::RuntimeCall(input.parse()?)) - } else if lookahead.peek(keyword::RuntimeEvent) { - Ok(Self::RuntimeEvent(input.parse()?)) - } else if lookahead.peek(keyword::RuntimeError) { - Ok(Self::RuntimeError(input.parse()?)) - } else if lookahead.peek(keyword::RuntimeOrigin) { - Ok(Self::RuntimeOrigin(input.parse()?)) - } else if lookahead.peek(keyword::RuntimeFreezeReason) { - Ok(Self::RuntimeFreezeReason(input.parse()?)) - } else if lookahead.peek(keyword::RuntimeHoldReason) { - Ok(Self::RuntimeHoldReason(input.parse()?)) - } else if lookahead.peek(keyword::RuntimeSlashReason) { - Ok(Self::RuntimeSlashReason(input.parse()?)) - } else if lookahead.peek(keyword::RuntimeLockId) { - Ok(Self::RuntimeLockId(input.parse()?)) - } else if lookahead.peek(keyword::RuntimeTask) { - Ok(Self::RuntimeTask(input.parse()?)) - } else { - Err(lookahead.error()) - } - } + if lookahead.peek(keyword::RuntimeCall) { + Ok(Self::RuntimeCall(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeEvent) { + Ok(Self::RuntimeEvent(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeError) { + Ok(Self::RuntimeError(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeOrigin) { + Ok(Self::RuntimeOrigin(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeFreezeReason) { + Ok(Self::RuntimeFreezeReason(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeHoldReason) { + Ok(Self::RuntimeHoldReason(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeSlashReason) { + Ok(Self::RuntimeSlashReason(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeLockId) { + Ok(Self::RuntimeLockId(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeTask) { + Ok(Self::RuntimeTask(input.parse()?)) + } else { + Err(lookahead.error()) + } + } } diff --git a/support/procedural-fork/src/storage_alias.rs b/support/procedural-fork/src/storage_alias.rs index 7099239f9..06f62768f 100644 --- a/support/procedural-fork/src/storage_alias.rs +++ b/support/procedural-fork/src/storage_alias.rs @@ -22,688 +22,655 @@ use frame_support_procedural_tools::generate_access_from_frame_or_crate; use proc_macro2::{Span, TokenStream}; use quote::{quote, ToTokens}; use syn::{ - parenthesized, - parse::{Parse, ParseStream}, - punctuated::Punctuated, - spanned::Spanned, - token, - visit::Visit, - Attribute, Error, Ident, Result, Token, Type, TypeParam, Visibility, WhereClause, + parenthesized, + parse::{Parse, ParseStream}, + punctuated::Punctuated, + spanned::Spanned, + token, + visit::Visit, + Attribute, Error, Ident, Result, Token, Type, TypeParam, Visibility, WhereClause, }; /// Extension trait for [`Type`]. trait TypeExt { - fn get_ident(&self) -> Option<&Ident>; - fn contains_ident(&self, ident: &Ident) -> bool; + fn get_ident(&self) -> Option<&Ident>; + fn contains_ident(&self, ident: &Ident) -> bool; } impl TypeExt for Type { - fn get_ident(&self) -> Option<&Ident> { - match self { - Type::Path(p) => match &p.qself { - Some(qself) => qself.ty.get_ident(), - None => p.path.get_ident(), - }, - _ => None, - } - } - - fn contains_ident(&self, ident: &Ident) -> bool { - struct ContainsIdent<'a> { - ident: &'a Ident, - found: bool, - } - impl<'a, 'ast> Visit<'ast> for ContainsIdent<'a> { - fn visit_ident(&mut self, i: &'ast Ident) { - if i == self.ident { - self.found = true; - } - } - } - - let mut visitor = ContainsIdent { - ident, - found: false, - }; - syn::visit::visit_type(&mut visitor, self); - visitor.found - } + fn get_ident(&self) -> Option<&Ident> { + match self { + Type::Path(p) => match &p.qself { + Some(qself) => qself.ty.get_ident(), + None => p.path.get_ident(), + }, + _ => None, + } + } + + fn contains_ident(&self, ident: &Ident) -> bool { + struct ContainsIdent<'a> { + ident: &'a Ident, + found: bool, + } + impl<'a, 'ast> Visit<'ast> for ContainsIdent<'a> { + fn visit_ident(&mut self, i: &'ast Ident) { + if i == self.ident { + self.found = true; + } + } + } + + let mut visitor = ContainsIdent { ident, found: false }; + syn::visit::visit_type(&mut visitor, self); + visitor.found + } } /// Represents generics which only support [`TypeParam`] separated by commas. struct SimpleGenerics { - lt_token: Token![<], - params: Punctuated, - gt_token: Token![>], + lt_token: Token![<], + params: Punctuated, + gt_token: Token![>], } impl SimpleGenerics { - /// Returns the generics for types declarations etc. - fn type_generics(&self) -> impl Iterator { - self.params.iter().map(|p| &p.ident) - } - - /// Returns the generics for the `impl` block. - fn impl_generics(&self) -> impl Iterator { - self.params.iter() - } + /// Returns the generics for types declarations etc. + fn type_generics(&self) -> impl Iterator { + self.params.iter().map(|p| &p.ident) + } + + /// Returns the generics for the `impl` block. + fn impl_generics(&self) -> impl Iterator { + self.params.iter() + } } impl Parse for SimpleGenerics { - fn parse(input: ParseStream<'_>) -> Result { - Ok(Self { - lt_token: input.parse()?, - params: Punctuated::parse_separated_nonempty(input)?, - gt_token: input.parse()?, - }) - } + fn parse(input: ParseStream<'_>) -> Result { + Ok(Self { + lt_token: input.parse()?, + params: Punctuated::parse_separated_nonempty(input)?, + gt_token: input.parse()?, + }) + } } impl ToTokens for SimpleGenerics { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.lt_token.to_tokens(tokens); - self.params.to_tokens(tokens); - self.gt_token.to_tokens(tokens); - } + fn to_tokens(&self, tokens: &mut TokenStream) { + self.lt_token.to_tokens(tokens); + self.params.to_tokens(tokens); + self.gt_token.to_tokens(tokens); + } } mod storage_types { - syn::custom_keyword!(StorageValue); - syn::custom_keyword!(StorageMap); - syn::custom_keyword!(CountedStorageMap); - syn::custom_keyword!(StorageDoubleMap); - syn::custom_keyword!(StorageNMap); + syn::custom_keyword!(StorageValue); + syn::custom_keyword!(StorageMap); + syn::custom_keyword!(CountedStorageMap); + syn::custom_keyword!(StorageDoubleMap); + syn::custom_keyword!(StorageNMap); } /// The types of prefixes the storage alias macro supports. mod prefix_types { - // Use the verbatim/unmodified input name as the prefix. - syn::custom_keyword!(verbatim); - // The input type is a pallet and its pallet name should be used as the prefix. - syn::custom_keyword!(pallet_name); - // The input type implements `Get<'static str>` and this `str` should be used as the prefix. - syn::custom_keyword!(dynamic); + // Use the verbatim/unmodified input name as the prefix. + syn::custom_keyword!(verbatim); + // The input type is a pallet and its pallet name should be used as the prefix. + syn::custom_keyword!(pallet_name); + // The input type implements `Get<'static str>` and this `str` should be used as the prefix. + syn::custom_keyword!(dynamic); } /// The supported storage types enum StorageType { - Value { - _kw: storage_types::StorageValue, - _lt_token: Token![<], - prefix: Type, - _value_comma: Token![,], - value_ty: Type, - query_type: Option<(Token![,], Type)>, - _trailing_comma: Option, - _gt_token: Token![>], - }, - Map { - _kw: storage_types::StorageMap, - _lt_token: Token![<], - prefix: Type, - _hasher_comma: Token![,], - hasher_ty: Type, - _key_comma: Token![,], - key_ty: Type, - _value_comma: Token![,], - value_ty: Type, - query_type: Option<(Token![,], Type)>, - _trailing_comma: Option, - _gt_token: Token![>], - }, - CountedMap { - _kw: storage_types::CountedStorageMap, - _lt_token: Token![<], - prefix: Type, - _hasher_comma: Token![,], - hasher_ty: Type, - _key_comma: Token![,], - key_ty: Type, - _value_comma: Token![,], - value_ty: Type, - query_type: Option<(Token![,], Type)>, - _trailing_comma: Option, - _gt_token: Token![>], - }, - DoubleMap { - _kw: storage_types::StorageDoubleMap, - _lt_token: Token![<], - prefix: Type, - _hasher1_comma: Token![,], - hasher1_ty: Type, - _key1_comma: Token![,], - key1_ty: Type, - _hasher2_comma: Token![,], - hasher2_ty: Type, - _key2_comma: Token![,], - key2_ty: Type, - _value_comma: Token![,], - value_ty: Type, - query_type: Option<(Token![,], Type)>, - _trailing_comma: Option, - _gt_token: Token![>], - }, - NMap { - _kw: storage_types::StorageNMap, - _lt_token: Token![<], - prefix: Type, - _paren_comma: Token![,], - _paren_token: token::Paren, - key_types: Punctuated, - _value_comma: Token![,], - value_ty: Type, - query_type: Option<(Token![,], Type)>, - _trailing_comma: Option, - _gt_token: Token![>], - }, + Value { + _kw: storage_types::StorageValue, + _lt_token: Token![<], + prefix: Type, + _value_comma: Token![,], + value_ty: Type, + query_type: Option<(Token![,], Type)>, + _trailing_comma: Option, + _gt_token: Token![>], + }, + Map { + _kw: storage_types::StorageMap, + _lt_token: Token![<], + prefix: Type, + _hasher_comma: Token![,], + hasher_ty: Type, + _key_comma: Token![,], + key_ty: Type, + _value_comma: Token![,], + value_ty: Type, + query_type: Option<(Token![,], Type)>, + _trailing_comma: Option, + _gt_token: Token![>], + }, + CountedMap { + _kw: storage_types::CountedStorageMap, + _lt_token: Token![<], + prefix: Type, + _hasher_comma: Token![,], + hasher_ty: Type, + _key_comma: Token![,], + key_ty: Type, + _value_comma: Token![,], + value_ty: Type, + query_type: Option<(Token![,], Type)>, + _trailing_comma: Option, + _gt_token: Token![>], + }, + DoubleMap { + _kw: storage_types::StorageDoubleMap, + _lt_token: Token![<], + prefix: Type, + _hasher1_comma: Token![,], + hasher1_ty: Type, + _key1_comma: Token![,], + key1_ty: Type, + _hasher2_comma: Token![,], + hasher2_ty: Type, + _key2_comma: Token![,], + key2_ty: Type, + _value_comma: Token![,], + value_ty: Type, + query_type: Option<(Token![,], Type)>, + _trailing_comma: Option, + _gt_token: Token![>], + }, + NMap { + _kw: storage_types::StorageNMap, + _lt_token: Token![<], + prefix: Type, + _paren_comma: Token![,], + _paren_token: token::Paren, + key_types: Punctuated, + _value_comma: Token![,], + value_ty: Type, + query_type: Option<(Token![,], Type)>, + _trailing_comma: Option, + _gt_token: Token![>], + }, } impl StorageType { - /// Generate the actual type declaration. - fn generate_type_declaration( - &self, - crate_: &syn::Path, - storage_instance: &StorageInstance, - storage_name: &Ident, - storage_generics: Option<&SimpleGenerics>, - visibility: &Visibility, - attributes: &[Attribute], - ) -> TokenStream { - let storage_instance_generics = &storage_instance.generics; - let storage_instance = &storage_instance.name; - let attributes = attributes.iter(); - let storage_generics = storage_generics.map(|g| { - let generics = g.type_generics(); - - quote!( < #( #generics ),* > ) - }); - - match self { - Self::Value { - value_ty, - query_type, - .. - } => { - let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); - - quote! { - #( #attributes )* - #visibility type #storage_name #storage_generics = #crate_::storage::types::StorageValue< - #storage_instance #storage_instance_generics, - #value_ty - #query_type - >; - } - } - Self::CountedMap { - value_ty, - query_type, - hasher_ty, - key_ty, - .. - } - | Self::Map { - value_ty, - query_type, - hasher_ty, - key_ty, - .. - } => { - let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); - let map_type = Ident::new( - match self { - Self::Map { .. } => "StorageMap", - _ => "CountedStorageMap", - }, - Span::call_site(), - ); - - quote! { - #( #attributes )* - #visibility type #storage_name #storage_generics = #crate_::storage::types::#map_type< - #storage_instance #storage_instance_generics, - #hasher_ty, - #key_ty, - #value_ty - #query_type - >; - } - } - Self::DoubleMap { - value_ty, - query_type, - hasher1_ty, - key1_ty, - hasher2_ty, - key2_ty, - .. - } => { - let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); - - quote! { - #( #attributes )* - #visibility type #storage_name #storage_generics = #crate_::storage::types::StorageDoubleMap< - #storage_instance #storage_instance_generics, - #hasher1_ty, - #key1_ty, - #hasher2_ty, - #key2_ty, - #value_ty - #query_type - >; - } - } - Self::NMap { - value_ty, - query_type, - key_types, - .. - } => { - let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); - let key_types = key_types.iter(); - - quote! { - #( #attributes )* - #visibility type #storage_name #storage_generics = #crate_::storage::types::StorageNMap< - #storage_instance #storage_instance_generics, - ( #( #key_types ),* ), - #value_ty - #query_type - >; - } - } - } - } - - /// The prefix for this storage type. - fn prefix(&self) -> &Type { - match self { - Self::Value { prefix, .. } - | Self::Map { prefix, .. } - | Self::CountedMap { prefix, .. } - | Self::NMap { prefix, .. } - | Self::DoubleMap { prefix, .. } => prefix, - } - } + /// Generate the actual type declaration. + fn generate_type_declaration( + &self, + crate_: &syn::Path, + storage_instance: &StorageInstance, + storage_name: &Ident, + storage_generics: Option<&SimpleGenerics>, + visibility: &Visibility, + attributes: &[Attribute], + ) -> TokenStream { + let storage_instance_generics = &storage_instance.generics; + let storage_instance = &storage_instance.name; + let attributes = attributes.iter(); + let storage_generics = storage_generics.map(|g| { + let generics = g.type_generics(); + + quote!( < #( #generics ),* > ) + }); + + match self { + Self::Value { value_ty, query_type, .. } => { + let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); + + quote! { + #( #attributes )* + #visibility type #storage_name #storage_generics = #crate_::storage::types::StorageValue< + #storage_instance #storage_instance_generics, + #value_ty + #query_type + >; + } + }, + Self::CountedMap { value_ty, query_type, hasher_ty, key_ty, .. } | + Self::Map { value_ty, query_type, hasher_ty, key_ty, .. } => { + let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); + let map_type = Ident::new( + match self { + Self::Map { .. } => "StorageMap", + _ => "CountedStorageMap", + }, + Span::call_site(), + ); + + quote! { + #( #attributes )* + #visibility type #storage_name #storage_generics = #crate_::storage::types::#map_type< + #storage_instance #storage_instance_generics, + #hasher_ty, + #key_ty, + #value_ty + #query_type + >; + } + }, + Self::DoubleMap { + value_ty, + query_type, + hasher1_ty, + key1_ty, + hasher2_ty, + key2_ty, + .. + } => { + let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); + + quote! { + #( #attributes )* + #visibility type #storage_name #storage_generics = #crate_::storage::types::StorageDoubleMap< + #storage_instance #storage_instance_generics, + #hasher1_ty, + #key1_ty, + #hasher2_ty, + #key2_ty, + #value_ty + #query_type + >; + } + }, + Self::NMap { value_ty, query_type, key_types, .. } => { + let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); + let key_types = key_types.iter(); + + quote! { + #( #attributes )* + #visibility type #storage_name #storage_generics = #crate_::storage::types::StorageNMap< + #storage_instance #storage_instance_generics, + ( #( #key_types ),* ), + #value_ty + #query_type + >; + } + }, + } + } + + /// The prefix for this storage type. + fn prefix(&self) -> &Type { + match self { + Self::Value { prefix, .. } | + Self::Map { prefix, .. } | + Self::CountedMap { prefix, .. } | + Self::NMap { prefix, .. } | + Self::DoubleMap { prefix, .. } => prefix, + } + } } impl Parse for StorageType { - fn parse(input: ParseStream<'_>) -> Result { - let lookahead = input.lookahead1(); - - let parse_query_type = |input: ParseStream<'_>| -> Result> { - if input.peek(Token![,]) && !input.peek2(Token![>]) { - Ok(Some((input.parse()?, input.parse()?))) - } else { - Ok(None) - } - }; - - if lookahead.peek(storage_types::StorageValue) { - Ok(Self::Value { - _kw: input.parse()?, - _lt_token: input.parse()?, - prefix: input.parse()?, - _value_comma: input.parse()?, - value_ty: input.parse()?, - query_type: parse_query_type(input)?, - _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, - _gt_token: input.parse()?, - }) - } else if lookahead.peek(storage_types::StorageMap) { - Ok(Self::Map { - _kw: input.parse()?, - _lt_token: input.parse()?, - prefix: input.parse()?, - _hasher_comma: input.parse()?, - hasher_ty: input.parse()?, - _key_comma: input.parse()?, - key_ty: input.parse()?, - _value_comma: input.parse()?, - value_ty: input.parse()?, - query_type: parse_query_type(input)?, - _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, - _gt_token: input.parse()?, - }) - } else if lookahead.peek(storage_types::CountedStorageMap) { - Ok(Self::CountedMap { - _kw: input.parse()?, - _lt_token: input.parse()?, - prefix: input.parse()?, - _hasher_comma: input.parse()?, - hasher_ty: input.parse()?, - _key_comma: input.parse()?, - key_ty: input.parse()?, - _value_comma: input.parse()?, - value_ty: input.parse()?, - query_type: parse_query_type(input)?, - _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, - _gt_token: input.parse()?, - }) - } else if lookahead.peek(storage_types::StorageDoubleMap) { - Ok(Self::DoubleMap { - _kw: input.parse()?, - _lt_token: input.parse()?, - prefix: input.parse()?, - _hasher1_comma: input.parse()?, - hasher1_ty: input.parse()?, - _key1_comma: input.parse()?, - key1_ty: input.parse()?, - _hasher2_comma: input.parse()?, - hasher2_ty: input.parse()?, - _key2_comma: input.parse()?, - key2_ty: input.parse()?, - _value_comma: input.parse()?, - value_ty: input.parse()?, - query_type: parse_query_type(input)?, - _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, - _gt_token: input.parse()?, - }) - } else if lookahead.peek(storage_types::StorageNMap) { - let content; - Ok(Self::NMap { - _kw: input.parse()?, - _lt_token: input.parse()?, - prefix: input.parse()?, - _paren_comma: input.parse()?, - _paren_token: parenthesized!(content in input), - key_types: Punctuated::parse_terminated(&content)?, - _value_comma: input.parse()?, - value_ty: input.parse()?, - query_type: parse_query_type(input)?, - _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, - _gt_token: input.parse()?, - }) - } else { - Err(lookahead.error()) - } - } + fn parse(input: ParseStream<'_>) -> Result { + let lookahead = input.lookahead1(); + + let parse_query_type = |input: ParseStream<'_>| -> Result> { + if input.peek(Token![,]) && !input.peek2(Token![>]) { + Ok(Some((input.parse()?, input.parse()?))) + } else { + Ok(None) + } + }; + + if lookahead.peek(storage_types::StorageValue) { + Ok(Self::Value { + _kw: input.parse()?, + _lt_token: input.parse()?, + prefix: input.parse()?, + _value_comma: input.parse()?, + value_ty: input.parse()?, + query_type: parse_query_type(input)?, + _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, + _gt_token: input.parse()?, + }) + } else if lookahead.peek(storage_types::StorageMap) { + Ok(Self::Map { + _kw: input.parse()?, + _lt_token: input.parse()?, + prefix: input.parse()?, + _hasher_comma: input.parse()?, + hasher_ty: input.parse()?, + _key_comma: input.parse()?, + key_ty: input.parse()?, + _value_comma: input.parse()?, + value_ty: input.parse()?, + query_type: parse_query_type(input)?, + _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, + _gt_token: input.parse()?, + }) + } else if lookahead.peek(storage_types::CountedStorageMap) { + Ok(Self::CountedMap { + _kw: input.parse()?, + _lt_token: input.parse()?, + prefix: input.parse()?, + _hasher_comma: input.parse()?, + hasher_ty: input.parse()?, + _key_comma: input.parse()?, + key_ty: input.parse()?, + _value_comma: input.parse()?, + value_ty: input.parse()?, + query_type: parse_query_type(input)?, + _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, + _gt_token: input.parse()?, + }) + } else if lookahead.peek(storage_types::StorageDoubleMap) { + Ok(Self::DoubleMap { + _kw: input.parse()?, + _lt_token: input.parse()?, + prefix: input.parse()?, + _hasher1_comma: input.parse()?, + hasher1_ty: input.parse()?, + _key1_comma: input.parse()?, + key1_ty: input.parse()?, + _hasher2_comma: input.parse()?, + hasher2_ty: input.parse()?, + _key2_comma: input.parse()?, + key2_ty: input.parse()?, + _value_comma: input.parse()?, + value_ty: input.parse()?, + query_type: parse_query_type(input)?, + _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, + _gt_token: input.parse()?, + }) + } else if lookahead.peek(storage_types::StorageNMap) { + let content; + Ok(Self::NMap { + _kw: input.parse()?, + _lt_token: input.parse()?, + prefix: input.parse()?, + _paren_comma: input.parse()?, + _paren_token: parenthesized!(content in input), + key_types: Punctuated::parse_terminated(&content)?, + _value_comma: input.parse()?, + value_ty: input.parse()?, + query_type: parse_query_type(input)?, + _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, + _gt_token: input.parse()?, + }) + } else { + Err(lookahead.error()) + } + } } /// The input expected by this macro. struct Input { - attributes: Vec, - visibility: Visibility, - _type: Token![type], - storage_name: Ident, - storage_generics: Option, - where_clause: Option, - _equal: Token![=], - storage_type: StorageType, - _semicolon: Token![;], + attributes: Vec, + visibility: Visibility, + _type: Token![type], + storage_name: Ident, + storage_generics: Option, + where_clause: Option, + _equal: Token![=], + storage_type: StorageType, + _semicolon: Token![;], } impl Parse for Input { - fn parse(input: ParseStream<'_>) -> Result { - let attributes = input.call(Attribute::parse_outer)?; - let visibility = input.parse()?; - let _type = input.parse()?; - let storage_name = input.parse()?; - - let lookahead = input.lookahead1(); - let storage_generics = if lookahead.peek(Token![<]) { - Some(input.parse()?) - } else if lookahead.peek(Token![=]) { - None - } else { - return Err(lookahead.error()); - }; - - let lookahead = input.lookahead1(); - let where_clause = if lookahead.peek(Token![where]) { - Some(input.parse()?) - } else if lookahead.peek(Token![=]) { - None - } else { - return Err(lookahead.error()); - }; - - let _equal = input.parse()?; - - let storage_type = input.parse()?; - - let _semicolon = input.parse()?; - - Ok(Self { - attributes, - visibility, - _type, - storage_name, - storage_generics, - _equal, - storage_type, - where_clause, - _semicolon, - }) - } + fn parse(input: ParseStream<'_>) -> Result { + let attributes = input.call(Attribute::parse_outer)?; + let visibility = input.parse()?; + let _type = input.parse()?; + let storage_name = input.parse()?; + + let lookahead = input.lookahead1(); + let storage_generics = if lookahead.peek(Token![<]) { + Some(input.parse()?) + } else if lookahead.peek(Token![=]) { + None + } else { + return Err(lookahead.error()) + }; + + let lookahead = input.lookahead1(); + let where_clause = if lookahead.peek(Token![where]) { + Some(input.parse()?) + } else if lookahead.peek(Token![=]) { + None + } else { + return Err(lookahead.error()) + }; + + let _equal = input.parse()?; + + let storage_type = input.parse()?; + + let _semicolon = input.parse()?; + + Ok(Self { + attributes, + visibility, + _type, + storage_name, + storage_generics, + _equal, + storage_type, + where_clause, + _semicolon, + }) + } } /// Defines which type of prefix the storage alias is using. #[derive(Clone, Copy)] enum PrefixType { - /// An appropriate prefix will be determined automatically. - /// - /// If generics are passed, this is assumed to be a pallet and the pallet name should be used. - /// Otherwise use the verbatim passed name as prefix. - Compatibility, - /// The provided ident/name will be used as the prefix. - Verbatim, - /// The provided type will be used to determine the prefix. This type must - /// implement `PalletInfoAccess` which specifies the proper name. This - /// name is then used as the prefix. - PalletName, - /// Uses the provided type implementing `Get<'static str>` to determine the prefix. - Dynamic, + /// An appropriate prefix will be determined automatically. + /// + /// If generics are passed, this is assumed to be a pallet and the pallet name should be used. + /// Otherwise use the verbatim passed name as prefix. + Compatibility, + /// The provided ident/name will be used as the prefix. + Verbatim, + /// The provided type will be used to determine the prefix. This type must + /// implement `PalletInfoAccess` which specifies the proper name. This + /// name is then used as the prefix. + PalletName, + /// Uses the provided type implementing `Get<'static str>` to determine the prefix. + Dynamic, } /// Implementation of the `storage_alias` attribute macro. pub fn storage_alias(attributes: TokenStream, input: TokenStream) -> Result { - let input = syn::parse2::(input)?; - let crate_ = generate_access_from_frame_or_crate("frame-support")?; - - let prefix_type = if attributes.is_empty() { - PrefixType::Compatibility - } else if syn::parse2::(attributes.clone()).is_ok() { - PrefixType::Verbatim - } else if syn::parse2::(attributes.clone()).is_ok() { - PrefixType::PalletName - } else if syn::parse2::(attributes.clone()).is_ok() { - PrefixType::Dynamic - } else { - return Err(Error::new(attributes.span(), "Unknown attributes")); - }; - - let storage_instance = generate_storage_instance( - &crate_, - &input.storage_name, - input.storage_generics.as_ref(), - input.where_clause.as_ref(), - input.storage_type.prefix(), - &input.visibility, - matches!(input.storage_type, StorageType::CountedMap { .. }), - prefix_type, - )?; - - let definition = input.storage_type.generate_type_declaration( - &crate_, - &storage_instance, - &input.storage_name, - input.storage_generics.as_ref(), - &input.visibility, - &input.attributes, - ); - - let storage_instance_code = storage_instance.code; - - Ok(quote! { - #storage_instance_code - - #definition - }) + let input = syn::parse2::(input)?; + let crate_ = generate_access_from_frame_or_crate("frame-support")?; + + let prefix_type = if attributes.is_empty() { + PrefixType::Compatibility + } else if syn::parse2::(attributes.clone()).is_ok() { + PrefixType::Verbatim + } else if syn::parse2::(attributes.clone()).is_ok() { + PrefixType::PalletName + } else if syn::parse2::(attributes.clone()).is_ok() { + PrefixType::Dynamic + } else { + return Err(Error::new(attributes.span(), "Unknown attributes")) + }; + + let storage_instance = generate_storage_instance( + &crate_, + &input.storage_name, + input.storage_generics.as_ref(), + input.where_clause.as_ref(), + input.storage_type.prefix(), + &input.visibility, + matches!(input.storage_type, StorageType::CountedMap { .. }), + prefix_type, + )?; + + let definition = input.storage_type.generate_type_declaration( + &crate_, + &storage_instance, + &input.storage_name, + input.storage_generics.as_ref(), + &input.visibility, + &input.attributes, + ); + + let storage_instance_code = storage_instance.code; + + Ok(quote! { + #storage_instance_code + + #definition + }) } /// The storage instance to use for the storage alias. struct StorageInstance { - name: Ident, - generics: TokenStream, - code: TokenStream, + name: Ident, + generics: TokenStream, + code: TokenStream, } /// Generate the [`StorageInstance`] for the storage alias. fn generate_storage_instance( - crate_: &syn::Path, - storage_name: &Ident, - storage_generics: Option<&SimpleGenerics>, - storage_where_clause: Option<&WhereClause>, - prefix: &Type, - visibility: &Visibility, - is_counted_map: bool, - prefix_type: PrefixType, + crate_: &syn::Path, + storage_name: &Ident, + storage_generics: Option<&SimpleGenerics>, + storage_where_clause: Option<&WhereClause>, + prefix: &Type, + visibility: &Visibility, + is_counted_map: bool, + prefix_type: PrefixType, ) -> Result { - if let Type::Infer(_) = prefix { - return Err(Error::new( - prefix.span(), - "`_` is not allowed as prefix by `storage_alias`.", - )); - } - - let impl_generics_used_by_prefix = storage_generics - .as_ref() - .map(|g| { - g.impl_generics() - .filter(|g| prefix.contains_ident(&g.ident)) - .collect::>() - }) - .unwrap_or_default(); - - let (pallet_prefix, impl_generics, type_generics) = match prefix_type { - PrefixType::Compatibility => { - if !impl_generics_used_by_prefix.is_empty() { - let type_generics = impl_generics_used_by_prefix.iter().map(|g| &g.ident); - let impl_generics = impl_generics_used_by_prefix.iter(); - - ( - quote! { - < #prefix as #crate_::traits::PalletInfoAccess>::name() - }, - quote!( #( #impl_generics ),* ), - quote!( #( #type_generics ),* ), - ) - } else if let Some(prefix) = prefix.get_ident() { - let prefix_str = prefix.to_string(); - - (quote!(#prefix_str), quote!(), quote!()) - } else { - return Err(Error::new_spanned( - prefix, - "If there are no generics, the prefix is only allowed to be an identifier.", - )); - } - } - PrefixType::Verbatim => { - let prefix_str = match prefix.get_ident() { - Some(p) => p.to_string(), - None => { - return Err(Error::new_spanned( - prefix, - "Prefix type `verbatim` requires that the prefix is an ident.", - )) - } - }; - - (quote!(#prefix_str), quote!(), quote!()) - } - PrefixType::PalletName => { - let type_generics = impl_generics_used_by_prefix.iter().map(|g| &g.ident); - let impl_generics = impl_generics_used_by_prefix.iter(); - - ( - quote! { - <#prefix as #crate_::traits::PalletInfoAccess>::name() - }, - quote!( #( #impl_generics ),* ), - quote!( #( #type_generics ),* ), - ) - } - PrefixType::Dynamic => { - let type_generics = impl_generics_used_by_prefix.iter().map(|g| &g.ident); - let impl_generics = impl_generics_used_by_prefix.iter(); - - ( - quote! { - <#prefix as #crate_::traits::Get<_>>::get() - }, - quote!( #( #impl_generics ),* ), - quote!( #( #type_generics ),* ), - ) - } - }; - - let where_clause = storage_where_clause.map(|w| quote!(#w)).unwrap_or_default(); - - let name_str = format!("{}_Storage_Instance", storage_name); - let name = Ident::new(&name_str, Span::call_site()); - let storage_name_str = storage_name.to_string(); - - let counter_code = is_counted_map.then(|| { - let counter_name = Ident::new(&counter_prefix(&name_str), Span::call_site()); - let counter_storage_name_str = counter_prefix(&storage_name_str); - let storage_prefix_hash = helper::two128_str(&counter_storage_name_str); - - quote! { - #visibility struct #counter_name< #impl_generics >( - ::core::marker::PhantomData<(#type_generics)> - ) #where_clause; - - impl<#impl_generics> #crate_::traits::StorageInstance - for #counter_name< #type_generics > #where_clause - { - fn pallet_prefix() -> &'static str { - #pallet_prefix - } - - const STORAGE_PREFIX: &'static str = #counter_storage_name_str; - fn storage_prefix_hash() -> [u8; 16] { - #storage_prefix_hash - } - } - - impl<#impl_generics> #crate_::storage::types::CountedStorageMapInstance - for #name< #type_generics > #where_clause - { - type CounterPrefix = #counter_name < #type_generics >; - } - } - }); - - let storage_prefix_hash = helper::two128_str(&storage_name_str); - - // Implement `StorageInstance` trait. - let code = quote! { - #[allow(non_camel_case_types)] - #visibility struct #name< #impl_generics >( - ::core::marker::PhantomData<(#type_generics)> - ) #where_clause; - - impl<#impl_generics> #crate_::traits::StorageInstance - for #name< #type_generics > #where_clause - { - fn pallet_prefix() -> &'static str { - #pallet_prefix - } - - const STORAGE_PREFIX: &'static str = #storage_name_str; - fn storage_prefix_hash() -> [u8; 16] { - #storage_prefix_hash - } - } - - #counter_code - }; - - Ok(StorageInstance { - name, - code, - generics: quote!( < #type_generics > ), - }) + if let Type::Infer(_) = prefix { + return Err(Error::new(prefix.span(), "`_` is not allowed as prefix by `storage_alias`.")) + } + + let impl_generics_used_by_prefix = storage_generics + .as_ref() + .map(|g| { + g.impl_generics() + .filter(|g| prefix.contains_ident(&g.ident)) + .collect::>() + }) + .unwrap_or_default(); + + let (pallet_prefix, impl_generics, type_generics) = match prefix_type { + PrefixType::Compatibility => + if !impl_generics_used_by_prefix.is_empty() { + let type_generics = impl_generics_used_by_prefix.iter().map(|g| &g.ident); + let impl_generics = impl_generics_used_by_prefix.iter(); + + ( + quote! { + < #prefix as #crate_::traits::PalletInfoAccess>::name() + }, + quote!( #( #impl_generics ),* ), + quote!( #( #type_generics ),* ), + ) + } else if let Some(prefix) = prefix.get_ident() { + let prefix_str = prefix.to_string(); + + (quote!(#prefix_str), quote!(), quote!()) + } else { + return Err(Error::new_spanned( + prefix, + "If there are no generics, the prefix is only allowed to be an identifier.", + )) + }, + PrefixType::Verbatim => { + let prefix_str = match prefix.get_ident() { + Some(p) => p.to_string(), + None => + return Err(Error::new_spanned( + prefix, + "Prefix type `verbatim` requires that the prefix is an ident.", + )), + }; + + (quote!(#prefix_str), quote!(), quote!()) + }, + PrefixType::PalletName => { + let type_generics = impl_generics_used_by_prefix.iter().map(|g| &g.ident); + let impl_generics = impl_generics_used_by_prefix.iter(); + + ( + quote! { + <#prefix as #crate_::traits::PalletInfoAccess>::name() + }, + quote!( #( #impl_generics ),* ), + quote!( #( #type_generics ),* ), + ) + }, + PrefixType::Dynamic => { + let type_generics = impl_generics_used_by_prefix.iter().map(|g| &g.ident); + let impl_generics = impl_generics_used_by_prefix.iter(); + + ( + quote! { + <#prefix as #crate_::traits::Get<_>>::get() + }, + quote!( #( #impl_generics ),* ), + quote!( #( #type_generics ),* ), + ) + }, + }; + + let where_clause = storage_where_clause.map(|w| quote!(#w)).unwrap_or_default(); + + let name_str = format!("{}_Storage_Instance", storage_name); + let name = Ident::new(&name_str, Span::call_site()); + let storage_name_str = storage_name.to_string(); + + let counter_code = is_counted_map.then(|| { + let counter_name = Ident::new(&counter_prefix(&name_str), Span::call_site()); + let counter_storage_name_str = counter_prefix(&storage_name_str); + let storage_prefix_hash = helper::two128_str(&counter_storage_name_str); + + quote! { + #visibility struct #counter_name< #impl_generics >( + ::core::marker::PhantomData<(#type_generics)> + ) #where_clause; + + impl<#impl_generics> #crate_::traits::StorageInstance + for #counter_name< #type_generics > #where_clause + { + fn pallet_prefix() -> &'static str { + #pallet_prefix + } + + const STORAGE_PREFIX: &'static str = #counter_storage_name_str; + fn storage_prefix_hash() -> [u8; 16] { + #storage_prefix_hash + } + } + + impl<#impl_generics> #crate_::storage::types::CountedStorageMapInstance + for #name< #type_generics > #where_clause + { + type CounterPrefix = #counter_name < #type_generics >; + } + } + }); + + let storage_prefix_hash = helper::two128_str(&storage_name_str); + + // Implement `StorageInstance` trait. + let code = quote! { + #[allow(non_camel_case_types)] + #visibility struct #name< #impl_generics >( + ::core::marker::PhantomData<(#type_generics)> + ) #where_clause; + + impl<#impl_generics> #crate_::traits::StorageInstance + for #name< #type_generics > #where_clause + { + fn pallet_prefix() -> &'static str { + #pallet_prefix + } + + const STORAGE_PREFIX: &'static str = #storage_name_str; + fn storage_prefix_hash() -> [u8; 16] { + #storage_prefix_hash + } + } + + #counter_code + }; + + Ok(StorageInstance { name, code, generics: quote!( < #type_generics > ) }) } diff --git a/support/procedural-fork/src/transactional.rs b/support/procedural-fork/src/transactional.rs index 73a841d9b..e9d4f84b7 100644 --- a/support/procedural-fork/src/transactional.rs +++ b/support/procedural-fork/src/transactional.rs @@ -21,50 +21,40 @@ use quote::quote; use syn::{ItemFn, Result}; pub fn transactional(_attr: TokenStream, input: TokenStream) -> Result { - let ItemFn { - attrs, - vis, - sig, - block, - } = syn::parse(input)?; - - let crate_ = generate_access_from_frame_or_crate("frame-support")?; - let output = quote! { - #(#attrs)* - #vis #sig { - use #crate_::storage::{with_transaction, TransactionOutcome}; - with_transaction(|| { - let r = (|| { #block })(); - if r.is_ok() { - TransactionOutcome::Commit(r) - } else { - TransactionOutcome::Rollback(r) - } - }) - } - }; - - Ok(output.into()) + let ItemFn { attrs, vis, sig, block } = syn::parse(input)?; + + let crate_ = generate_access_from_frame_or_crate("frame-support")?; + let output = quote! { + #(#attrs)* + #vis #sig { + use #crate_::storage::{with_transaction, TransactionOutcome}; + with_transaction(|| { + let r = (|| { #block })(); + if r.is_ok() { + TransactionOutcome::Commit(r) + } else { + TransactionOutcome::Rollback(r) + } + }) + } + }; + + Ok(output.into()) } pub fn require_transactional(_attr: TokenStream, input: TokenStream) -> Result { - let ItemFn { - attrs, - vis, - sig, - block, - } = syn::parse(input)?; - - let crate_ = generate_access_from_frame_or_crate("frame-support")?; - let output = quote! { - #(#attrs)* - #vis #sig { - if !#crate_::storage::transactional::is_transactional() { - return Err(#crate_::sp_runtime::TransactionalError::NoLayer.into()); - } - #block - } - }; - - Ok(output.into()) + let ItemFn { attrs, vis, sig, block } = syn::parse(input)?; + + let crate_ = generate_access_from_frame_or_crate("frame-support")?; + let output = quote! { + #(#attrs)* + #vis #sig { + if !#crate_::storage::transactional::is_transactional() { + return Err(#crate_::sp_runtime::TransactionalError::NoLayer.into()); + } + #block + } + }; + + Ok(output.into()) } diff --git a/support/procedural-fork/src/tt_macro.rs b/support/procedural-fork/src/tt_macro.rs index 3f280013f..d37127421 100644 --- a/support/procedural-fork/src/tt_macro.rs +++ b/support/procedural-fork/src/tt_macro.rs @@ -22,29 +22,29 @@ use proc_macro2::{Ident, TokenStream}; use quote::format_ident; struct CreateTtReturnMacroDef { - name: Ident, - args: Vec<(Ident, TokenStream)>, + name: Ident, + args: Vec<(Ident, TokenStream)>, } impl syn::parse::Parse for CreateTtReturnMacroDef { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let name = input.parse()?; - let _ = input.parse::()?; + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let name = input.parse()?; + let _ = input.parse::()?; - let mut args = Vec::new(); - while !input.is_empty() { - let mut value; - let key: Ident = input.parse()?; - let _ = input.parse::()?; - let _: syn::token::Bracket = syn::bracketed!(value in input); - let _: syn::token::Brace = syn::braced!(value in value); - let value: TokenStream = value.parse()?; + let mut args = Vec::new(); + while !input.is_empty() { + let mut value; + let key: Ident = input.parse()?; + let _ = input.parse::()?; + let _: syn::token::Bracket = syn::bracketed!(value in input); + let _: syn::token::Brace = syn::braced!(value in value); + let value: TokenStream = value.parse()?; - args.push((key, value)) - } + args.push((key, value)) + } - Ok(Self { name, args }) - } + Ok(Self { name, args }) + } } /// A proc macro that accepts a name and any number of key-value pairs, to be used to create a @@ -74,32 +74,32 @@ impl syn::parse::Parse for CreateTtReturnMacroDef { /// } /// ``` pub fn create_tt_return_macro(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let CreateTtReturnMacroDef { name, args } = - syn::parse_macro_input!(input as CreateTtReturnMacroDef); + let CreateTtReturnMacroDef { name, args } = + syn::parse_macro_input!(input as CreateTtReturnMacroDef); - let (keys, values): (Vec<_>, Vec<_>) = args.into_iter().unzip(); - let count = COUNTER.with(|counter| counter.borrow_mut().inc()); - let unique_name = format_ident!("{}_{}", name, count); + let (keys, values): (Vec<_>, Vec<_>) = args.into_iter().unzip(); + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + let unique_name = format_ident!("{}_{}", name, count); - let decl_macro = quote::quote! { - #[macro_export] - #[doc(hidden)] - macro_rules! #unique_name { - { - $caller:tt - $(your_tt_return = [{ $my_tt_macro:path }])? - } => { - $my_tt_return! { - $caller - #( - #keys = [{ #values }] - )* - } - } - } + let decl_macro = quote::quote! { + #[macro_export] + #[doc(hidden)] + macro_rules! #unique_name { + { + $caller:tt + $(your_tt_return = [{ $my_tt_macro:path }])? + } => { + $my_tt_return! { + $caller + #( + #keys = [{ #values }] + )* + } + } + } - pub use #unique_name as #name; - }; + pub use #unique_name as #name; + }; - decl_macro.into() + decl_macro.into() } From f0d85ac1f6f381cd097511a66511794f66d3e5ba Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 19 Sep 2024 09:56:43 -0400 Subject: [PATCH 47/53] cargo fmt --all --- support/procedural-fork/src/benchmark.rs | 2117 +++++++++-------- .../src/construct_runtime/expand/call.rs | 393 +-- .../expand/composite_helper.rs | 132 +- .../src/construct_runtime/expand/config.rs | 208 +- .../construct_runtime/expand/freeze_reason.rs | 90 +- .../construct_runtime/expand/hold_reason.rs | 90 +- .../src/construct_runtime/expand/inherent.rs | 459 ++-- .../src/construct_runtime/expand/lock_id.rs | 72 +- .../src/construct_runtime/expand/metadata.rs | 399 ++-- .../src/construct_runtime/expand/origin.rs | 846 +++---- .../construct_runtime/expand/outer_enums.rs | 379 +-- .../construct_runtime/expand/slash_reason.rs | 72 +- .../src/construct_runtime/expand/task.rs | 212 +- .../src/construct_runtime/expand/unsigned.rs | 113 +- .../src/construct_runtime/mod.rs | 1010 ++++---- .../src/construct_runtime/parse.rs | 1257 +++++----- support/procedural-fork/src/crate_version.rs | 36 +- support/procedural-fork/src/derive_impl.rs | 385 +-- .../procedural-fork/src/dummy_part_checker.rs | 98 +- support/procedural-fork/src/dynamic_params.rs | 422 ++-- support/procedural-fork/src/key_prefix.rs | 142 +- .../procedural-fork/src/match_and_insert.rs | 244 +- support/procedural-fork/src/no_bound/clone.rs | 162 +- support/procedural-fork/src/no_bound/debug.rs | 186 +- .../procedural-fork/src/no_bound/default.rs | 139 +- support/procedural-fork/src/no_bound/ord.rs | 96 +- .../src/no_bound/partial_eq.rs | 214 +- .../src/no_bound/partial_ord.rs | 119 +- .../procedural-fork/src/pallet/expand/call.rs | 874 +++---- .../src/pallet/expand/composite.rs | 20 +- .../src/pallet/expand/config.rs | 120 +- .../src/pallet/expand/constants.rs | 172 +- .../src/pallet/expand/doc_only.rs | 152 +- .../src/pallet/expand/documentation.rs | 189 +- .../src/pallet/expand/error.rs | 298 +-- .../src/pallet/expand/event.rs | 303 +-- .../src/pallet/expand/genesis_build.rs | 50 +- .../src/pallet/expand/genesis_config.rs | 239 +- .../src/pallet/expand/hooks.rs | 588 ++--- .../src/pallet/expand/inherent.rs | 59 +- .../src/pallet/expand/instances.rs | 32 +- .../procedural-fork/src/pallet/expand/mod.rs | 124 +- .../src/pallet/expand/origin.rs | 59 +- .../src/pallet/expand/pallet_struct.rs | 524 ++-- .../src/pallet/expand/storage.rs | 1415 +++++------ .../src/pallet/expand/tasks.rs | 308 +-- .../src/pallet/expand/tt_default_parts.rs | 394 +-- .../src/pallet/expand/type_value.rs | 90 +- .../src/pallet/expand/validate_unsigned.rs | 60 +- .../src/pallet/expand/warnings.rs | 111 +- support/procedural-fork/src/pallet/mod.rs | 38 +- .../procedural-fork/src/pallet/parse/call.rs | 791 +++--- .../src/pallet/parse/composite.rs | 330 +-- .../src/pallet/parse/config.rs | 983 ++++---- .../procedural-fork/src/pallet/parse/error.rs | 148 +- .../procedural-fork/src/pallet/parse/event.rs | 206 +- .../src/pallet/parse/extra_constants.rs | 240 +- .../src/pallet/parse/genesis_build.rs | 69 +- .../src/pallet/parse/genesis_config.rs | 81 +- .../src/pallet/parse/helper.rs | 933 ++++---- .../procedural-fork/src/pallet/parse/hooks.rs | 104 +- .../src/pallet/parse/inherent.rs | 68 +- .../procedural-fork/src/pallet/parse/mod.rs | 1160 ++++----- .../src/pallet/parse/origin.rs | 80 +- .../src/pallet/parse/pallet_struct.rs | 220 +- .../src/pallet/parse/storage.rs | 1749 +++++++------- .../procedural-fork/src/pallet/parse/tasks.rs | 1489 ++++++------ .../src/pallet/parse/tests/mod.rs | 142 +- .../src/pallet/parse/tests/tasks.rs | 372 +-- .../src/pallet/parse/type_value.rs | 176 +- .../src/pallet/parse/validate_unsigned.rs | 64 +- support/procedural-fork/src/pallet_error.rs | 307 +-- .../procedural-fork/src/runtime/expand/mod.rs | 574 ++--- support/procedural-fork/src/runtime/mod.rs | 32 +- .../src/runtime/parse/helper.rs | 29 +- .../procedural-fork/src/runtime/parse/mod.rs | 365 +-- .../src/runtime/parse/pallet.rs | 146 +- .../src/runtime/parse/pallet_decl.rs | 77 +- .../src/runtime/parse/runtime_struct.rs | 25 +- .../src/runtime/parse/runtime_types.rs | 90 +- support/procedural-fork/src/storage_alias.rs | 1211 +++++----- support/procedural-fork/src/transactional.rs | 76 +- support/procedural-fork/src/tt_macro.rs | 82 +- 83 files changed, 14810 insertions(+), 13920 deletions(-) diff --git a/support/procedural-fork/src/benchmark.rs b/support/procedural-fork/src/benchmark.rs index 0a62c3f92..376200d6e 100644 --- a/support/procedural-fork/src/benchmark.rs +++ b/support/procedural-fork/src/benchmark.rs @@ -23,332 +23,369 @@ use proc_macro::TokenStream; use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; use quote::{quote, ToTokens}; use syn::{ - parse::{Nothing, ParseStream}, - parse_quote, - punctuated::Punctuated, - spanned::Spanned, - token::{Comma, Gt, Lt, PathSep}, - Attribute, Error, Expr, ExprBlock, ExprCall, ExprPath, FnArg, Item, ItemFn, ItemMod, Pat, Path, - PathArguments, PathSegment, Result, ReturnType, Signature, Stmt, Token, Type, TypePath, - Visibility, WhereClause, + parse::{Nothing, ParseStream}, + parse_quote, + punctuated::Punctuated, + spanned::Spanned, + token::{Comma, Gt, Lt, PathSep}, + Attribute, Error, Expr, ExprBlock, ExprCall, ExprPath, FnArg, Item, ItemFn, ItemMod, Pat, Path, + PathArguments, PathSegment, Result, ReturnType, Signature, Stmt, Token, Type, TypePath, + Visibility, WhereClause, }; mod keywords { - use syn::custom_keyword; - - custom_keyword!(benchmark); - custom_keyword!(benchmarks); - custom_keyword!(block); - custom_keyword!(extra); - custom_keyword!(pov_mode); - custom_keyword!(extrinsic_call); - custom_keyword!(skip_meta); - custom_keyword!(BenchmarkError); - custom_keyword!(Result); - custom_keyword!(MaxEncodedLen); - custom_keyword!(Measured); - custom_keyword!(Ignored); - - pub const BENCHMARK_TOKEN: &str = stringify!(benchmark); - pub const BENCHMARKS_TOKEN: &str = stringify!(benchmarks); + use syn::custom_keyword; + + custom_keyword!(benchmark); + custom_keyword!(benchmarks); + custom_keyword!(block); + custom_keyword!(extra); + custom_keyword!(pov_mode); + custom_keyword!(extrinsic_call); + custom_keyword!(skip_meta); + custom_keyword!(BenchmarkError); + custom_keyword!(Result); + custom_keyword!(MaxEncodedLen); + custom_keyword!(Measured); + custom_keyword!(Ignored); + + pub const BENCHMARK_TOKEN: &str = stringify!(benchmark); + pub const BENCHMARKS_TOKEN: &str = stringify!(benchmarks); } /// This represents the raw parsed data for a param definition such as `x: Linear<10, 20>`. #[derive(Clone)] struct ParamDef { - name: String, - _typ: Type, - start: syn::GenericArgument, - end: syn::GenericArgument, + name: String, + _typ: Type, + start: syn::GenericArgument, + end: syn::GenericArgument, } /// Allows easy parsing of the `<10, 20>` component of `x: Linear<10, 20>`. #[derive(Parse)] struct RangeArgs { - _lt_token: Lt, - start: syn::GenericArgument, - _comma: Comma, - end: syn::GenericArgument, - _trailing_comma: Option, - _gt_token: Gt, + _lt_token: Lt, + start: syn::GenericArgument, + _comma: Comma, + end: syn::GenericArgument, + _trailing_comma: Option, + _gt_token: Gt, } #[derive(Clone, Debug)] struct BenchmarkAttrs { - skip_meta: bool, - extra: bool, - pov_mode: Option, + skip_meta: bool, + extra: bool, + pov_mode: Option, } /// Represents a single benchmark option enum BenchmarkAttr { - Extra, - SkipMeta, - /// How the PoV should be measured. - PoV(PovModeAttr), + Extra, + SkipMeta, + /// How the PoV should be measured. + PoV(PovModeAttr), } impl syn::parse::Parse for PovModeAttr { - fn parse(input: ParseStream) -> Result { - let _pov: keywords::pov_mode = input.parse()?; - let _eq: Token![=] = input.parse()?; - let root = PovEstimationMode::parse(input)?; - - let mut maybe_content = None; - let _ = || -> Result<()> { - let content; - syn::braced!(content in input); - maybe_content = Some(content); - Ok(()) - }(); - - let per_key = match maybe_content { - Some(content) => { - let per_key = Punctuated::::parse_terminated(&content)?; - per_key.into_iter().collect() - }, - None => Vec::new(), - }; - - Ok(Self { root, per_key }) - } + fn parse(input: ParseStream) -> Result { + let _pov: keywords::pov_mode = input.parse()?; + let _eq: Token![=] = input.parse()?; + let root = PovEstimationMode::parse(input)?; + + let mut maybe_content = None; + let _ = || -> Result<()> { + let content; + syn::braced!(content in input); + maybe_content = Some(content); + Ok(()) + }(); + + let per_key = match maybe_content { + Some(content) => { + let per_key = Punctuated::::parse_terminated(&content)?; + per_key.into_iter().collect() + } + None => Vec::new(), + }; + + Ok(Self { root, per_key }) + } } impl syn::parse::Parse for BenchmarkAttr { - fn parse(input: ParseStream) -> Result { - let lookahead = input.lookahead1(); - if lookahead.peek(keywords::extra) { - let _extra: keywords::extra = input.parse()?; - Ok(BenchmarkAttr::Extra) - } else if lookahead.peek(keywords::skip_meta) { - let _skip_meta: keywords::skip_meta = input.parse()?; - Ok(BenchmarkAttr::SkipMeta) - } else if lookahead.peek(keywords::pov_mode) { - PovModeAttr::parse(input).map(BenchmarkAttr::PoV) - } else { - Err(lookahead.error()) - } - } + fn parse(input: ParseStream) -> Result { + let lookahead = input.lookahead1(); + if lookahead.peek(keywords::extra) { + let _extra: keywords::extra = input.parse()?; + Ok(BenchmarkAttr::Extra) + } else if lookahead.peek(keywords::skip_meta) { + let _skip_meta: keywords::skip_meta = input.parse()?; + Ok(BenchmarkAttr::SkipMeta) + } else if lookahead.peek(keywords::pov_mode) { + PovModeAttr::parse(input).map(BenchmarkAttr::PoV) + } else { + Err(lookahead.error()) + } + } } /// A `#[pov_mode = .. { .. }]` attribute. #[derive(Debug, Clone)] struct PovModeAttr { - /// The root mode for this benchmarks. - root: PovEstimationMode, - /// The pov-mode for a specific key. This overwrites `root` for this key. - per_key: Vec, + /// The root mode for this benchmarks. + root: PovEstimationMode, + /// The pov-mode for a specific key. This overwrites `root` for this key. + per_key: Vec, } /// A single key-value pair inside the `{}` of a `#[pov_mode = .. { .. }]` attribute. #[derive(Debug, Clone, derive_syn_parse::Parse)] struct PovModeKeyAttr { - /// A specific storage key for which to set the PoV mode. - key: Path, - _underscore: Token![:], - /// The PoV mode for this key. - mode: PovEstimationMode, + /// A specific storage key for which to set the PoV mode. + key: Path, + _underscore: Token![:], + /// The PoV mode for this key. + mode: PovEstimationMode, } /// How the PoV should be estimated. #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum PovEstimationMode { - /// Use the maximal encoded length as provided by [`codec::MaxEncodedLen`]. - MaxEncodedLen, - /// Measure the accessed value size in the pallet benchmarking and add some trie overhead. - Measured, - /// Do not estimate the PoV size for this storage item or benchmark. - Ignored, + /// Use the maximal encoded length as provided by [`codec::MaxEncodedLen`]. + MaxEncodedLen, + /// Measure the accessed value size in the pallet benchmarking and add some trie overhead. + Measured, + /// Do not estimate the PoV size for this storage item or benchmark. + Ignored, } impl syn::parse::Parse for PovEstimationMode { - fn parse(input: ParseStream) -> Result { - let lookahead = input.lookahead1(); - if lookahead.peek(keywords::MaxEncodedLen) { - let _max_encoded_len: keywords::MaxEncodedLen = input.parse()?; - return Ok(PovEstimationMode::MaxEncodedLen) - } else if lookahead.peek(keywords::Measured) { - let _measured: keywords::Measured = input.parse()?; - return Ok(PovEstimationMode::Measured) - } else if lookahead.peek(keywords::Ignored) { - let _ignored: keywords::Ignored = input.parse()?; - return Ok(PovEstimationMode::Ignored) - } else { - return Err(lookahead.error()) - } - } + fn parse(input: ParseStream) -> Result { + let lookahead = input.lookahead1(); + if lookahead.peek(keywords::MaxEncodedLen) { + let _max_encoded_len: keywords::MaxEncodedLen = input.parse()?; + return Ok(PovEstimationMode::MaxEncodedLen); + } else if lookahead.peek(keywords::Measured) { + let _measured: keywords::Measured = input.parse()?; + return Ok(PovEstimationMode::Measured); + } else if lookahead.peek(keywords::Ignored) { + let _ignored: keywords::Ignored = input.parse()?; + return Ok(PovEstimationMode::Ignored); + } else { + return Err(lookahead.error()); + } + } } impl ToString for PovEstimationMode { - fn to_string(&self) -> String { - match self { - PovEstimationMode::MaxEncodedLen => "MaxEncodedLen".into(), - PovEstimationMode::Measured => "Measured".into(), - PovEstimationMode::Ignored => "Ignored".into(), - } - } + fn to_string(&self) -> String { + match self { + PovEstimationMode::MaxEncodedLen => "MaxEncodedLen".into(), + PovEstimationMode::Measured => "Measured".into(), + PovEstimationMode::Ignored => "Ignored".into(), + } + } } impl quote::ToTokens for PovEstimationMode { - fn to_tokens(&self, tokens: &mut TokenStream2) { - match self { - PovEstimationMode::MaxEncodedLen => tokens.extend(quote!(MaxEncodedLen)), - PovEstimationMode::Measured => tokens.extend(quote!(Measured)), - PovEstimationMode::Ignored => tokens.extend(quote!(Ignored)), - } - } + fn to_tokens(&self, tokens: &mut TokenStream2) { + match self { + PovEstimationMode::MaxEncodedLen => tokens.extend(quote!(MaxEncodedLen)), + PovEstimationMode::Measured => tokens.extend(quote!(Measured)), + PovEstimationMode::Ignored => tokens.extend(quote!(Ignored)), + } + } } impl syn::parse::Parse for BenchmarkAttrs { - fn parse(input: ParseStream) -> syn::Result { - let mut extra = false; - let mut skip_meta = false; - let mut pov_mode = None; - let args = Punctuated::::parse_terminated(&input)?; - - for arg in args.into_iter() { - match arg { - BenchmarkAttr::Extra => { - if extra { - return Err(input.error("`extra` can only be specified once")) - } - extra = true; - }, - BenchmarkAttr::SkipMeta => { - if skip_meta { - return Err(input.error("`skip_meta` can only be specified once")) - } - skip_meta = true; - }, - BenchmarkAttr::PoV(mode) => { - if pov_mode.is_some() { - return Err(input.error("`pov_mode` can only be specified once")) - } - pov_mode = Some(mode); - }, - } - } - Ok(BenchmarkAttrs { extra, skip_meta, pov_mode }) - } + fn parse(input: ParseStream) -> syn::Result { + let mut extra = false; + let mut skip_meta = false; + let mut pov_mode = None; + let args = Punctuated::::parse_terminated(&input)?; + + for arg in args.into_iter() { + match arg { + BenchmarkAttr::Extra => { + if extra { + return Err(input.error("`extra` can only be specified once")); + } + extra = true; + } + BenchmarkAttr::SkipMeta => { + if skip_meta { + return Err(input.error("`skip_meta` can only be specified once")); + } + skip_meta = true; + } + BenchmarkAttr::PoV(mode) => { + if pov_mode.is_some() { + return Err(input.error("`pov_mode` can only be specified once")); + } + pov_mode = Some(mode); + } + } + } + Ok(BenchmarkAttrs { + extra, + skip_meta, + pov_mode, + }) + } } /// Represents the parsed extrinsic call for a benchmark #[derive(Clone)] enum BenchmarkCallDef { - ExtrinsicCall { origin: Expr, expr_call: ExprCall, attr_span: Span }, // #[extrinsic_call] - Block { block: ExprBlock, attr_span: Span }, // #[block] + ExtrinsicCall { + origin: Expr, + expr_call: ExprCall, + attr_span: Span, + }, // #[extrinsic_call] + Block { + block: ExprBlock, + attr_span: Span, + }, // #[block] } impl BenchmarkCallDef { - /// Returns the `span()` for attribute - fn attr_span(&self) -> Span { - match self { - BenchmarkCallDef::ExtrinsicCall { origin: _, expr_call: _, attr_span } => *attr_span, - BenchmarkCallDef::Block { block: _, attr_span } => *attr_span, - } - } + /// Returns the `span()` for attribute + fn attr_span(&self) -> Span { + match self { + BenchmarkCallDef::ExtrinsicCall { + origin: _, + expr_call: _, + attr_span, + } => *attr_span, + BenchmarkCallDef::Block { + block: _, + attr_span, + } => *attr_span, + } + } } /// Represents a parsed `#[benchmark]` or `#[instance_benchmark]` item. #[derive(Clone)] struct BenchmarkDef { - params: Vec, - setup_stmts: Vec, - call_def: BenchmarkCallDef, - verify_stmts: Vec, - last_stmt: Option, - fn_sig: Signature, - fn_vis: Visibility, - fn_attrs: Vec, + params: Vec, + setup_stmts: Vec, + call_def: BenchmarkCallDef, + verify_stmts: Vec, + last_stmt: Option, + fn_sig: Signature, + fn_vis: Visibility, + fn_attrs: Vec, } /// used to parse something compatible with `Result` #[derive(Parse)] struct ResultDef { - _result_kw: keywords::Result, - _lt: Token![<], - unit: Type, - _comma: Comma, - e_type: TypePath, - _gt: Token![>], + _result_kw: keywords::Result, + _lt: Token![<], + unit: Type, + _comma: Comma, + e_type: TypePath, + _gt: Token![>], } /// Ensures that `ReturnType` is a `Result<(), BenchmarkError>`, if specified fn ensure_valid_return_type(item_fn: &ItemFn) -> Result<()> { - if let ReturnType::Type(_, typ) = &item_fn.sig.output { - let non_unit = |span| return Err(Error::new(span, "expected `()`")); - let Type::Path(TypePath { path, qself: _ }) = &**typ else { - return Err(Error::new( + if let ReturnType::Type(_, typ) = &item_fn.sig.output { + let non_unit = |span| return Err(Error::new(span, "expected `()`")); + let Type::Path(TypePath { path, qself: _ }) = &**typ else { + return Err(Error::new( typ.span(), "Only `Result<(), BenchmarkError>` or a blank return type is allowed on benchmark function definitions", - )) - }; - let seg = path - .segments - .last() - .expect("to be parsed as a TypePath, it must have at least one segment; qed"); - let res: ResultDef = syn::parse2(seg.to_token_stream())?; - // ensure T in Result is () - let Type::Tuple(tup) = res.unit else { return non_unit(res.unit.span()) }; - if !tup.elems.is_empty() { - return non_unit(tup.span()) - } - let TypePath { path, qself: _ } = res.e_type; - let seg = path - .segments - .last() - .expect("to be parsed as a TypePath, it must have at least one segment; qed"); - syn::parse2::(seg.to_token_stream())?; - } - Ok(()) + )); + }; + let seg = path + .segments + .last() + .expect("to be parsed as a TypePath, it must have at least one segment; qed"); + let res: ResultDef = syn::parse2(seg.to_token_stream())?; + // ensure T in Result is () + let Type::Tuple(tup) = res.unit else { + return non_unit(res.unit.span()); + }; + if !tup.elems.is_empty() { + return non_unit(tup.span()); + } + let TypePath { path, qself: _ } = res.e_type; + let seg = path + .segments + .last() + .expect("to be parsed as a TypePath, it must have at least one segment; qed"); + syn::parse2::(seg.to_token_stream())?; + } + Ok(()) } /// Parses params such as `x: Linear<0, 1>` fn parse_params(item_fn: &ItemFn) -> Result> { - let mut params: Vec = Vec::new(); - for arg in &item_fn.sig.inputs { - let invalid_param = |span| { - return Err(Error::new( - span, - "Invalid benchmark function param. A valid example would be `x: Linear<5, 10>`.", - )) - }; - - let FnArg::Typed(arg) = arg else { return invalid_param(arg.span()) }; - let Pat::Ident(ident) = &*arg.pat else { return invalid_param(arg.span()) }; - - // check param name - let var_span = ident.span(); - let invalid_param_name = || { - return Err(Error::new( + let mut params: Vec = Vec::new(); + for arg in &item_fn.sig.inputs { + let invalid_param = |span| { + return Err(Error::new( + span, + "Invalid benchmark function param. A valid example would be `x: Linear<5, 10>`.", + )); + }; + + let FnArg::Typed(arg) = arg else { + return invalid_param(arg.span()); + }; + let Pat::Ident(ident) = &*arg.pat else { + return invalid_param(arg.span()); + }; + + // check param name + let var_span = ident.span(); + let invalid_param_name = || { + return Err(Error::new( var_span, "Benchmark parameter names must consist of a single lowercase letter (a-z) and no other characters.", )); - }; - let name = ident.ident.to_token_stream().to_string(); - if name.len() > 1 { - return invalid_param_name() - }; - let Some(name_char) = name.chars().next() else { return invalid_param_name() }; - if !name_char.is_alphabetic() || !name_char.is_lowercase() { - return invalid_param_name() - } - - // parse type - let typ = &*arg.ty; - let Type::Path(tpath) = typ else { return invalid_param(typ.span()) }; - let Some(segment) = tpath.path.segments.last() else { return invalid_param(typ.span()) }; - let args = segment.arguments.to_token_stream().into(); - let Ok(args) = syn::parse::(args) else { return invalid_param(typ.span()) }; - - params.push(ParamDef { name, _typ: typ.clone(), start: args.start, end: args.end }); - } - Ok(params) + }; + let name = ident.ident.to_token_stream().to_string(); + if name.len() > 1 { + return invalid_param_name(); + }; + let Some(name_char) = name.chars().next() else { + return invalid_param_name(); + }; + if !name_char.is_alphabetic() || !name_char.is_lowercase() { + return invalid_param_name(); + } + + // parse type + let typ = &*arg.ty; + let Type::Path(tpath) = typ else { + return invalid_param(typ.span()); + }; + let Some(segment) = tpath.path.segments.last() else { + return invalid_param(typ.span()); + }; + let args = segment.arguments.to_token_stream().into(); + let Ok(args) = syn::parse::(args) else { + return invalid_param(typ.span()); + }; + + params.push(ParamDef { + name, + _typ: typ.clone(), + start: args.start, + end: args.end, + }); + } + Ok(params) } /// Used in several places where the `#[extrinsic_call]` or `#[body]` annotation is missing fn missing_call(item_fn: &ItemFn) -> Result { - return Err(Error::new( + return Err(Error::new( item_fn.block.brace_token.span.join(), "No valid #[extrinsic_call] or #[block] annotation could be found in benchmark function body." )); @@ -358,8 +395,8 @@ fn missing_call(item_fn: &ItemFn) -> Result { /// returns them. Also handles parsing errors for invalid / extra call defs. AKA this is /// general handling for `#[extrinsic_call]` and `#[block]` fn parse_call_def(item_fn: &ItemFn) -> Result<(usize, BenchmarkCallDef)> { - // #[extrinsic_call] / #[block] handling - let call_defs = item_fn.block.stmts.iter().enumerate().filter_map(|(i, child)| { + // #[extrinsic_call] / #[block] handling + let call_defs = item_fn.block.stmts.iter().enumerate().filter_map(|(i, child)| { if let Stmt::Expr(Expr::Call(expr_call), _semi) = child { // #[extrinsic_call] case expr_call.attrs.iter().enumerate().find_map(|(k, attr)| { @@ -393,810 +430,850 @@ fn parse_call_def(item_fn: &ItemFn) -> Result<(usize, BenchmarkCallDef)> { None } }).collect::>>()?; - Ok(match &call_defs[..] { - [(i, call_def)] => (*i, call_def.clone()), // = 1 - [] => return missing_call(item_fn), - _ => - return Err(Error::new( - call_defs[1].1.attr_span(), - "Only one #[extrinsic_call] or #[block] attribute is allowed per benchmark.", - )), - }) + Ok(match &call_defs[..] { + [(i, call_def)] => (*i, call_def.clone()), // = 1 + [] => return missing_call(item_fn), + _ => { + return Err(Error::new( + call_defs[1].1.attr_span(), + "Only one #[extrinsic_call] or #[block] attribute is allowed per benchmark.", + )) + } + }) } impl BenchmarkDef { - /// Constructs a [`BenchmarkDef`] by traversing an existing [`ItemFn`] node. - pub fn from(item_fn: &ItemFn) -> Result { - let params = parse_params(item_fn)?; - ensure_valid_return_type(item_fn)?; - let (i, call_def) = parse_call_def(&item_fn)?; - - let (verify_stmts, last_stmt) = match item_fn.sig.output { - ReturnType::Default => - // no return type, last_stmt should be None - (Vec::from(&item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len()]), None), - ReturnType::Type(_, _) => { - // defined return type, last_stmt should be Result<(), BenchmarkError> - // compatible and should not be included in verify_stmts - if i + 1 >= item_fn.block.stmts.len() { - return Err(Error::new( - item_fn.block.span(), - "Benchmark `#[block]` or `#[extrinsic_call]` item cannot be the \ + /// Constructs a [`BenchmarkDef`] by traversing an existing [`ItemFn`] node. + pub fn from(item_fn: &ItemFn) -> Result { + let params = parse_params(item_fn)?; + ensure_valid_return_type(item_fn)?; + let (i, call_def) = parse_call_def(&item_fn)?; + + let (verify_stmts, last_stmt) = match item_fn.sig.output { + ReturnType::Default => + // no return type, last_stmt should be None + { + ( + Vec::from(&item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len()]), + None, + ) + } + ReturnType::Type(_, _) => { + // defined return type, last_stmt should be Result<(), BenchmarkError> + // compatible and should not be included in verify_stmts + if i + 1 >= item_fn.block.stmts.len() { + return Err(Error::new( + item_fn.block.span(), + "Benchmark `#[block]` or `#[extrinsic_call]` item cannot be the \ last statement of your benchmark function definition if you have \ defined a return type. You should return something compatible \ with Result<(), BenchmarkError> (i.e. `Ok(())`) as the last statement \ or change your signature to a blank return type.", - )) - } - let Some(stmt) = item_fn.block.stmts.last() else { return missing_call(item_fn) }; - ( - Vec::from(&item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len() - 1]), - Some(stmt.clone()), - ) - }, - }; - - Ok(BenchmarkDef { - params, - setup_stmts: Vec::from(&item_fn.block.stmts[0..i]), - call_def, - verify_stmts, - last_stmt, - fn_sig: item_fn.sig.clone(), - fn_vis: item_fn.vis.clone(), - fn_attrs: item_fn.attrs.clone(), - }) - } + )); + } + let Some(stmt) = item_fn.block.stmts.last() else { + return missing_call(item_fn); + }; + ( + Vec::from(&item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len() - 1]), + Some(stmt.clone()), + ) + } + }; + + Ok(BenchmarkDef { + params, + setup_stmts: Vec::from(&item_fn.block.stmts[0..i]), + call_def, + verify_stmts, + last_stmt, + fn_sig: item_fn.sig.clone(), + fn_vis: item_fn.vis.clone(), + fn_attrs: item_fn.attrs.clone(), + }) + } } /// Parses and expands a `#[benchmarks]` or `#[instance_benchmarks]` invocation pub fn benchmarks( - attrs: TokenStream, - tokens: TokenStream, - instance: bool, + attrs: TokenStream, + tokens: TokenStream, + instance: bool, ) -> syn::Result { - let krate = generate_access_from_frame_or_crate("frame-benchmarking")?; - // gather module info - let module: ItemMod = syn::parse(tokens)?; - let mod_span = module.span(); - let where_clause = match syn::parse::(attrs.clone()) { - Ok(_) => quote!(), - Err(_) => syn::parse::(attrs)?.predicates.to_token_stream(), - }; - let mod_vis = module.vis; - let mod_name = module.ident; - - // consume #[benchmarks] attribute by excluding it from mod_attrs - let mod_attrs: Vec<&Attribute> = module - .attrs - .iter() - .filter(|attr| !attr.path().is_ident(keywords::BENCHMARKS_TOKEN)) - .collect(); - - let mut benchmark_names: Vec = Vec::new(); - let mut extra_benchmark_names: Vec = Vec::new(); - let mut skip_meta_benchmark_names: Vec = Vec::new(); - // Map benchmarks to PoV modes. - let mut pov_modes = Vec::new(); - - let (_brace, mut content) = - module.content.ok_or(syn::Error::new(mod_span, "Module cannot be empty!"))?; - - // find all function defs marked with #[benchmark] - let benchmark_fn_metas = content.iter_mut().filter_map(|stmt| { - // parse as a function def first - let Item::Fn(func) = stmt else { return None }; - - // find #[benchmark] attribute on function def - let benchmark_attr = - func.attrs.iter().find(|attr| attr.path().is_ident(keywords::BENCHMARK_TOKEN))?; - - Some((benchmark_attr.clone(), func.clone(), stmt)) - }); - - // parse individual benchmark defs and args - for (benchmark_attr, func, stmt) in benchmark_fn_metas { - // parse benchmark def - let benchmark_def = BenchmarkDef::from(&func)?; - - // record benchmark name - let name = &func.sig.ident; - benchmark_names.push(name.clone()); - - // Check if we need to parse any args - if benchmark_attr.meta.require_path_only().is_err() { - // parse any args provided to #[benchmark] - let benchmark_attrs: BenchmarkAttrs = benchmark_attr.parse_args()?; - - // record name sets - if benchmark_attrs.extra { - extra_benchmark_names.push(name.clone()); - } else if benchmark_attrs.skip_meta { - skip_meta_benchmark_names.push(name.clone()); - } - - if let Some(mode) = benchmark_attrs.pov_mode { - let mut modes = Vec::new(); - // We cannot expand strings here since it is no-std, but syn does not expand bytes. - let name = name.to_string(); - let m = mode.root.to_string(); - modes.push(quote!(("ALL".as_bytes().to_vec(), #m.as_bytes().to_vec()))); - - for attr in mode.per_key.iter() { - // syn always puts spaces in quoted paths: - let key = attr.key.clone().into_token_stream().to_string().replace(" ", ""); - let mode = attr.mode.to_string(); - modes.push(quote!((#key.as_bytes().to_vec(), #mode.as_bytes().to_vec()))); - } - - pov_modes.push( - quote!((#name.as_bytes().to_vec(), #krate::__private::vec![#(#modes),*])), - ); - } - } - - // expand benchmark - let expanded = expand_benchmark(benchmark_def, name, instance, where_clause.clone()); - - // replace original function def with expanded code - *stmt = Item::Verbatim(expanded); - } - - // generics - let type_use_generics = match instance { - false => quote!(T), - true => quote!(T, I), - }; - let type_impl_generics = match instance { - false => quote!(T: Config), - true => quote!(T: Config, I: 'static), - }; - - let frame_system = generate_access_from_frame_or_crate("frame-system")?; - - // benchmark name variables - let benchmark_names_str: Vec = benchmark_names.iter().map(|n| n.to_string()).collect(); - let extra_benchmark_names_str: Vec = - extra_benchmark_names.iter().map(|n| n.to_string()).collect(); - let skip_meta_benchmark_names_str: Vec = - skip_meta_benchmark_names.iter().map(|n| n.to_string()).collect(); - let mut selected_benchmark_mappings: Vec = Vec::new(); - let mut benchmarks_by_name_mappings: Vec = Vec::new(); - let test_idents: Vec = benchmark_names_str - .iter() - .map(|n| Ident::new(format!("test_benchmark_{}", n).as_str(), Span::call_site())) - .collect(); - for i in 0..benchmark_names.len() { - let name_ident = &benchmark_names[i]; - let name_str = &benchmark_names_str[i]; - let test_ident = &test_idents[i]; - selected_benchmark_mappings.push(quote!(#name_str => SelectedBenchmark::#name_ident)); - benchmarks_by_name_mappings.push(quote!(#name_str => Self::#test_ident())) - } - - let impl_test_function = content - .iter_mut() - .find_map(|item| { - let Item::Macro(item_macro) = item else { - return None; - }; - - if !item_macro - .mac - .path - .segments - .iter() - .any(|s| s.ident == "impl_benchmark_test_suite") - { - return None; - } - - let tokens = item_macro.mac.tokens.clone(); - *item = Item::Verbatim(quote! {}); - - Some(quote! { - impl_test_function!( - (#( {} #benchmark_names )*) - (#( #extra_benchmark_names )*) - (#( #skip_meta_benchmark_names )*) - #tokens - ); - }) - }) - .unwrap_or(quote! {}); - - // emit final quoted tokens - let res = quote! { - #(#mod_attrs) - * - #mod_vis mod #mod_name { - #(#content) - * - - #[allow(non_camel_case_types)] - enum SelectedBenchmark { - #(#benchmark_names), - * - } - - impl<#type_impl_generics> #krate::BenchmarkingSetup<#type_use_generics> for SelectedBenchmark where #where_clause { - fn components(&self) -> #krate::__private::Vec<(#krate::BenchmarkParameter, u32, u32)> { - match self { - #( - Self::#benchmark_names => { - <#benchmark_names as #krate::BenchmarkingSetup<#type_use_generics>>::components(&#benchmark_names) - } - ) - * - } - } - - fn instance( - &self, - components: &[(#krate::BenchmarkParameter, u32)], - verify: bool, - ) -> Result< - #krate::__private::Box Result<(), #krate::BenchmarkError>>, - #krate::BenchmarkError, - > { - match self { - #( - Self::#benchmark_names => { - <#benchmark_names as #krate::BenchmarkingSetup< - #type_use_generics - >>::instance(&#benchmark_names, components, verify) - } - ) - * - } - } - } - #[cfg(any(feature = "runtime-benchmarks", test))] - impl<#type_impl_generics> #krate::Benchmarking for Pallet<#type_use_generics> - where T: #frame_system::Config, #where_clause - { - fn benchmarks( - extra: bool, - ) -> #krate::__private::Vec<#krate::BenchmarkMetadata> { - let mut all_names = #krate::__private::vec![ - #(#benchmark_names_str), - * - ]; - if !extra { - let extra = [ - #(#extra_benchmark_names_str), - * - ]; - all_names.retain(|x| !extra.contains(x)); - } - let pov_modes: - #krate::__private::Vec<( - #krate::__private::Vec, - #krate::__private::Vec<( - #krate::__private::Vec, - #krate::__private::Vec - )>, - )> = #krate::__private::vec![ - #( #pov_modes ),* - ]; - all_names.into_iter().map(|benchmark| { - let selected_benchmark = match benchmark { - #(#selected_benchmark_mappings), - *, - _ => panic!("all benchmarks should be selectable") - }; - let components = >::components(&selected_benchmark); - let name = benchmark.as_bytes().to_vec(); - let modes = pov_modes.iter().find(|p| p.0 == name).map(|p| p.1.clone()); - - #krate::BenchmarkMetadata { - name: benchmark.as_bytes().to_vec(), - components, - pov_modes: modes.unwrap_or_default(), - } - }).collect::<#krate::__private::Vec<_>>() - } - - fn run_benchmark( - extrinsic: &[u8], - c: &[(#krate::BenchmarkParameter, u32)], - whitelist: &[#krate::__private::TrackedStorageKey], - verify: bool, - internal_repeats: u32, - ) -> Result<#krate::__private::Vec<#krate::BenchmarkResult>, #krate::BenchmarkError> { - let extrinsic = #krate::__private::str::from_utf8(extrinsic).map_err(|_| "`extrinsic` is not a valid utf-8 string!")?; - let selected_benchmark = match extrinsic { - #(#selected_benchmark_mappings), - *, - _ => return Err("Could not find extrinsic.".into()), - }; - let mut whitelist = whitelist.to_vec(); - let whitelisted_caller_key = <#frame_system::Account< - T, - > as #krate::__private::storage::StorageMap<_, _,>>::hashed_key_for( - #krate::whitelisted_caller::() - ); - whitelist.push(whitelisted_caller_key.into()); - let transactional_layer_key = #krate::__private::TrackedStorageKey::new( - #krate::__private::storage::transactional::TRANSACTION_LEVEL_KEY.into(), - ); - whitelist.push(transactional_layer_key); - // Whitelist the `:extrinsic_index`. - let extrinsic_index = #krate::__private::TrackedStorageKey::new( - #krate::__private::well_known_keys::EXTRINSIC_INDEX.into() - ); - whitelist.push(extrinsic_index); - // Whitelist the `:intrablock_entropy`. - let intrablock_entropy = #krate::__private::TrackedStorageKey::new( - #krate::__private::well_known_keys::INTRABLOCK_ENTROPY.into() - ); - whitelist.push(intrablock_entropy); - - #krate::benchmarking::set_whitelist(whitelist.clone()); - let mut results: #krate::__private::Vec<#krate::BenchmarkResult> = #krate::__private::Vec::new(); - - // Always do at least one internal repeat... - for _ in 0 .. internal_repeats.max(1) { - // Always reset the state after the benchmark. - #krate::__private::defer!(#krate::benchmarking::wipe_db()); - - // Set up the externalities environment for the setup we want to - // benchmark. - let closure_to_benchmark = < - SelectedBenchmark as #krate::BenchmarkingSetup<#type_use_generics> - >::instance(&selected_benchmark, c, verify)?; - - // Set the block number to at least 1 so events are deposited. - if #krate::__private::Zero::is_zero(&#frame_system::Pallet::::block_number()) { - #frame_system::Pallet::::set_block_number(1u32.into()); - } - - // Commit the externalities to the database, flushing the DB cache. - // This will enable worst case scenario for reading from the database. - #krate::benchmarking::commit_db(); - - // Access all whitelisted keys to get them into the proof recorder since the - // recorder does now have a whitelist. - for key in &whitelist { - #krate::__private::storage::unhashed::get_raw(&key.key); - } - - // Reset the read/write counter so we don't count operations in the setup process. - #krate::benchmarking::reset_read_write_count(); - - // Time the extrinsic logic. - #krate::__private::log::trace!( - target: "benchmark", - "Start Benchmark: {} ({:?})", - extrinsic, - c - ); - - let start_pov = #krate::benchmarking::proof_size(); - let start_extrinsic = #krate::benchmarking::current_time(); - - closure_to_benchmark()?; - - let finish_extrinsic = #krate::benchmarking::current_time(); - let end_pov = #krate::benchmarking::proof_size(); - - // Calculate the diff caused by the benchmark. - let elapsed_extrinsic = finish_extrinsic.saturating_sub(start_extrinsic); - let diff_pov = match (start_pov, end_pov) { - (Some(start), Some(end)) => end.saturating_sub(start), - _ => Default::default(), - }; - - // Commit the changes to get proper write count - #krate::benchmarking::commit_db(); - #krate::__private::log::trace!( - target: "benchmark", - "End Benchmark: {} ns", elapsed_extrinsic - ); - let read_write_count = #krate::benchmarking::read_write_count(); - #krate::__private::log::trace!( - target: "benchmark", - "Read/Write Count {:?}", read_write_count - ); - - // Time the storage root recalculation. - let start_storage_root = #krate::benchmarking::current_time(); - #krate::__private::storage_root(#krate::__private::StateVersion::V1); - let finish_storage_root = #krate::benchmarking::current_time(); - let elapsed_storage_root = finish_storage_root - start_storage_root; - - let skip_meta = [ #(#skip_meta_benchmark_names_str),* ]; - let read_and_written_keys = if skip_meta.contains(&extrinsic) { - #krate::__private::vec![(b"Skipped Metadata".to_vec(), 0, 0, false)] - } else { - #krate::benchmarking::get_read_and_written_keys() - }; - - results.push(#krate::BenchmarkResult { - components: c.to_vec(), - extrinsic_time: elapsed_extrinsic, - storage_root_time: elapsed_storage_root, - reads: read_write_count.0, - repeat_reads: read_write_count.1, - writes: read_write_count.2, - repeat_writes: read_write_count.3, - proof_size: diff_pov, - keys: read_and_written_keys, - }); - } - - return Ok(results); - } - } - - #[cfg(test)] - impl<#type_impl_generics> Pallet<#type_use_generics> where T: #frame_system::Config, #where_clause { - /// Test a particular benchmark by name. - /// - /// This isn't called `test_benchmark_by_name` just in case some end-user eventually - /// writes a benchmark, itself called `by_name`; the function would be shadowed in - /// that case. - /// - /// This is generally intended to be used by child test modules such as those created - /// by the `impl_benchmark_test_suite` macro. However, it is not an error if a pallet - /// author chooses not to implement benchmarks. - #[allow(unused)] - fn test_bench_by_name(name: &[u8]) -> Result<(), #krate::BenchmarkError> { - let name = #krate::__private::str::from_utf8(name) - .map_err(|_| -> #krate::BenchmarkError { "`name` is not a valid utf8 string!".into() })?; - match name { - #(#benchmarks_by_name_mappings), - *, - _ => Err("Could not find test for requested benchmark.".into()), - } - } - } - - #impl_test_function - } - #mod_vis use #mod_name::*; - }; - Ok(res.into()) + let krate = generate_access_from_frame_or_crate("frame-benchmarking")?; + // gather module info + let module: ItemMod = syn::parse(tokens)?; + let mod_span = module.span(); + let where_clause = match syn::parse::(attrs.clone()) { + Ok(_) => quote!(), + Err(_) => syn::parse::(attrs)? + .predicates + .to_token_stream(), + }; + let mod_vis = module.vis; + let mod_name = module.ident; + + // consume #[benchmarks] attribute by excluding it from mod_attrs + let mod_attrs: Vec<&Attribute> = module + .attrs + .iter() + .filter(|attr| !attr.path().is_ident(keywords::BENCHMARKS_TOKEN)) + .collect(); + + let mut benchmark_names: Vec = Vec::new(); + let mut extra_benchmark_names: Vec = Vec::new(); + let mut skip_meta_benchmark_names: Vec = Vec::new(); + // Map benchmarks to PoV modes. + let mut pov_modes = Vec::new(); + + let (_brace, mut content) = module + .content + .ok_or(syn::Error::new(mod_span, "Module cannot be empty!"))?; + + // find all function defs marked with #[benchmark] + let benchmark_fn_metas = content.iter_mut().filter_map(|stmt| { + // parse as a function def first + let Item::Fn(func) = stmt else { return None }; + + // find #[benchmark] attribute on function def + let benchmark_attr = func + .attrs + .iter() + .find(|attr| attr.path().is_ident(keywords::BENCHMARK_TOKEN))?; + + Some((benchmark_attr.clone(), func.clone(), stmt)) + }); + + // parse individual benchmark defs and args + for (benchmark_attr, func, stmt) in benchmark_fn_metas { + // parse benchmark def + let benchmark_def = BenchmarkDef::from(&func)?; + + // record benchmark name + let name = &func.sig.ident; + benchmark_names.push(name.clone()); + + // Check if we need to parse any args + if benchmark_attr.meta.require_path_only().is_err() { + // parse any args provided to #[benchmark] + let benchmark_attrs: BenchmarkAttrs = benchmark_attr.parse_args()?; + + // record name sets + if benchmark_attrs.extra { + extra_benchmark_names.push(name.clone()); + } else if benchmark_attrs.skip_meta { + skip_meta_benchmark_names.push(name.clone()); + } + + if let Some(mode) = benchmark_attrs.pov_mode { + let mut modes = Vec::new(); + // We cannot expand strings here since it is no-std, but syn does not expand bytes. + let name = name.to_string(); + let m = mode.root.to_string(); + modes.push(quote!(("ALL".as_bytes().to_vec(), #m.as_bytes().to_vec()))); + + for attr in mode.per_key.iter() { + // syn always puts spaces in quoted paths: + let key = attr + .key + .clone() + .into_token_stream() + .to_string() + .replace(" ", ""); + let mode = attr.mode.to_string(); + modes.push(quote!((#key.as_bytes().to_vec(), #mode.as_bytes().to_vec()))); + } + + pov_modes.push( + quote!((#name.as_bytes().to_vec(), #krate::__private::vec![#(#modes),*])), + ); + } + } + + // expand benchmark + let expanded = expand_benchmark(benchmark_def, name, instance, where_clause.clone()); + + // replace original function def with expanded code + *stmt = Item::Verbatim(expanded); + } + + // generics + let type_use_generics = match instance { + false => quote!(T), + true => quote!(T, I), + }; + let type_impl_generics = match instance { + false => quote!(T: Config), + true => quote!(T: Config, I: 'static), + }; + + let frame_system = generate_access_from_frame_or_crate("frame-system")?; + + // benchmark name variables + let benchmark_names_str: Vec = benchmark_names.iter().map(|n| n.to_string()).collect(); + let extra_benchmark_names_str: Vec = extra_benchmark_names + .iter() + .map(|n| n.to_string()) + .collect(); + let skip_meta_benchmark_names_str: Vec = skip_meta_benchmark_names + .iter() + .map(|n| n.to_string()) + .collect(); + let mut selected_benchmark_mappings: Vec = Vec::new(); + let mut benchmarks_by_name_mappings: Vec = Vec::new(); + let test_idents: Vec = benchmark_names_str + .iter() + .map(|n| Ident::new(format!("test_benchmark_{}", n).as_str(), Span::call_site())) + .collect(); + for i in 0..benchmark_names.len() { + let name_ident = &benchmark_names[i]; + let name_str = &benchmark_names_str[i]; + let test_ident = &test_idents[i]; + selected_benchmark_mappings.push(quote!(#name_str => SelectedBenchmark::#name_ident)); + benchmarks_by_name_mappings.push(quote!(#name_str => Self::#test_ident())) + } + + let impl_test_function = content + .iter_mut() + .find_map(|item| { + let Item::Macro(item_macro) = item else { + return None; + }; + + if !item_macro + .mac + .path + .segments + .iter() + .any(|s| s.ident == "impl_benchmark_test_suite") + { + return None; + } + + let tokens = item_macro.mac.tokens.clone(); + *item = Item::Verbatim(quote! {}); + + Some(quote! { + impl_test_function!( + (#( {} #benchmark_names )*) + (#( #extra_benchmark_names )*) + (#( #skip_meta_benchmark_names )*) + #tokens + ); + }) + }) + .unwrap_or(quote! {}); + + // emit final quoted tokens + let res = quote! { + #(#mod_attrs) + * + #mod_vis mod #mod_name { + #(#content) + * + + #[allow(non_camel_case_types)] + enum SelectedBenchmark { + #(#benchmark_names), + * + } + + impl<#type_impl_generics> #krate::BenchmarkingSetup<#type_use_generics> for SelectedBenchmark where #where_clause { + fn components(&self) -> #krate::__private::Vec<(#krate::BenchmarkParameter, u32, u32)> { + match self { + #( + Self::#benchmark_names => { + <#benchmark_names as #krate::BenchmarkingSetup<#type_use_generics>>::components(&#benchmark_names) + } + ) + * + } + } + + fn instance( + &self, + components: &[(#krate::BenchmarkParameter, u32)], + verify: bool, + ) -> Result< + #krate::__private::Box Result<(), #krate::BenchmarkError>>, + #krate::BenchmarkError, + > { + match self { + #( + Self::#benchmark_names => { + <#benchmark_names as #krate::BenchmarkingSetup< + #type_use_generics + >>::instance(&#benchmark_names, components, verify) + } + ) + * + } + } + } + #[cfg(any(feature = "runtime-benchmarks", test))] + impl<#type_impl_generics> #krate::Benchmarking for Pallet<#type_use_generics> + where T: #frame_system::Config, #where_clause + { + fn benchmarks( + extra: bool, + ) -> #krate::__private::Vec<#krate::BenchmarkMetadata> { + let mut all_names = #krate::__private::vec![ + #(#benchmark_names_str), + * + ]; + if !extra { + let extra = [ + #(#extra_benchmark_names_str), + * + ]; + all_names.retain(|x| !extra.contains(x)); + } + let pov_modes: + #krate::__private::Vec<( + #krate::__private::Vec, + #krate::__private::Vec<( + #krate::__private::Vec, + #krate::__private::Vec + )>, + )> = #krate::__private::vec![ + #( #pov_modes ),* + ]; + all_names.into_iter().map(|benchmark| { + let selected_benchmark = match benchmark { + #(#selected_benchmark_mappings), + *, + _ => panic!("all benchmarks should be selectable") + }; + let components = >::components(&selected_benchmark); + let name = benchmark.as_bytes().to_vec(); + let modes = pov_modes.iter().find(|p| p.0 == name).map(|p| p.1.clone()); + + #krate::BenchmarkMetadata { + name: benchmark.as_bytes().to_vec(), + components, + pov_modes: modes.unwrap_or_default(), + } + }).collect::<#krate::__private::Vec<_>>() + } + + fn run_benchmark( + extrinsic: &[u8], + c: &[(#krate::BenchmarkParameter, u32)], + whitelist: &[#krate::__private::TrackedStorageKey], + verify: bool, + internal_repeats: u32, + ) -> Result<#krate::__private::Vec<#krate::BenchmarkResult>, #krate::BenchmarkError> { + let extrinsic = #krate::__private::str::from_utf8(extrinsic).map_err(|_| "`extrinsic` is not a valid utf-8 string!")?; + let selected_benchmark = match extrinsic { + #(#selected_benchmark_mappings), + *, + _ => return Err("Could not find extrinsic.".into()), + }; + let mut whitelist = whitelist.to_vec(); + let whitelisted_caller_key = <#frame_system::Account< + T, + > as #krate::__private::storage::StorageMap<_, _,>>::hashed_key_for( + #krate::whitelisted_caller::() + ); + whitelist.push(whitelisted_caller_key.into()); + let transactional_layer_key = #krate::__private::TrackedStorageKey::new( + #krate::__private::storage::transactional::TRANSACTION_LEVEL_KEY.into(), + ); + whitelist.push(transactional_layer_key); + // Whitelist the `:extrinsic_index`. + let extrinsic_index = #krate::__private::TrackedStorageKey::new( + #krate::__private::well_known_keys::EXTRINSIC_INDEX.into() + ); + whitelist.push(extrinsic_index); + // Whitelist the `:intrablock_entropy`. + let intrablock_entropy = #krate::__private::TrackedStorageKey::new( + #krate::__private::well_known_keys::INTRABLOCK_ENTROPY.into() + ); + whitelist.push(intrablock_entropy); + + #krate::benchmarking::set_whitelist(whitelist.clone()); + let mut results: #krate::__private::Vec<#krate::BenchmarkResult> = #krate::__private::Vec::new(); + + // Always do at least one internal repeat... + for _ in 0 .. internal_repeats.max(1) { + // Always reset the state after the benchmark. + #krate::__private::defer!(#krate::benchmarking::wipe_db()); + + // Set up the externalities environment for the setup we want to + // benchmark. + let closure_to_benchmark = < + SelectedBenchmark as #krate::BenchmarkingSetup<#type_use_generics> + >::instance(&selected_benchmark, c, verify)?; + + // Set the block number to at least 1 so events are deposited. + if #krate::__private::Zero::is_zero(&#frame_system::Pallet::::block_number()) { + #frame_system::Pallet::::set_block_number(1u32.into()); + } + + // Commit the externalities to the database, flushing the DB cache. + // This will enable worst case scenario for reading from the database. + #krate::benchmarking::commit_db(); + + // Access all whitelisted keys to get them into the proof recorder since the + // recorder does now have a whitelist. + for key in &whitelist { + #krate::__private::storage::unhashed::get_raw(&key.key); + } + + // Reset the read/write counter so we don't count operations in the setup process. + #krate::benchmarking::reset_read_write_count(); + + // Time the extrinsic logic. + #krate::__private::log::trace!( + target: "benchmark", + "Start Benchmark: {} ({:?})", + extrinsic, + c + ); + + let start_pov = #krate::benchmarking::proof_size(); + let start_extrinsic = #krate::benchmarking::current_time(); + + closure_to_benchmark()?; + + let finish_extrinsic = #krate::benchmarking::current_time(); + let end_pov = #krate::benchmarking::proof_size(); + + // Calculate the diff caused by the benchmark. + let elapsed_extrinsic = finish_extrinsic.saturating_sub(start_extrinsic); + let diff_pov = match (start_pov, end_pov) { + (Some(start), Some(end)) => end.saturating_sub(start), + _ => Default::default(), + }; + + // Commit the changes to get proper write count + #krate::benchmarking::commit_db(); + #krate::__private::log::trace!( + target: "benchmark", + "End Benchmark: {} ns", elapsed_extrinsic + ); + let read_write_count = #krate::benchmarking::read_write_count(); + #krate::__private::log::trace!( + target: "benchmark", + "Read/Write Count {:?}", read_write_count + ); + + // Time the storage root recalculation. + let start_storage_root = #krate::benchmarking::current_time(); + #krate::__private::storage_root(#krate::__private::StateVersion::V1); + let finish_storage_root = #krate::benchmarking::current_time(); + let elapsed_storage_root = finish_storage_root - start_storage_root; + + let skip_meta = [ #(#skip_meta_benchmark_names_str),* ]; + let read_and_written_keys = if skip_meta.contains(&extrinsic) { + #krate::__private::vec![(b"Skipped Metadata".to_vec(), 0, 0, false)] + } else { + #krate::benchmarking::get_read_and_written_keys() + }; + + results.push(#krate::BenchmarkResult { + components: c.to_vec(), + extrinsic_time: elapsed_extrinsic, + storage_root_time: elapsed_storage_root, + reads: read_write_count.0, + repeat_reads: read_write_count.1, + writes: read_write_count.2, + repeat_writes: read_write_count.3, + proof_size: diff_pov, + keys: read_and_written_keys, + }); + } + + return Ok(results); + } + } + + #[cfg(test)] + impl<#type_impl_generics> Pallet<#type_use_generics> where T: #frame_system::Config, #where_clause { + /// Test a particular benchmark by name. + /// + /// This isn't called `test_benchmark_by_name` just in case some end-user eventually + /// writes a benchmark, itself called `by_name`; the function would be shadowed in + /// that case. + /// + /// This is generally intended to be used by child test modules such as those created + /// by the `impl_benchmark_test_suite` macro. However, it is not an error if a pallet + /// author chooses not to implement benchmarks. + #[allow(unused)] + fn test_bench_by_name(name: &[u8]) -> Result<(), #krate::BenchmarkError> { + let name = #krate::__private::str::from_utf8(name) + .map_err(|_| -> #krate::BenchmarkError { "`name` is not a valid utf8 string!".into() })?; + match name { + #(#benchmarks_by_name_mappings), + *, + _ => Err("Could not find test for requested benchmark.".into()), + } + } + } + + #impl_test_function + } + #mod_vis use #mod_name::*; + }; + Ok(res.into()) } /// Prepares a [`Vec`] to be interpolated by [`quote!`] by creating easily-iterable /// arrays formatted in such a way that they can be interpolated directly. struct UnrolledParams { - param_ranges: Vec, - param_names: Vec, + param_ranges: Vec, + param_names: Vec, } impl UnrolledParams { - /// Constructs an [`UnrolledParams`] from a [`Vec`] - fn from(params: &Vec) -> UnrolledParams { - let param_ranges: Vec = params - .iter() - .map(|p| { - let name = Ident::new(&p.name, Span::call_site()); - let start = &p.start; - let end = &p.end; - quote!(#name, #start, #end) - }) - .collect(); - let param_names: Vec = params - .iter() - .map(|p| { - let name = Ident::new(&p.name, Span::call_site()); - quote!(#name) - }) - .collect(); - UnrolledParams { param_ranges, param_names } - } + /// Constructs an [`UnrolledParams`] from a [`Vec`] + fn from(params: &Vec) -> UnrolledParams { + let param_ranges: Vec = params + .iter() + .map(|p| { + let name = Ident::new(&p.name, Span::call_site()); + let start = &p.start; + let end = &p.end; + quote!(#name, #start, #end) + }) + .collect(); + let param_names: Vec = params + .iter() + .map(|p| { + let name = Ident::new(&p.name, Span::call_site()); + quote!(#name) + }) + .collect(); + UnrolledParams { + param_ranges, + param_names, + } + } } /// Performs expansion of an already-parsed [`BenchmarkDef`]. fn expand_benchmark( - benchmark_def: BenchmarkDef, - name: &Ident, - is_instance: bool, - where_clause: TokenStream2, + benchmark_def: BenchmarkDef, + name: &Ident, + is_instance: bool, + where_clause: TokenStream2, ) -> TokenStream2 { - // set up variables needed during quoting - let krate = match generate_access_from_frame_or_crate("frame-benchmarking") { - Ok(ident) => ident, - Err(err) => return err.to_compile_error().into(), - }; - let frame_system = match generate_access_from_frame_or_crate("frame-system") { - Ok(path) => path, - Err(err) => return err.to_compile_error().into(), - }; - let codec = quote!(#krate::__private::codec); - let traits = quote!(#krate::__private::traits); - let setup_stmts = benchmark_def.setup_stmts; - let verify_stmts = benchmark_def.verify_stmts; - let last_stmt = benchmark_def.last_stmt; - let test_ident = - Ident::new(format!("test_benchmark_{}", name.to_string()).as_str(), Span::call_site()); - - // unroll params (prepare for quoting) - let unrolled = UnrolledParams::from(&benchmark_def.params); - let param_names = unrolled.param_names; - let param_ranges = unrolled.param_ranges; - - let type_use_generics = match is_instance { - false => quote!(T), - true => quote!(T, I), - }; - - let type_impl_generics = match is_instance { - false => quote!(T: Config), - true => quote!(T: Config, I: 'static), - }; - - // used in the benchmarking impls - let (pre_call, post_call, fn_call_body) = match &benchmark_def.call_def { - BenchmarkCallDef::ExtrinsicCall { origin, expr_call, attr_span: _ } => { - let mut expr_call = expr_call.clone(); - - // remove first arg from expr_call - let mut final_args = Punctuated::::new(); - let args: Vec<&Expr> = expr_call.args.iter().collect(); - for arg in &args[1..] { - final_args.push((*(*arg)).clone()); - } - expr_call.args = final_args; - - let origin = match origin { - Expr::Cast(t) => { - let ty = t.ty.clone(); - quote! { - <::RuntimeOrigin as From<#ty>>::from(#origin); - } - }, - _ => quote! { - #origin.into(); - }, - }; - - // determine call name (handles `_` and normal call syntax) - let expr_span = expr_call.span(); - let call_err = || { - syn::Error::new(expr_span, "Extrinsic call must be a function call or `_`") - .to_compile_error() - }; - let call_name = match *expr_call.func { - Expr::Path(expr_path) => { - // normal function call - let Some(segment) = expr_path.path.segments.last() else { return call_err() }; - segment.ident.to_string() - }, - Expr::Infer(_) => { - // `_` style - // replace `_` with fn name - name.to_string() - }, - _ => return call_err(), - }; - - // modify extrinsic call to be prefixed with "new_call_variant" - let call_name = format!("new_call_variant_{}", call_name); - let mut punct: Punctuated = Punctuated::new(); - punct.push(PathSegment { - arguments: PathArguments::None, - ident: Ident::new(call_name.as_str(), Span::call_site()), - }); - *expr_call.func = Expr::Path(ExprPath { - attrs: vec![], - qself: None, - path: Path { leading_colon: None, segments: punct }, - }); - let pre_call = quote! { - let __call = Call::<#type_use_generics>::#expr_call; - let __benchmarked_call_encoded = #codec::Encode::encode(&__call); - }; - let post_call = quote! { - let __call_decoded = as #codec::Decode> - ::decode(&mut &__benchmarked_call_encoded[..]) - .expect("call is encoded above, encoding must be correct"); - let __origin = #origin; - as #traits::UnfilteredDispatchable>::dispatch_bypass_filter( - __call_decoded, - __origin, - ) - }; - ( - // (pre_call, post_call, fn_call_body): - pre_call.clone(), - quote!(#post_call?;), - quote! { - #pre_call - #post_call.unwrap(); - }, - ) - }, - BenchmarkCallDef::Block { block, attr_span: _ } => - (quote!(), quote!(#block), quote!(#block)), - }; - - let vis = benchmark_def.fn_vis; - - // remove #[benchmark] attribute - let fn_attrs = benchmark_def - .fn_attrs - .iter() - .filter(|attr| !attr.path().is_ident(keywords::BENCHMARK_TOKEN)); - - // modify signature generics, ident, and inputs, e.g: - // before: `fn bench(u: Linear<1, 100>) -> Result<(), BenchmarkError>` - // after: `fn _bench , I: 'static>(u: u32, verify: bool) -> Result<(), - // BenchmarkError>` - let mut sig = benchmark_def.fn_sig; - sig.generics = parse_quote!(<#type_impl_generics>); - if !where_clause.is_empty() { - sig.generics.where_clause = parse_quote!(where #where_clause); - } - sig.ident = - Ident::new(format!("_{}", name.to_token_stream().to_string()).as_str(), Span::call_site()); - let mut fn_param_inputs: Vec = - param_names.iter().map(|name| quote!(#name: u32)).collect(); - fn_param_inputs.push(quote!(verify: bool)); - sig.inputs = parse_quote!(#(#fn_param_inputs),*); - - // used in instance() impl - let impl_last_stmt = match &last_stmt { - Some(stmt) => quote!(#stmt), - None => quote!(Ok(())), - }; - let fn_attrs_clone = fn_attrs.clone(); - - let fn_def = quote! { - #( - #fn_attrs_clone - )* - #vis #sig { - #( - #setup_stmts - )* - #fn_call_body - if verify { - #( - #verify_stmts - )* - } - #last_stmt - } - }; - - // generate final quoted tokens - let res = quote! { - // benchmark function definition - #fn_def - - #[allow(non_camel_case_types)] - #( - #fn_attrs - )* - struct #name; - - #[allow(unused_variables)] - impl<#type_impl_generics> #krate::BenchmarkingSetup<#type_use_generics> - for #name where #where_clause { - fn components(&self) -> #krate::__private::Vec<(#krate::BenchmarkParameter, u32, u32)> { - #krate::__private::vec! [ - #( - (#krate::BenchmarkParameter::#param_ranges) - ),* - ] - } - - fn instance( - &self, - components: &[(#krate::BenchmarkParameter, u32)], - verify: bool - ) -> Result<#krate::__private::Box Result<(), #krate::BenchmarkError>>, #krate::BenchmarkError> { - #( - // prepare instance #param_names - let #param_names = components.iter() - .find(|&c| c.0 == #krate::BenchmarkParameter::#param_names) - .ok_or("Could not find component during benchmark preparation.")? - .1; - )* - - // benchmark setup code - #( - #setup_stmts - )* - #pre_call - Ok(#krate::__private::Box::new(move || -> Result<(), #krate::BenchmarkError> { - #post_call - if verify { - #( - #verify_stmts - )* - } - #impl_last_stmt - })) - } - } - - #[cfg(test)] - impl<#type_impl_generics> Pallet<#type_use_generics> where T: #frame_system::Config, #where_clause { - #[allow(unused)] - fn #test_ident() -> Result<(), #krate::BenchmarkError> { - let selected_benchmark = SelectedBenchmark::#name; - let components = < - SelectedBenchmark as #krate::BenchmarkingSetup - >::components(&selected_benchmark); - let execute_benchmark = | - c: #krate::__private::Vec<(#krate::BenchmarkParameter, u32)> - | -> Result<(), #krate::BenchmarkError> { - // Always reset the state after the benchmark. - #krate::__private::defer!(#krate::benchmarking::wipe_db()); - - // Set up the benchmark, return execution + verification function. - let closure_to_verify = < - SelectedBenchmark as #krate::BenchmarkingSetup - >::instance(&selected_benchmark, &c, true)?; - - // Set the block number to at least 1 so events are deposited. - if #krate::__private::Zero::is_zero(&#frame_system::Pallet::::block_number()) { - #frame_system::Pallet::::set_block_number(1u32.into()); - } - - // Run execution + verification - closure_to_verify() - }; - - if components.is_empty() { - execute_benchmark(Default::default())?; - } else { - let num_values: u32 = if let Ok(ev) = std::env::var("VALUES_PER_COMPONENT") { - ev.parse().map_err(|_| { - #krate::BenchmarkError::Stop( - "Could not parse env var `VALUES_PER_COMPONENT` as u32." - ) - })? - } else { - 6 - }; - - if num_values < 2 { - return Err("`VALUES_PER_COMPONENT` must be at least 2".into()); - } - - for (name, low, high) in components.clone().into_iter() { - // Test the lowest, highest (if its different from the lowest) - // and up to num_values-2 more equidistant values in between. - // For 0..10 and num_values=6 this would mean: [0, 2, 4, 6, 8, 10] - if high < low { - return Err("The start of a `ParamRange` must be less than or equal to the end".into()); - } - - let mut values = #krate::__private::vec![low]; - let diff = (high - low).min(num_values - 1); - let slope = (high - low) as f32 / diff as f32; - - for i in 1..=diff { - let value = ((low as f32 + slope * i as f32) as u32) - .clamp(low, high); - values.push(value); - } - - for component_value in values { - // Select the max value for all the other components. - let c: #krate::__private::Vec<(#krate::BenchmarkParameter, u32)> = components - .iter() - .map(|(n, _, h)| - if *n == name { - (*n, component_value) - } else { - (*n, *h) - } - ) - .collect(); - - execute_benchmark(c)?; - } - } - } - return Ok(()); - } - } - }; - res + // set up variables needed during quoting + let krate = match generate_access_from_frame_or_crate("frame-benchmarking") { + Ok(ident) => ident, + Err(err) => return err.to_compile_error().into(), + }; + let frame_system = match generate_access_from_frame_or_crate("frame-system") { + Ok(path) => path, + Err(err) => return err.to_compile_error().into(), + }; + let codec = quote!(#krate::__private::codec); + let traits = quote!(#krate::__private::traits); + let setup_stmts = benchmark_def.setup_stmts; + let verify_stmts = benchmark_def.verify_stmts; + let last_stmt = benchmark_def.last_stmt; + let test_ident = Ident::new( + format!("test_benchmark_{}", name.to_string()).as_str(), + Span::call_site(), + ); + + // unroll params (prepare for quoting) + let unrolled = UnrolledParams::from(&benchmark_def.params); + let param_names = unrolled.param_names; + let param_ranges = unrolled.param_ranges; + + let type_use_generics = match is_instance { + false => quote!(T), + true => quote!(T, I), + }; + + let type_impl_generics = match is_instance { + false => quote!(T: Config), + true => quote!(T: Config, I: 'static), + }; + + // used in the benchmarking impls + let (pre_call, post_call, fn_call_body) = match &benchmark_def.call_def { + BenchmarkCallDef::ExtrinsicCall { + origin, + expr_call, + attr_span: _, + } => { + let mut expr_call = expr_call.clone(); + + // remove first arg from expr_call + let mut final_args = Punctuated::::new(); + let args: Vec<&Expr> = expr_call.args.iter().collect(); + for arg in &args[1..] { + final_args.push((*(*arg)).clone()); + } + expr_call.args = final_args; + + let origin = match origin { + Expr::Cast(t) => { + let ty = t.ty.clone(); + quote! { + <::RuntimeOrigin as From<#ty>>::from(#origin); + } + } + _ => quote! { + #origin.into(); + }, + }; + + // determine call name (handles `_` and normal call syntax) + let expr_span = expr_call.span(); + let call_err = || { + syn::Error::new(expr_span, "Extrinsic call must be a function call or `_`") + .to_compile_error() + }; + let call_name = match *expr_call.func { + Expr::Path(expr_path) => { + // normal function call + let Some(segment) = expr_path.path.segments.last() else { + return call_err(); + }; + segment.ident.to_string() + } + Expr::Infer(_) => { + // `_` style + // replace `_` with fn name + name.to_string() + } + _ => return call_err(), + }; + + // modify extrinsic call to be prefixed with "new_call_variant" + let call_name = format!("new_call_variant_{}", call_name); + let mut punct: Punctuated = Punctuated::new(); + punct.push(PathSegment { + arguments: PathArguments::None, + ident: Ident::new(call_name.as_str(), Span::call_site()), + }); + *expr_call.func = Expr::Path(ExprPath { + attrs: vec![], + qself: None, + path: Path { + leading_colon: None, + segments: punct, + }, + }); + let pre_call = quote! { + let __call = Call::<#type_use_generics>::#expr_call; + let __benchmarked_call_encoded = #codec::Encode::encode(&__call); + }; + let post_call = quote! { + let __call_decoded = as #codec::Decode> + ::decode(&mut &__benchmarked_call_encoded[..]) + .expect("call is encoded above, encoding must be correct"); + let __origin = #origin; + as #traits::UnfilteredDispatchable>::dispatch_bypass_filter( + __call_decoded, + __origin, + ) + }; + ( + // (pre_call, post_call, fn_call_body): + pre_call.clone(), + quote!(#post_call?;), + quote! { + #pre_call + #post_call.unwrap(); + }, + ) + } + BenchmarkCallDef::Block { + block, + attr_span: _, + } => (quote!(), quote!(#block), quote!(#block)), + }; + + let vis = benchmark_def.fn_vis; + + // remove #[benchmark] attribute + let fn_attrs = benchmark_def + .fn_attrs + .iter() + .filter(|attr| !attr.path().is_ident(keywords::BENCHMARK_TOKEN)); + + // modify signature generics, ident, and inputs, e.g: + // before: `fn bench(u: Linear<1, 100>) -> Result<(), BenchmarkError>` + // after: `fn _bench , I: 'static>(u: u32, verify: bool) -> Result<(), + // BenchmarkError>` + let mut sig = benchmark_def.fn_sig; + sig.generics = parse_quote!(<#type_impl_generics>); + if !where_clause.is_empty() { + sig.generics.where_clause = parse_quote!(where #where_clause); + } + sig.ident = Ident::new( + format!("_{}", name.to_token_stream().to_string()).as_str(), + Span::call_site(), + ); + let mut fn_param_inputs: Vec = + param_names.iter().map(|name| quote!(#name: u32)).collect(); + fn_param_inputs.push(quote!(verify: bool)); + sig.inputs = parse_quote!(#(#fn_param_inputs),*); + + // used in instance() impl + let impl_last_stmt = match &last_stmt { + Some(stmt) => quote!(#stmt), + None => quote!(Ok(())), + }; + let fn_attrs_clone = fn_attrs.clone(); + + let fn_def = quote! { + #( + #fn_attrs_clone + )* + #vis #sig { + #( + #setup_stmts + )* + #fn_call_body + if verify { + #( + #verify_stmts + )* + } + #last_stmt + } + }; + + // generate final quoted tokens + let res = quote! { + // benchmark function definition + #fn_def + + #[allow(non_camel_case_types)] + #( + #fn_attrs + )* + struct #name; + + #[allow(unused_variables)] + impl<#type_impl_generics> #krate::BenchmarkingSetup<#type_use_generics> + for #name where #where_clause { + fn components(&self) -> #krate::__private::Vec<(#krate::BenchmarkParameter, u32, u32)> { + #krate::__private::vec! [ + #( + (#krate::BenchmarkParameter::#param_ranges) + ),* + ] + } + + fn instance( + &self, + components: &[(#krate::BenchmarkParameter, u32)], + verify: bool + ) -> Result<#krate::__private::Box Result<(), #krate::BenchmarkError>>, #krate::BenchmarkError> { + #( + // prepare instance #param_names + let #param_names = components.iter() + .find(|&c| c.0 == #krate::BenchmarkParameter::#param_names) + .ok_or("Could not find component during benchmark preparation.")? + .1; + )* + + // benchmark setup code + #( + #setup_stmts + )* + #pre_call + Ok(#krate::__private::Box::new(move || -> Result<(), #krate::BenchmarkError> { + #post_call + if verify { + #( + #verify_stmts + )* + } + #impl_last_stmt + })) + } + } + + #[cfg(test)] + impl<#type_impl_generics> Pallet<#type_use_generics> where T: #frame_system::Config, #where_clause { + #[allow(unused)] + fn #test_ident() -> Result<(), #krate::BenchmarkError> { + let selected_benchmark = SelectedBenchmark::#name; + let components = < + SelectedBenchmark as #krate::BenchmarkingSetup + >::components(&selected_benchmark); + let execute_benchmark = | + c: #krate::__private::Vec<(#krate::BenchmarkParameter, u32)> + | -> Result<(), #krate::BenchmarkError> { + // Always reset the state after the benchmark. + #krate::__private::defer!(#krate::benchmarking::wipe_db()); + + // Set up the benchmark, return execution + verification function. + let closure_to_verify = < + SelectedBenchmark as #krate::BenchmarkingSetup + >::instance(&selected_benchmark, &c, true)?; + + // Set the block number to at least 1 so events are deposited. + if #krate::__private::Zero::is_zero(&#frame_system::Pallet::::block_number()) { + #frame_system::Pallet::::set_block_number(1u32.into()); + } + + // Run execution + verification + closure_to_verify() + }; + + if components.is_empty() { + execute_benchmark(Default::default())?; + } else { + let num_values: u32 = if let Ok(ev) = std::env::var("VALUES_PER_COMPONENT") { + ev.parse().map_err(|_| { + #krate::BenchmarkError::Stop( + "Could not parse env var `VALUES_PER_COMPONENT` as u32." + ) + })? + } else { + 6 + }; + + if num_values < 2 { + return Err("`VALUES_PER_COMPONENT` must be at least 2".into()); + } + + for (name, low, high) in components.clone().into_iter() { + // Test the lowest, highest (if its different from the lowest) + // and up to num_values-2 more equidistant values in between. + // For 0..10 and num_values=6 this would mean: [0, 2, 4, 6, 8, 10] + if high < low { + return Err("The start of a `ParamRange` must be less than or equal to the end".into()); + } + + let mut values = #krate::__private::vec![low]; + let diff = (high - low).min(num_values - 1); + let slope = (high - low) as f32 / diff as f32; + + for i in 1..=diff { + let value = ((low as f32 + slope * i as f32) as u32) + .clamp(low, high); + values.push(value); + } + + for component_value in values { + // Select the max value for all the other components. + let c: #krate::__private::Vec<(#krate::BenchmarkParameter, u32)> = components + .iter() + .map(|(n, _, h)| + if *n == name { + (*n, component_value) + } else { + (*n, *h) + } + ) + .collect(); + + execute_benchmark(c)?; + } + } + } + return Ok(()); + } + } + }; + res } diff --git a/support/procedural-fork/src/construct_runtime/expand/call.rs b/support/procedural-fork/src/construct_runtime/expand/call.rs index b0041ccc0..7e8c2e856 100644 --- a/support/procedural-fork/src/construct_runtime/expand/call.rs +++ b/support/procedural-fork/src/construct_runtime/expand/call.rs @@ -22,202 +22,205 @@ use std::str::FromStr; use syn::Ident; pub fn expand_outer_dispatch( - runtime: &Ident, - system_pallet: &Pallet, - pallet_decls: &[Pallet], - scrate: &TokenStream, + runtime: &Ident, + system_pallet: &Pallet, + pallet_decls: &[Pallet], + scrate: &TokenStream, ) -> TokenStream { - let mut variant_defs = TokenStream::new(); - let mut variant_patterns = Vec::new(); - let mut query_call_part_macros = Vec::new(); - let mut pallet_names = Vec::new(); - let mut pallet_attrs = Vec::new(); - let system_path = &system_pallet.path; - - let pallets_with_call = pallet_decls.iter().filter(|decl| decl.exists_part("Call")); - - for pallet_declaration in pallets_with_call { - let name = &pallet_declaration.name; - let path = &pallet_declaration.path; - let index = pallet_declaration.index; - let attr = - pallet_declaration.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { - let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) - .expect("was successfully parsed before; qed"); - quote! { - #acc - #attr - } - }); - - variant_defs.extend(quote! { - #attr - #[codec(index = #index)] - #name( #scrate::dispatch::CallableCallFor<#name, #runtime> ), - }); - variant_patterns.push(quote!(RuntimeCall::#name(call))); - pallet_names.push(name); - pallet_attrs.push(attr); - query_call_part_macros.push(quote! { - #path::__substrate_call_check::is_call_part_defined!(#name); - }); - } - - quote! { - #( #query_call_part_macros )* - - #[derive( - Clone, PartialEq, Eq, - #scrate::__private::codec::Encode, - #scrate::__private::codec::Decode, - #scrate::__private::scale_info::TypeInfo, - #scrate::__private::RuntimeDebug, - )] - pub enum RuntimeCall { - #variant_defs - } - #[cfg(test)] - impl RuntimeCall { - /// Return a list of the module names together with their size in memory. - pub const fn sizes() -> &'static [( &'static str, usize )] { - use #scrate::dispatch::Callable; - use core::mem::size_of; - &[#( - #pallet_attrs - ( - stringify!(#pallet_names), - size_of::< <#pallet_names as Callable<#runtime>>::RuntimeCall >(), - ), - )*] - } - - /// Panics with diagnostic information if the size is greater than the given `limit`. - pub fn assert_size_under(limit: usize) { - let size = core::mem::size_of::(); - let call_oversize = size > limit; - if call_oversize { - println!("Size of `Call` is {} bytes (provided limit is {} bytes)", size, limit); - let mut sizes = Self::sizes().to_vec(); - sizes.sort_by_key(|x| -(x.1 as isize)); - for (i, &(name, size)) in sizes.iter().enumerate().take(5) { - println!("Offender #{}: {} at {} bytes", i + 1, name, size); - } - if let Some((_, next_size)) = sizes.get(5) { - println!("{} others of size {} bytes or less", sizes.len() - 5, next_size); - } - panic!( - "Size of `Call` is more than limit; use `Box` on complex parameter types to reduce the + let mut variant_defs = TokenStream::new(); + let mut variant_patterns = Vec::new(); + let mut query_call_part_macros = Vec::new(); + let mut pallet_names = Vec::new(); + let mut pallet_attrs = Vec::new(); + let system_path = &system_pallet.path; + + let pallets_with_call = pallet_decls.iter().filter(|decl| decl.exists_part("Call")); + + for pallet_declaration in pallets_with_call { + let name = &pallet_declaration.name; + let path = &pallet_declaration.path; + let index = pallet_declaration.index; + let attr = + pallet_declaration + .cfg_pattern + .iter() + .fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); + + variant_defs.extend(quote! { + #attr + #[codec(index = #index)] + #name( #scrate::dispatch::CallableCallFor<#name, #runtime> ), + }); + variant_patterns.push(quote!(RuntimeCall::#name(call))); + pallet_names.push(name); + pallet_attrs.push(attr); + query_call_part_macros.push(quote! { + #path::__substrate_call_check::is_call_part_defined!(#name); + }); + } + + quote! { + #( #query_call_part_macros )* + + #[derive( + Clone, PartialEq, Eq, + #scrate::__private::codec::Encode, + #scrate::__private::codec::Decode, + #scrate::__private::scale_info::TypeInfo, + #scrate::__private::RuntimeDebug, + )] + pub enum RuntimeCall { + #variant_defs + } + #[cfg(test)] + impl RuntimeCall { + /// Return a list of the module names together with their size in memory. + pub const fn sizes() -> &'static [( &'static str, usize )] { + use #scrate::dispatch::Callable; + use core::mem::size_of; + &[#( + #pallet_attrs + ( + stringify!(#pallet_names), + size_of::< <#pallet_names as Callable<#runtime>>::RuntimeCall >(), + ), + )*] + } + + /// Panics with diagnostic information if the size is greater than the given `limit`. + pub fn assert_size_under(limit: usize) { + let size = core::mem::size_of::(); + let call_oversize = size > limit; + if call_oversize { + println!("Size of `Call` is {} bytes (provided limit is {} bytes)", size, limit); + let mut sizes = Self::sizes().to_vec(); + sizes.sort_by_key(|x| -(x.1 as isize)); + for (i, &(name, size)) in sizes.iter().enumerate().take(5) { + println!("Offender #{}: {} at {} bytes", i + 1, name, size); + } + if let Some((_, next_size)) = sizes.get(5) { + println!("{} others of size {} bytes or less", sizes.len() - 5, next_size); + } + panic!( + "Size of `Call` is more than limit; use `Box` on complex parameter types to reduce the size of `Call`. If the limit is too strong, maybe consider providing a higher limit." - ); - } - } - } - impl #scrate::dispatch::GetDispatchInfo for RuntimeCall { - fn get_dispatch_info(&self) -> #scrate::dispatch::DispatchInfo { - match self { - #( - #pallet_attrs - #variant_patterns => call.get_dispatch_info(), - )* - } - } - } - - impl #scrate::dispatch::CheckIfFeeless for RuntimeCall { - type Origin = #system_path::pallet_prelude::OriginFor<#runtime>; - fn is_feeless(&self, origin: &Self::Origin) -> bool { - match self { - #( - #pallet_attrs - #variant_patterns => call.is_feeless(origin), - )* - } - } - } - - impl #scrate::traits::GetCallMetadata for RuntimeCall { - fn get_call_metadata(&self) -> #scrate::traits::CallMetadata { - use #scrate::traits::GetCallName; - match self { - #( - #pallet_attrs - #variant_patterns => { - let function_name = call.get_call_name(); - let pallet_name = stringify!(#pallet_names); - #scrate::traits::CallMetadata { function_name, pallet_name } - } - )* - } - } - - fn get_module_names() -> &'static [&'static str] { - &[#( - #pallet_attrs - stringify!(#pallet_names), - )*] - } - - fn get_call_names(module: &str) -> &'static [&'static str] { - use #scrate::{dispatch::Callable, traits::GetCallName}; - match module { - #( - #pallet_attrs - stringify!(#pallet_names) => - <<#pallet_names as Callable<#runtime>>::RuntimeCall - as GetCallName>::get_call_names(), - )* - _ => unreachable!(), - } - } - } - impl #scrate::__private::Dispatchable for RuntimeCall { - type RuntimeOrigin = RuntimeOrigin; - type Config = RuntimeCall; - type Info = #scrate::dispatch::DispatchInfo; - type PostInfo = #scrate::dispatch::PostDispatchInfo; - fn dispatch(self, origin: RuntimeOrigin) -> #scrate::dispatch::DispatchResultWithPostInfo { - if !::filter_call(&origin, &self) { - return ::core::result::Result::Err( - #system_path::Error::<#runtime>::CallFiltered.into() - ); - } - - #scrate::traits::UnfilteredDispatchable::dispatch_bypass_filter(self, origin) - } - } - impl #scrate::traits::UnfilteredDispatchable for RuntimeCall { - type RuntimeOrigin = RuntimeOrigin; - fn dispatch_bypass_filter(self, origin: RuntimeOrigin) -> #scrate::dispatch::DispatchResultWithPostInfo { - match self { - #( - #pallet_attrs - #variant_patterns => - #scrate::traits::UnfilteredDispatchable::dispatch_bypass_filter(call, origin), - )* - } - } - } - - #( - #pallet_attrs - impl #scrate::traits::IsSubType<#scrate::dispatch::CallableCallFor<#pallet_names, #runtime>> for RuntimeCall { - #[allow(unreachable_patterns)] - fn is_sub_type(&self) -> Option<&#scrate::dispatch::CallableCallFor<#pallet_names, #runtime>> { - match self { - #variant_patterns => Some(call), - // May be unreachable - _ => None, - } - } - } - - #pallet_attrs - impl From<#scrate::dispatch::CallableCallFor<#pallet_names, #runtime>> for RuntimeCall { - fn from(call: #scrate::dispatch::CallableCallFor<#pallet_names, #runtime>) -> Self { - #variant_patterns - } - } - )* - } + ); + } + } + } + impl #scrate::dispatch::GetDispatchInfo for RuntimeCall { + fn get_dispatch_info(&self) -> #scrate::dispatch::DispatchInfo { + match self { + #( + #pallet_attrs + #variant_patterns => call.get_dispatch_info(), + )* + } + } + } + + impl #scrate::dispatch::CheckIfFeeless for RuntimeCall { + type Origin = #system_path::pallet_prelude::OriginFor<#runtime>; + fn is_feeless(&self, origin: &Self::Origin) -> bool { + match self { + #( + #pallet_attrs + #variant_patterns => call.is_feeless(origin), + )* + } + } + } + + impl #scrate::traits::GetCallMetadata for RuntimeCall { + fn get_call_metadata(&self) -> #scrate::traits::CallMetadata { + use #scrate::traits::GetCallName; + match self { + #( + #pallet_attrs + #variant_patterns => { + let function_name = call.get_call_name(); + let pallet_name = stringify!(#pallet_names); + #scrate::traits::CallMetadata { function_name, pallet_name } + } + )* + } + } + + fn get_module_names() -> &'static [&'static str] { + &[#( + #pallet_attrs + stringify!(#pallet_names), + )*] + } + + fn get_call_names(module: &str) -> &'static [&'static str] { + use #scrate::{dispatch::Callable, traits::GetCallName}; + match module { + #( + #pallet_attrs + stringify!(#pallet_names) => + <<#pallet_names as Callable<#runtime>>::RuntimeCall + as GetCallName>::get_call_names(), + )* + _ => unreachable!(), + } + } + } + impl #scrate::__private::Dispatchable for RuntimeCall { + type RuntimeOrigin = RuntimeOrigin; + type Config = RuntimeCall; + type Info = #scrate::dispatch::DispatchInfo; + type PostInfo = #scrate::dispatch::PostDispatchInfo; + fn dispatch(self, origin: RuntimeOrigin) -> #scrate::dispatch::DispatchResultWithPostInfo { + if !::filter_call(&origin, &self) { + return ::core::result::Result::Err( + #system_path::Error::<#runtime>::CallFiltered.into() + ); + } + + #scrate::traits::UnfilteredDispatchable::dispatch_bypass_filter(self, origin) + } + } + impl #scrate::traits::UnfilteredDispatchable for RuntimeCall { + type RuntimeOrigin = RuntimeOrigin; + fn dispatch_bypass_filter(self, origin: RuntimeOrigin) -> #scrate::dispatch::DispatchResultWithPostInfo { + match self { + #( + #pallet_attrs + #variant_patterns => + #scrate::traits::UnfilteredDispatchable::dispatch_bypass_filter(call, origin), + )* + } + } + } + + #( + #pallet_attrs + impl #scrate::traits::IsSubType<#scrate::dispatch::CallableCallFor<#pallet_names, #runtime>> for RuntimeCall { + #[allow(unreachable_patterns)] + fn is_sub_type(&self) -> Option<&#scrate::dispatch::CallableCallFor<#pallet_names, #runtime>> { + match self { + #variant_patterns => Some(call), + // May be unreachable + _ => None, + } + } + } + + #pallet_attrs + impl From<#scrate::dispatch::CallableCallFor<#pallet_names, #runtime>> for RuntimeCall { + fn from(call: #scrate::dispatch::CallableCallFor<#pallet_names, #runtime>) -> Self { + #variant_patterns + } + } + )* + } } diff --git a/support/procedural-fork/src/construct_runtime/expand/composite_helper.rs b/support/procedural-fork/src/construct_runtime/expand/composite_helper.rs index 101a476fb..be6b2f085 100644 --- a/support/procedural-fork/src/construct_runtime/expand/composite_helper.rs +++ b/support/procedural-fork/src/construct_runtime/expand/composite_helper.rs @@ -20,82 +20,82 @@ use proc_macro2::{Ident, TokenStream}; use quote::quote; pub(crate) fn expand_conversion_fn( - composite_name: &str, - path: &PalletPath, - instance: Option<&Ident>, - variant_name: &Ident, + composite_name: &str, + path: &PalletPath, + instance: Option<&Ident>, + variant_name: &Ident, ) -> TokenStream { - let composite_name = quote::format_ident!("{}", composite_name); - let runtime_composite_name = quote::format_ident!("Runtime{}", composite_name); + let composite_name = quote::format_ident!("{}", composite_name); + let runtime_composite_name = quote::format_ident!("Runtime{}", composite_name); - if let Some(inst) = instance { - quote! { - impl From<#path::#composite_name<#path::#inst>> for #runtime_composite_name { - fn from(hr: #path::#composite_name<#path::#inst>) -> Self { - #runtime_composite_name::#variant_name(hr) - } - } - } - } else { - quote! { - impl From<#path::#composite_name> for #runtime_composite_name { - fn from(hr: #path::#composite_name) -> Self { - #runtime_composite_name::#variant_name(hr) - } - } - } - } + if let Some(inst) = instance { + quote! { + impl From<#path::#composite_name<#path::#inst>> for #runtime_composite_name { + fn from(hr: #path::#composite_name<#path::#inst>) -> Self { + #runtime_composite_name::#variant_name(hr) + } + } + } + } else { + quote! { + impl From<#path::#composite_name> for #runtime_composite_name { + fn from(hr: #path::#composite_name) -> Self { + #runtime_composite_name::#variant_name(hr) + } + } + } + } } pub(crate) fn expand_variant( - composite_name: &str, - index: u8, - path: &PalletPath, - instance: Option<&Ident>, - variant_name: &Ident, + composite_name: &str, + index: u8, + path: &PalletPath, + instance: Option<&Ident>, + variant_name: &Ident, ) -> TokenStream { - let composite_name = quote::format_ident!("{}", composite_name); + let composite_name = quote::format_ident!("{}", composite_name); - if let Some(inst) = instance { - quote! { - #[codec(index = #index)] - #variant_name(#path::#composite_name<#path::#inst>), - } - } else { - quote! { - #[codec(index = #index)] - #variant_name(#path::#composite_name), - } - } + if let Some(inst) = instance { + quote! { + #[codec(index = #index)] + #variant_name(#path::#composite_name<#path::#inst>), + } + } else { + quote! { + #[codec(index = #index)] + #variant_name(#path::#composite_name), + } + } } pub(crate) fn expand_variant_count( - composite_name: &str, - path: &PalletPath, - instance: Option<&Ident>, + composite_name: &str, + path: &PalletPath, + instance: Option<&Ident>, ) -> TokenStream { - let composite_name = quote::format_ident!("{}", composite_name); + let composite_name = quote::format_ident!("{}", composite_name); - if let Some(inst) = instance { - quote! { - #path::#composite_name::<#path::#inst>::VARIANT_COUNT - } - } else { - // Wrapped `<`..`>` means: use default type parameter for enum. - // - // This is used for pallets without instance support or pallets with instance support when - // we don't specify instance: - // - // ``` - // pub struct Pallet{..} - // - // #[pallet::composite_enum] - // pub enum HoldReason {..} - // - // Pallet1: pallet_x, // <- default type parameter - // ``` - quote! { - <#path::#composite_name>::VARIANT_COUNT - } - } + if let Some(inst) = instance { + quote! { + #path::#composite_name::<#path::#inst>::VARIANT_COUNT + } + } else { + // Wrapped `<`..`>` means: use default type parameter for enum. + // + // This is used for pallets without instance support or pallets with instance support when + // we don't specify instance: + // + // ``` + // pub struct Pallet{..} + // + // #[pallet::composite_enum] + // pub enum HoldReason {..} + // + // Pallet1: pallet_x, // <- default type parameter + // ``` + quote! { + <#path::#composite_name>::VARIANT_COUNT + } + } } diff --git a/support/procedural-fork/src/construct_runtime/expand/config.rs b/support/procedural-fork/src/construct_runtime/expand/config.rs index dbbe6ba6e..ff715e584 100644 --- a/support/procedural-fork/src/construct_runtime/expand/config.rs +++ b/support/procedural-fork/src/construct_runtime/expand/config.rs @@ -23,125 +23,135 @@ use std::str::FromStr; use syn::Ident; pub fn expand_outer_config( - runtime: &Ident, - pallet_decls: &[Pallet], - scrate: &TokenStream, + runtime: &Ident, + pallet_decls: &[Pallet], + scrate: &TokenStream, ) -> TokenStream { - let mut types = TokenStream::new(); - let mut fields = TokenStream::new(); - let mut genesis_build_calls = TokenStream::new(); - let mut query_genesis_config_part_macros = Vec::new(); + let mut types = TokenStream::new(); + let mut fields = TokenStream::new(); + let mut genesis_build_calls = TokenStream::new(); + let mut query_genesis_config_part_macros = Vec::new(); - for decl in pallet_decls { - if let Some(pallet_entry) = decl.find_part("Config") { - let path = &decl.path; - let pallet_name = &decl.name; - let path_str = path.into_token_stream().to_string(); - let config = format_ident!("{}Config", pallet_name); - let field_name = - &Ident::new(&pallet_name.to_string().to_snake_case(), decl.name.span()); - let part_is_generic = !pallet_entry.generics.params.is_empty(); - let attr = &decl.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { - let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) - .expect("was successfully parsed before; qed"); - quote! { - #acc - #attr - } - }); + for decl in pallet_decls { + if let Some(pallet_entry) = decl.find_part("Config") { + let path = &decl.path; + let pallet_name = &decl.name; + let path_str = path.into_token_stream().to_string(); + let config = format_ident!("{}Config", pallet_name); + let field_name = + &Ident::new(&pallet_name.to_string().to_snake_case(), decl.name.span()); + let part_is_generic = !pallet_entry.generics.params.is_empty(); + let attr = &decl + .cfg_pattern + .iter() + .fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); - types.extend(expand_config_types(attr, runtime, decl, &config, part_is_generic)); - fields.extend(quote!(#attr pub #field_name: #config,)); - genesis_build_calls - .extend(expand_config_build_storage_call(scrate, &config, attr, field_name)); - query_genesis_config_part_macros.push(quote! { + types.extend(expand_config_types( + attr, + runtime, + decl, + &config, + part_is_generic, + )); + fields.extend(quote!(#attr pub #field_name: #config,)); + genesis_build_calls.extend(expand_config_build_storage_call( + scrate, &config, attr, field_name, + )); + query_genesis_config_part_macros.push(quote! { #path::__substrate_genesis_config_check::is_genesis_config_defined!(#pallet_name); #[cfg(feature = "std")] #path::__substrate_genesis_config_check::is_std_enabled_for_genesis!(#pallet_name, #path_str); }); - } - } + } + } - quote! { - #( #query_genesis_config_part_macros )* + quote! { + #( #query_genesis_config_part_macros )* - #types + #types - use #scrate::__private::serde as __genesis_config_serde_import__; - #[derive(#scrate::__private::serde::Serialize, #scrate::__private::serde::Deserialize, Default)] - #[serde(rename_all = "camelCase")] - #[serde(deny_unknown_fields)] - #[serde(crate = "__genesis_config_serde_import__")] - pub struct RuntimeGenesisConfig { - #fields - } + use #scrate::__private::serde as __genesis_config_serde_import__; + #[derive(#scrate::__private::serde::Serialize, #scrate::__private::serde::Deserialize, Default)] + #[serde(rename_all = "camelCase")] + #[serde(deny_unknown_fields)] + #[serde(crate = "__genesis_config_serde_import__")] + pub struct RuntimeGenesisConfig { + #fields + } - #[cfg(any(feature = "std", test))] - impl #scrate::sp_runtime::BuildStorage for RuntimeGenesisConfig { - fn assimilate_storage( - &self, - storage: &mut #scrate::sp_runtime::Storage, - ) -> std::result::Result<(), String> { - #scrate::__private::BasicExternalities::execute_with_storage(storage, || { - ::build(&self); - Ok(()) - }) - } - } + #[cfg(any(feature = "std", test))] + impl #scrate::sp_runtime::BuildStorage for RuntimeGenesisConfig { + fn assimilate_storage( + &self, + storage: &mut #scrate::sp_runtime::Storage, + ) -> std::result::Result<(), String> { + #scrate::__private::BasicExternalities::execute_with_storage(storage, || { + ::build(&self); + Ok(()) + }) + } + } - impl #scrate::traits::BuildGenesisConfig for RuntimeGenesisConfig { - fn build(&self) { - #genesis_build_calls - ::on_genesis(); - } - } + impl #scrate::traits::BuildGenesisConfig for RuntimeGenesisConfig { + fn build(&self) { + #genesis_build_calls + ::on_genesis(); + } + } - /// Test the `Default` derive impl of the `RuntimeGenesisConfig`. - #[cfg(test)] - #[test] - fn test_genesis_config_builds() { - #scrate::__private::sp_io::TestExternalities::default().execute_with(|| { - ::build( - &RuntimeGenesisConfig::default() - ); - }); - } - } + /// Test the `Default` derive impl of the `RuntimeGenesisConfig`. + #[cfg(test)] + #[test] + fn test_genesis_config_builds() { + #scrate::__private::sp_io::TestExternalities::default().execute_with(|| { + ::build( + &RuntimeGenesisConfig::default() + ); + }); + } + } } fn expand_config_types( - attr: &TokenStream, - runtime: &Ident, - decl: &Pallet, - config: &Ident, - part_is_generic: bool, + attr: &TokenStream, + runtime: &Ident, + decl: &Pallet, + config: &Ident, + part_is_generic: bool, ) -> TokenStream { - let path = &decl.path; + let path = &decl.path; - match (decl.instance.as_ref(), part_is_generic) { - (Some(inst), true) => quote! { - #attr - pub type #config = #path::GenesisConfig<#runtime, #path::#inst>; - }, - (None, true) => quote! { - #attr - pub type #config = #path::GenesisConfig<#runtime>; - }, - (_, false) => quote! { - #attr - pub type #config = #path::GenesisConfig; - }, - } + match (decl.instance.as_ref(), part_is_generic) { + (Some(inst), true) => quote! { + #attr + pub type #config = #path::GenesisConfig<#runtime, #path::#inst>; + }, + (None, true) => quote! { + #attr + pub type #config = #path::GenesisConfig<#runtime>; + }, + (_, false) => quote! { + #attr + pub type #config = #path::GenesisConfig; + }, + } } fn expand_config_build_storage_call( - scrate: &TokenStream, - pallet_genesis_config: &Ident, - attr: &TokenStream, - field_name: &Ident, + scrate: &TokenStream, + pallet_genesis_config: &Ident, + attr: &TokenStream, + field_name: &Ident, ) -> TokenStream { - quote! { - #attr - <#pallet_genesis_config as #scrate::traits::BuildGenesisConfig>::build(&self.#field_name); - } + quote! { + #attr + <#pallet_genesis_config as #scrate::traits::BuildGenesisConfig>::build(&self.#field_name); + } } diff --git a/support/procedural-fork/src/construct_runtime/expand/freeze_reason.rs b/support/procedural-fork/src/construct_runtime/expand/freeze_reason.rs index f12f99526..131c919ef 100644 --- a/support/procedural-fork/src/construct_runtime/expand/freeze_reason.rs +++ b/support/procedural-fork/src/construct_runtime/expand/freeze_reason.rs @@ -21,55 +21,55 @@ use proc_macro2::TokenStream; use quote::quote; pub fn expand_outer_freeze_reason(pallet_decls: &[Pallet], scrate: &TokenStream) -> TokenStream { - let mut conversion_fns = Vec::new(); - let mut freeze_reason_variants = Vec::new(); - let mut freeze_reason_variants_count = Vec::new(); - for decl in pallet_decls { - if let Some(_) = decl.find_part("FreezeReason") { - let variant_name = &decl.name; - let path = &decl.path; - let index = decl.index; - let instance = decl.instance.as_ref(); + let mut conversion_fns = Vec::new(); + let mut freeze_reason_variants = Vec::new(); + let mut freeze_reason_variants_count = Vec::new(); + for decl in pallet_decls { + if let Some(_) = decl.find_part("FreezeReason") { + let variant_name = &decl.name; + let path = &decl.path; + let index = decl.index; + let instance = decl.instance.as_ref(); - conversion_fns.push(composite_helper::expand_conversion_fn( - "FreezeReason", - path, - instance, - variant_name, - )); + conversion_fns.push(composite_helper::expand_conversion_fn( + "FreezeReason", + path, + instance, + variant_name, + )); - freeze_reason_variants.push(composite_helper::expand_variant( - "FreezeReason", - index, - path, - instance, - variant_name, - )); + freeze_reason_variants.push(composite_helper::expand_variant( + "FreezeReason", + index, + path, + instance, + variant_name, + )); - freeze_reason_variants_count.push(composite_helper::expand_variant_count( - "FreezeReason", - path, - instance, - )); - } - } + freeze_reason_variants_count.push(composite_helper::expand_variant_count( + "FreezeReason", + path, + instance, + )); + } + } - quote! { - /// A reason for placing a freeze on funds. - #[derive( - Copy, Clone, Eq, PartialEq, - #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, - #scrate::__private::scale_info::TypeInfo, - #scrate::__private::RuntimeDebug, - )] - pub enum RuntimeFreezeReason { - #( #freeze_reason_variants )* - } + quote! { + /// A reason for placing a freeze on funds. + #[derive( + Copy, Clone, Eq, PartialEq, + #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, + #scrate::__private::scale_info::TypeInfo, + #scrate::__private::RuntimeDebug, + )] + pub enum RuntimeFreezeReason { + #( #freeze_reason_variants )* + } - impl #scrate::traits::VariantCount for RuntimeFreezeReason { - const VARIANT_COUNT: u32 = 0 #( + #freeze_reason_variants_count )*; - } + impl #scrate::traits::VariantCount for RuntimeFreezeReason { + const VARIANT_COUNT: u32 = 0 #( + #freeze_reason_variants_count )*; + } - #( #conversion_fns )* - } + #( #conversion_fns )* + } } diff --git a/support/procedural-fork/src/construct_runtime/expand/hold_reason.rs b/support/procedural-fork/src/construct_runtime/expand/hold_reason.rs index cdab92712..58870a321 100644 --- a/support/procedural-fork/src/construct_runtime/expand/hold_reason.rs +++ b/support/procedural-fork/src/construct_runtime/expand/hold_reason.rs @@ -21,55 +21,55 @@ use proc_macro2::TokenStream; use quote::quote; pub fn expand_outer_hold_reason(pallet_decls: &[Pallet], scrate: &TokenStream) -> TokenStream { - let mut conversion_fns = Vec::new(); - let mut hold_reason_variants = Vec::new(); - let mut hold_reason_variants_count = Vec::new(); - for decl in pallet_decls { - if let Some(_) = decl.find_part("HoldReason") { - let variant_name = &decl.name; - let path = &decl.path; - let index = decl.index; - let instance = decl.instance.as_ref(); + let mut conversion_fns = Vec::new(); + let mut hold_reason_variants = Vec::new(); + let mut hold_reason_variants_count = Vec::new(); + for decl in pallet_decls { + if let Some(_) = decl.find_part("HoldReason") { + let variant_name = &decl.name; + let path = &decl.path; + let index = decl.index; + let instance = decl.instance.as_ref(); - conversion_fns.push(composite_helper::expand_conversion_fn( - "HoldReason", - path, - instance, - variant_name, - )); + conversion_fns.push(composite_helper::expand_conversion_fn( + "HoldReason", + path, + instance, + variant_name, + )); - hold_reason_variants.push(composite_helper::expand_variant( - "HoldReason", - index, - path, - instance, - variant_name, - )); + hold_reason_variants.push(composite_helper::expand_variant( + "HoldReason", + index, + path, + instance, + variant_name, + )); - hold_reason_variants_count.push(composite_helper::expand_variant_count( - "HoldReason", - path, - instance, - )); - } - } + hold_reason_variants_count.push(composite_helper::expand_variant_count( + "HoldReason", + path, + instance, + )); + } + } - quote! { - /// A reason for placing a hold on funds. - #[derive( - Copy, Clone, Eq, PartialEq, - #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, - #scrate::__private::scale_info::TypeInfo, - #scrate::__private::RuntimeDebug, - )] - pub enum RuntimeHoldReason { - #( #hold_reason_variants )* - } + quote! { + /// A reason for placing a hold on funds. + #[derive( + Copy, Clone, Eq, PartialEq, + #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, + #scrate::__private::scale_info::TypeInfo, + #scrate::__private::RuntimeDebug, + )] + pub enum RuntimeHoldReason { + #( #hold_reason_variants )* + } - impl #scrate::traits::VariantCount for RuntimeHoldReason { - const VARIANT_COUNT: u32 = 0 #( + #hold_reason_variants_count )*; - } + impl #scrate::traits::VariantCount for RuntimeHoldReason { + const VARIANT_COUNT: u32 = 0 #( + #hold_reason_variants_count )*; + } - #( #conversion_fns )* - } + #( #conversion_fns )* + } } diff --git a/support/procedural-fork/src/construct_runtime/expand/inherent.rs b/support/procedural-fork/src/construct_runtime/expand/inherent.rs index da483fa6c..b58d540fe 100644 --- a/support/procedural-fork/src/construct_runtime/expand/inherent.rs +++ b/support/procedural-fork/src/construct_runtime/expand/inherent.rs @@ -22,233 +22,236 @@ use std::str::FromStr; use syn::Ident; pub fn expand_outer_inherent( - runtime: &Ident, - block: &TokenStream, - unchecked_extrinsic: &TokenStream, - pallet_decls: &[Pallet], - scrate: &TokenStream, + runtime: &Ident, + block: &TokenStream, + unchecked_extrinsic: &TokenStream, + pallet_decls: &[Pallet], + scrate: &TokenStream, ) -> TokenStream { - let mut pallet_names = Vec::new(); - let mut pallet_attrs = Vec::new(); - let mut query_inherent_part_macros = Vec::new(); - - for pallet_decl in pallet_decls { - if pallet_decl.exists_part("Inherent") { - let name = &pallet_decl.name; - let path = &pallet_decl.path; - let attr = pallet_decl.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { - let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) - .expect("was successfully parsed before; qed"); - quote! { - #acc - #attr - } - }); - - pallet_names.push(name); - pallet_attrs.push(attr); - query_inherent_part_macros.push(quote! { - #path::__substrate_inherent_check::is_inherent_part_defined!(#name); - }); - } - } - - quote! { - #( #query_inherent_part_macros )* - - trait InherentDataExt { - fn create_extrinsics(&self) -> - #scrate::__private::sp_std::vec::Vec<<#block as #scrate::sp_runtime::traits::Block>::Extrinsic>; - fn check_extrinsics(&self, block: &#block) -> #scrate::inherent::CheckInherentsResult; - } - - impl InherentDataExt for #scrate::inherent::InherentData { - fn create_extrinsics(&self) -> - #scrate::__private::sp_std::vec::Vec<<#block as #scrate::sp_runtime::traits::Block>::Extrinsic> - { - use #scrate::inherent::ProvideInherent; - - let mut inherents = #scrate::__private::sp_std::vec::Vec::new(); - - #( - #pallet_attrs - if let Some(inherent) = #pallet_names::create_inherent(self) { - let inherent = <#unchecked_extrinsic as #scrate::sp_runtime::traits::Extrinsic>::new( - inherent.into(), - None, - ).expect("Runtime UncheckedExtrinsic is not Opaque, so it has to return \ - `Some`; qed"); - - inherents.push(inherent); - } - )* - - inherents - } - - fn check_extrinsics(&self, block: &#block) -> #scrate::inherent::CheckInherentsResult { - use #scrate::inherent::{ProvideInherent, IsFatalError}; - use #scrate::traits::{IsSubType, ExtrinsicCall}; - use #scrate::sp_runtime::traits::Block as _; - use #scrate::__private::{sp_inherents::Error, log}; - - let mut result = #scrate::inherent::CheckInherentsResult::new(); - - // This handle assume we abort on the first fatal error. - fn handle_put_error_result(res: Result<(), Error>) { - const LOG_TARGET: &str = "runtime::inherent"; - match res { - Ok(()) => (), - Err(Error::InherentDataExists(id)) => - log::debug!( - target: LOG_TARGET, - "Some error already reported for inherent {:?}, new non fatal \ - error is ignored", - id - ), - Err(Error::FatalErrorReported) => - log::error!( - target: LOG_TARGET, - "Fatal error already reported, unexpected considering there is \ - only one fatal error", - ), - Err(_) => - log::error!( - target: LOG_TARGET, - "Unexpected error from `put_error` operation", - ), - } - } - - for xt in block.extrinsics() { - // Inherents are before any other extrinsics. - // And signed extrinsics are not inherents. - if #scrate::sp_runtime::traits::Extrinsic::is_signed(xt).unwrap_or(false) { - break - } - - let mut is_inherent = false; - - #( - #pallet_attrs - { - let call = <#unchecked_extrinsic as ExtrinsicCall>::call(xt); - if let Some(call) = IsSubType::<_>::is_sub_type(call) { - if #pallet_names::is_inherent(call) { - is_inherent = true; - if let Err(e) = #pallet_names::check_inherent(call, self) { - handle_put_error_result(result.put_error( - #pallet_names::INHERENT_IDENTIFIER, &e - )); - if e.is_fatal_error() { - return result; - } - } - } - } - } - )* - - // Inherents are before any other extrinsics. - // No module marked it as inherent thus it is not. - if !is_inherent { - break - } - } - - #( - #pallet_attrs - match #pallet_names::is_inherent_required(self) { - Ok(Some(e)) => { - let found = block.extrinsics().iter().any(|xt| { - let is_signed = #scrate::sp_runtime::traits::Extrinsic::is_signed(xt) - .unwrap_or(false); - - if !is_signed { - let call = < - #unchecked_extrinsic as ExtrinsicCall - >::call(xt); - if let Some(call) = IsSubType::<_>::is_sub_type(call) { - #pallet_names::is_inherent(&call) - } else { - false - } - } else { - // Signed extrinsics are not inherents. - false - } - }); - - if !found { - handle_put_error_result(result.put_error( - #pallet_names::INHERENT_IDENTIFIER, &e - )); - if e.is_fatal_error() { - return result; - } - } - }, - Ok(None) => (), - Err(e) => { - handle_put_error_result(result.put_error( - #pallet_names::INHERENT_IDENTIFIER, &e - )); - if e.is_fatal_error() { - return result; - } - }, - } - )* - - result - } - } - - impl #scrate::traits::IsInherent<<#block as #scrate::sp_runtime::traits::Block>::Extrinsic> for #runtime { - fn is_inherent(ext: &<#block as #scrate::sp_runtime::traits::Block>::Extrinsic) -> bool { - use #scrate::inherent::ProvideInherent; - use #scrate::traits::{IsSubType, ExtrinsicCall}; - - if #scrate::sp_runtime::traits::Extrinsic::is_signed(ext).unwrap_or(false) { - // Signed extrinsics are never inherents. - return false - } - - #( - #pallet_attrs - { - let call = <#unchecked_extrinsic as ExtrinsicCall>::call(ext); - if let Some(call) = IsSubType::<_>::is_sub_type(call) { - if <#pallet_names as ProvideInherent>::is_inherent(&call) { - return true; - } - } - } - )* - false - } - } - - impl #scrate::traits::EnsureInherentsAreFirst<#block> for #runtime { - fn ensure_inherents_are_first(block: &#block) -> Result { - use #scrate::inherent::ProvideInherent; - use #scrate::traits::{IsSubType, ExtrinsicCall}; - use #scrate::sp_runtime::traits::Block as _; - - let mut num_inherents = 0u32; - - for (i, xt) in block.extrinsics().iter().enumerate() { - if >::is_inherent(xt) { - if num_inherents != i as u32 { - return Err(i as u32); - } - - num_inherents += 1; // Safe since we are in an `enumerate` loop. - } - } - - Ok(num_inherents) - } - } - } + let mut pallet_names = Vec::new(); + let mut pallet_attrs = Vec::new(); + let mut query_inherent_part_macros = Vec::new(); + + for pallet_decl in pallet_decls { + if pallet_decl.exists_part("Inherent") { + let name = &pallet_decl.name; + let path = &pallet_decl.path; + let attr = pallet_decl + .cfg_pattern + .iter() + .fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); + + pallet_names.push(name); + pallet_attrs.push(attr); + query_inherent_part_macros.push(quote! { + #path::__substrate_inherent_check::is_inherent_part_defined!(#name); + }); + } + } + + quote! { + #( #query_inherent_part_macros )* + + trait InherentDataExt { + fn create_extrinsics(&self) -> + #scrate::__private::sp_std::vec::Vec<<#block as #scrate::sp_runtime::traits::Block>::Extrinsic>; + fn check_extrinsics(&self, block: &#block) -> #scrate::inherent::CheckInherentsResult; + } + + impl InherentDataExt for #scrate::inherent::InherentData { + fn create_extrinsics(&self) -> + #scrate::__private::sp_std::vec::Vec<<#block as #scrate::sp_runtime::traits::Block>::Extrinsic> + { + use #scrate::inherent::ProvideInherent; + + let mut inherents = #scrate::__private::sp_std::vec::Vec::new(); + + #( + #pallet_attrs + if let Some(inherent) = #pallet_names::create_inherent(self) { + let inherent = <#unchecked_extrinsic as #scrate::sp_runtime::traits::Extrinsic>::new( + inherent.into(), + None, + ).expect("Runtime UncheckedExtrinsic is not Opaque, so it has to return \ + `Some`; qed"); + + inherents.push(inherent); + } + )* + + inherents + } + + fn check_extrinsics(&self, block: &#block) -> #scrate::inherent::CheckInherentsResult { + use #scrate::inherent::{ProvideInherent, IsFatalError}; + use #scrate::traits::{IsSubType, ExtrinsicCall}; + use #scrate::sp_runtime::traits::Block as _; + use #scrate::__private::{sp_inherents::Error, log}; + + let mut result = #scrate::inherent::CheckInherentsResult::new(); + + // This handle assume we abort on the first fatal error. + fn handle_put_error_result(res: Result<(), Error>) { + const LOG_TARGET: &str = "runtime::inherent"; + match res { + Ok(()) => (), + Err(Error::InherentDataExists(id)) => + log::debug!( + target: LOG_TARGET, + "Some error already reported for inherent {:?}, new non fatal \ + error is ignored", + id + ), + Err(Error::FatalErrorReported) => + log::error!( + target: LOG_TARGET, + "Fatal error already reported, unexpected considering there is \ + only one fatal error", + ), + Err(_) => + log::error!( + target: LOG_TARGET, + "Unexpected error from `put_error` operation", + ), + } + } + + for xt in block.extrinsics() { + // Inherents are before any other extrinsics. + // And signed extrinsics are not inherents. + if #scrate::sp_runtime::traits::Extrinsic::is_signed(xt).unwrap_or(false) { + break + } + + let mut is_inherent = false; + + #( + #pallet_attrs + { + let call = <#unchecked_extrinsic as ExtrinsicCall>::call(xt); + if let Some(call) = IsSubType::<_>::is_sub_type(call) { + if #pallet_names::is_inherent(call) { + is_inherent = true; + if let Err(e) = #pallet_names::check_inherent(call, self) { + handle_put_error_result(result.put_error( + #pallet_names::INHERENT_IDENTIFIER, &e + )); + if e.is_fatal_error() { + return result; + } + } + } + } + } + )* + + // Inherents are before any other extrinsics. + // No module marked it as inherent thus it is not. + if !is_inherent { + break + } + } + + #( + #pallet_attrs + match #pallet_names::is_inherent_required(self) { + Ok(Some(e)) => { + let found = block.extrinsics().iter().any(|xt| { + let is_signed = #scrate::sp_runtime::traits::Extrinsic::is_signed(xt) + .unwrap_or(false); + + if !is_signed { + let call = < + #unchecked_extrinsic as ExtrinsicCall + >::call(xt); + if let Some(call) = IsSubType::<_>::is_sub_type(call) { + #pallet_names::is_inherent(&call) + } else { + false + } + } else { + // Signed extrinsics are not inherents. + false + } + }); + + if !found { + handle_put_error_result(result.put_error( + #pallet_names::INHERENT_IDENTIFIER, &e + )); + if e.is_fatal_error() { + return result; + } + } + }, + Ok(None) => (), + Err(e) => { + handle_put_error_result(result.put_error( + #pallet_names::INHERENT_IDENTIFIER, &e + )); + if e.is_fatal_error() { + return result; + } + }, + } + )* + + result + } + } + + impl #scrate::traits::IsInherent<<#block as #scrate::sp_runtime::traits::Block>::Extrinsic> for #runtime { + fn is_inherent(ext: &<#block as #scrate::sp_runtime::traits::Block>::Extrinsic) -> bool { + use #scrate::inherent::ProvideInherent; + use #scrate::traits::{IsSubType, ExtrinsicCall}; + + if #scrate::sp_runtime::traits::Extrinsic::is_signed(ext).unwrap_or(false) { + // Signed extrinsics are never inherents. + return false + } + + #( + #pallet_attrs + { + let call = <#unchecked_extrinsic as ExtrinsicCall>::call(ext); + if let Some(call) = IsSubType::<_>::is_sub_type(call) { + if <#pallet_names as ProvideInherent>::is_inherent(&call) { + return true; + } + } + } + )* + false + } + } + + impl #scrate::traits::EnsureInherentsAreFirst<#block> for #runtime { + fn ensure_inherents_are_first(block: &#block) -> Result { + use #scrate::inherent::ProvideInherent; + use #scrate::traits::{IsSubType, ExtrinsicCall}; + use #scrate::sp_runtime::traits::Block as _; + + let mut num_inherents = 0u32; + + for (i, xt) in block.extrinsics().iter().enumerate() { + if >::is_inherent(xt) { + if num_inherents != i as u32 { + return Err(i as u32); + } + + num_inherents += 1; // Safe since we are in an `enumerate` loop. + } + } + + Ok(num_inherents) + } + } + } } diff --git a/support/procedural-fork/src/construct_runtime/expand/lock_id.rs b/support/procedural-fork/src/construct_runtime/expand/lock_id.rs index e67c0da00..67c2fb933 100644 --- a/support/procedural-fork/src/construct_runtime/expand/lock_id.rs +++ b/support/procedural-fork/src/construct_runtime/expand/lock_id.rs @@ -21,44 +21,44 @@ use proc_macro2::TokenStream; use quote::quote; pub fn expand_outer_lock_id(pallet_decls: &[Pallet], scrate: &TokenStream) -> TokenStream { - let mut conversion_fns = Vec::new(); - let mut lock_id_variants = Vec::new(); - for decl in pallet_decls { - if let Some(_) = decl.find_part("LockId") { - let variant_name = &decl.name; - let path = &decl.path; - let index = decl.index; - let instance = decl.instance.as_ref(); + let mut conversion_fns = Vec::new(); + let mut lock_id_variants = Vec::new(); + for decl in pallet_decls { + if let Some(_) = decl.find_part("LockId") { + let variant_name = &decl.name; + let path = &decl.path; + let index = decl.index; + let instance = decl.instance.as_ref(); - conversion_fns.push(composite_helper::expand_conversion_fn( - "LockId", - path, - instance, - variant_name, - )); + conversion_fns.push(composite_helper::expand_conversion_fn( + "LockId", + path, + instance, + variant_name, + )); - lock_id_variants.push(composite_helper::expand_variant( - "LockId", - index, - path, - instance, - variant_name, - )); - } - } + lock_id_variants.push(composite_helper::expand_variant( + "LockId", + index, + path, + instance, + variant_name, + )); + } + } - quote! { - /// An identifier for each lock placed on funds. - #[derive( - Copy, Clone, Eq, PartialEq, - #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, - #scrate::__private::scale_info::TypeInfo, - #scrate::__private::RuntimeDebug, - )] - pub enum RuntimeLockId { - #( #lock_id_variants )* - } + quote! { + /// An identifier for each lock placed on funds. + #[derive( + Copy, Clone, Eq, PartialEq, + #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, + #scrate::__private::scale_info::TypeInfo, + #scrate::__private::RuntimeDebug, + )] + pub enum RuntimeLockId { + #( #lock_id_variants )* + } - #( #conversion_fns )* - } + #( #conversion_fns )* + } } diff --git a/support/procedural-fork/src/construct_runtime/expand/metadata.rs b/support/procedural-fork/src/construct_runtime/expand/metadata.rs index 0e76f9a92..f98c719ca 100644 --- a/support/procedural-fork/src/construct_runtime/expand/metadata.rs +++ b/support/procedural-fork/src/construct_runtime/expand/metadata.rs @@ -22,237 +22,240 @@ use std::str::FromStr; use syn::Ident; pub fn expand_runtime_metadata( - runtime: &Ident, - pallet_declarations: &[Pallet], - scrate: &TokenStream, - extrinsic: &TokenStream, - system_path: &PalletPath, + runtime: &Ident, + pallet_declarations: &[Pallet], + scrate: &TokenStream, + extrinsic: &TokenStream, + system_path: &PalletPath, ) -> TokenStream { - let pallets = pallet_declarations - .iter() - .filter_map(|pallet_declaration| { - pallet_declaration.find_part("Pallet").map(|_| { - let filtered_names: Vec<_> = pallet_declaration - .pallet_parts() - .iter() - .filter(|part| part.name() != "Pallet") - .map(|part| part.name()) - .collect(); - (pallet_declaration, filtered_names) - }) - }) - .map(|(decl, filtered_names)| { - let name = &decl.name; - let index = &decl.index; - let storage = expand_pallet_metadata_storage(&filtered_names, runtime, decl); - let calls = expand_pallet_metadata_calls(&filtered_names, runtime, decl); - let event = expand_pallet_metadata_events(&filtered_names, runtime, scrate, decl); - let constants = expand_pallet_metadata_constants(runtime, decl); - let errors = expand_pallet_metadata_errors(runtime, decl); - let docs = expand_pallet_metadata_docs(runtime, decl); - let attr = decl.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { - let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) - .expect("was successfully parsed before; qed"); - quote! { - #acc - #attr - } - }); + let pallets = pallet_declarations + .iter() + .filter_map(|pallet_declaration| { + pallet_declaration.find_part("Pallet").map(|_| { + let filtered_names: Vec<_> = pallet_declaration + .pallet_parts() + .iter() + .filter(|part| part.name() != "Pallet") + .map(|part| part.name()) + .collect(); + (pallet_declaration, filtered_names) + }) + }) + .map(|(decl, filtered_names)| { + let name = &decl.name; + let index = &decl.index; + let storage = expand_pallet_metadata_storage(&filtered_names, runtime, decl); + let calls = expand_pallet_metadata_calls(&filtered_names, runtime, decl); + let event = expand_pallet_metadata_events(&filtered_names, runtime, scrate, decl); + let constants = expand_pallet_metadata_constants(runtime, decl); + let errors = expand_pallet_metadata_errors(runtime, decl); + let docs = expand_pallet_metadata_docs(runtime, decl); + let attr = decl + .cfg_pattern + .iter() + .fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); - quote! { - #attr - #scrate::__private::metadata_ir::PalletMetadataIR { - name: stringify!(#name), - index: #index, - storage: #storage, - calls: #calls, - event: #event, - constants: #constants, - error: #errors, - docs: #docs, - } - } - }) - .collect::>(); + quote! { + #attr + #scrate::__private::metadata_ir::PalletMetadataIR { + name: stringify!(#name), + index: #index, + storage: #storage, + calls: #calls, + event: #event, + constants: #constants, + error: #errors, + docs: #docs, + } + } + }) + .collect::>(); - quote! { - impl #runtime { - fn metadata_ir() -> #scrate::__private::metadata_ir::MetadataIR { - // Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata. - // The function is implemented by calling `impl_runtime_apis!`. - // - // However, the `construct_runtime!` may be called without calling `impl_runtime_apis!`. - // Rely on the `Deref` trait to differentiate between a runtime that implements - // APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro construct_runtime!). - // - // Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function. - // `InternalConstructRuntime` is implemented by the `construct_runtime!` for Runtime references (`& Runtime`), - // while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`). - // - // Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!` - // when both macros are called; and will resolve an empty `runtime_metadata` when only the `construct_runtime!` - // is called. - // - // `Deref` needs a reference for resolving the function call. - let rt = #runtime; + quote! { + impl #runtime { + fn metadata_ir() -> #scrate::__private::metadata_ir::MetadataIR { + // Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata. + // The function is implemented by calling `impl_runtime_apis!`. + // + // However, the `construct_runtime!` may be called without calling `impl_runtime_apis!`. + // Rely on the `Deref` trait to differentiate between a runtime that implements + // APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro construct_runtime!). + // + // Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function. + // `InternalConstructRuntime` is implemented by the `construct_runtime!` for Runtime references (`& Runtime`), + // while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`). + // + // Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!` + // when both macros are called; and will resolve an empty `runtime_metadata` when only the `construct_runtime!` + // is called. + // + // `Deref` needs a reference for resolving the function call. + let rt = #runtime; - let ty = #scrate::__private::scale_info::meta_type::<#extrinsic>(); - let address_ty = #scrate::__private::scale_info::meta_type::< - <<#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::SignaturePayload as #scrate::sp_runtime::traits::SignaturePayload>::SignatureAddress - >(); - let call_ty = #scrate::__private::scale_info::meta_type::< - <#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::Call - >(); - let signature_ty = #scrate::__private::scale_info::meta_type::< - <<#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::SignaturePayload as #scrate::sp_runtime::traits::SignaturePayload>::Signature - >(); - let extra_ty = #scrate::__private::scale_info::meta_type::< - <<#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::SignaturePayload as #scrate::sp_runtime::traits::SignaturePayload>::SignatureExtra - >(); + let ty = #scrate::__private::scale_info::meta_type::<#extrinsic>(); + let address_ty = #scrate::__private::scale_info::meta_type::< + <<#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::SignaturePayload as #scrate::sp_runtime::traits::SignaturePayload>::SignatureAddress + >(); + let call_ty = #scrate::__private::scale_info::meta_type::< + <#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::Call + >(); + let signature_ty = #scrate::__private::scale_info::meta_type::< + <<#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::SignaturePayload as #scrate::sp_runtime::traits::SignaturePayload>::Signature + >(); + let extra_ty = #scrate::__private::scale_info::meta_type::< + <<#extrinsic as #scrate::sp_runtime::traits::Extrinsic>::SignaturePayload as #scrate::sp_runtime::traits::SignaturePayload>::SignatureExtra + >(); - #scrate::__private::metadata_ir::MetadataIR { - pallets: #scrate::__private::sp_std::vec![ #(#pallets),* ], - extrinsic: #scrate::__private::metadata_ir::ExtrinsicMetadataIR { - ty, - version: <#extrinsic as #scrate::sp_runtime::traits::ExtrinsicMetadata>::VERSION, - address_ty, - call_ty, - signature_ty, - extra_ty, - signed_extensions: < - < - #extrinsic as #scrate::sp_runtime::traits::ExtrinsicMetadata - >::SignedExtensions as #scrate::sp_runtime::traits::SignedExtension - >::metadata() - .into_iter() - .map(|meta| #scrate::__private::metadata_ir::SignedExtensionMetadataIR { - identifier: meta.identifier, - ty: meta.ty, - additional_signed: meta.additional_signed, - }) - .collect(), - }, - ty: #scrate::__private::scale_info::meta_type::<#runtime>(), - apis: (&rt).runtime_metadata(), - outer_enums: #scrate::__private::metadata_ir::OuterEnumsIR { - call_enum_ty: #scrate::__private::scale_info::meta_type::< - <#runtime as #system_path::Config>::RuntimeCall - >(), - event_enum_ty: #scrate::__private::scale_info::meta_type::(), - error_enum_ty: #scrate::__private::scale_info::meta_type::(), - } - } - } + #scrate::__private::metadata_ir::MetadataIR { + pallets: #scrate::__private::sp_std::vec![ #(#pallets),* ], + extrinsic: #scrate::__private::metadata_ir::ExtrinsicMetadataIR { + ty, + version: <#extrinsic as #scrate::sp_runtime::traits::ExtrinsicMetadata>::VERSION, + address_ty, + call_ty, + signature_ty, + extra_ty, + signed_extensions: < + < + #extrinsic as #scrate::sp_runtime::traits::ExtrinsicMetadata + >::SignedExtensions as #scrate::sp_runtime::traits::SignedExtension + >::metadata() + .into_iter() + .map(|meta| #scrate::__private::metadata_ir::SignedExtensionMetadataIR { + identifier: meta.identifier, + ty: meta.ty, + additional_signed: meta.additional_signed, + }) + .collect(), + }, + ty: #scrate::__private::scale_info::meta_type::<#runtime>(), + apis: (&rt).runtime_metadata(), + outer_enums: #scrate::__private::metadata_ir::OuterEnumsIR { + call_enum_ty: #scrate::__private::scale_info::meta_type::< + <#runtime as #system_path::Config>::RuntimeCall + >(), + event_enum_ty: #scrate::__private::scale_info::meta_type::(), + error_enum_ty: #scrate::__private::scale_info::meta_type::(), + } + } + } - pub fn metadata() -> #scrate::__private::metadata::RuntimeMetadataPrefixed { - // Note: this always returns the V14 version. The runtime API function - // must be deprecated. - #scrate::__private::metadata_ir::into_v14(#runtime::metadata_ir()) - } + pub fn metadata() -> #scrate::__private::metadata::RuntimeMetadataPrefixed { + // Note: this always returns the V14 version. The runtime API function + // must be deprecated. + #scrate::__private::metadata_ir::into_v14(#runtime::metadata_ir()) + } - pub fn metadata_at_version(version: u32) -> Option<#scrate::__private::OpaqueMetadata> { - #scrate::__private::metadata_ir::into_version(#runtime::metadata_ir(), version).map(|prefixed| { - #scrate::__private::OpaqueMetadata::new(prefixed.into()) - }) - } + pub fn metadata_at_version(version: u32) -> Option<#scrate::__private::OpaqueMetadata> { + #scrate::__private::metadata_ir::into_version(#runtime::metadata_ir(), version).map(|prefixed| { + #scrate::__private::OpaqueMetadata::new(prefixed.into()) + }) + } - pub fn metadata_versions() -> #scrate::__private::sp_std::vec::Vec { - #scrate::__private::metadata_ir::supported_versions() - } - } - } + pub fn metadata_versions() -> #scrate::__private::sp_std::vec::Vec { + #scrate::__private::metadata_ir::supported_versions() + } + } + } } fn expand_pallet_metadata_storage( - filtered_names: &[&'static str], - runtime: &Ident, - decl: &Pallet, + filtered_names: &[&'static str], + runtime: &Ident, + decl: &Pallet, ) -> TokenStream { - if filtered_names.contains(&"Storage") { - let instance = decl.instance.as_ref().into_iter(); - let path = &decl.path; + if filtered_names.contains(&"Storage") { + let instance = decl.instance.as_ref().into_iter(); + let path = &decl.path; - quote! { - Some(#path::Pallet::<#runtime #(, #path::#instance)*>::storage_metadata()) - } - } else { - quote!(None) - } + quote! { + Some(#path::Pallet::<#runtime #(, #path::#instance)*>::storage_metadata()) + } + } else { + quote!(None) + } } fn expand_pallet_metadata_calls( - filtered_names: &[&'static str], - runtime: &Ident, - decl: &Pallet, + filtered_names: &[&'static str], + runtime: &Ident, + decl: &Pallet, ) -> TokenStream { - if filtered_names.contains(&"Call") { - let instance = decl.instance.as_ref().into_iter(); - let path = &decl.path; + if filtered_names.contains(&"Call") { + let instance = decl.instance.as_ref().into_iter(); + let path = &decl.path; - quote! { - Some(#path::Pallet::<#runtime #(, #path::#instance)*>::call_functions()) - } - } else { - quote!(None) - } + quote! { + Some(#path::Pallet::<#runtime #(, #path::#instance)*>::call_functions()) + } + } else { + quote!(None) + } } fn expand_pallet_metadata_events( - filtered_names: &[&'static str], - runtime: &Ident, - scrate: &TokenStream, - decl: &Pallet, + filtered_names: &[&'static str], + runtime: &Ident, + scrate: &TokenStream, + decl: &Pallet, ) -> TokenStream { - if filtered_names.contains(&"Event") { - let path = &decl.path; - let part_is_generic = !decl - .find_part("Event") - .expect("Event part exists; qed") - .generics - .params - .is_empty(); - let pallet_event = match (decl.instance.as_ref(), part_is_generic) { - (Some(inst), true) => quote!(#path::Event::<#runtime, #path::#inst>), - (Some(inst), false) => quote!(#path::Event::<#path::#inst>), - (None, true) => quote!(#path::Event::<#runtime>), - (None, false) => quote!(#path::Event), - }; + if filtered_names.contains(&"Event") { + let path = &decl.path; + let part_is_generic = !decl + .find_part("Event") + .expect("Event part exists; qed") + .generics + .params + .is_empty(); + let pallet_event = match (decl.instance.as_ref(), part_is_generic) { + (Some(inst), true) => quote!(#path::Event::<#runtime, #path::#inst>), + (Some(inst), false) => quote!(#path::Event::<#path::#inst>), + (None, true) => quote!(#path::Event::<#runtime>), + (None, false) => quote!(#path::Event), + }; - quote! { - Some( - #scrate::__private::metadata_ir::PalletEventMetadataIR { - ty: #scrate::__private::scale_info::meta_type::<#pallet_event>() - } - ) - } - } else { - quote!(None) - } + quote! { + Some( + #scrate::__private::metadata_ir::PalletEventMetadataIR { + ty: #scrate::__private::scale_info::meta_type::<#pallet_event>() + } + ) + } + } else { + quote!(None) + } } fn expand_pallet_metadata_constants(runtime: &Ident, decl: &Pallet) -> TokenStream { - let path = &decl.path; - let instance = decl.instance.as_ref().into_iter(); + let path = &decl.path; + let instance = decl.instance.as_ref().into_iter(); - quote! { - #path::Pallet::<#runtime #(, #path::#instance)*>::pallet_constants_metadata() - } + quote! { + #path::Pallet::<#runtime #(, #path::#instance)*>::pallet_constants_metadata() + } } fn expand_pallet_metadata_errors(runtime: &Ident, decl: &Pallet) -> TokenStream { - let path = &decl.path; - let instance = decl.instance.as_ref().into_iter(); + let path = &decl.path; + let instance = decl.instance.as_ref().into_iter(); - quote! { - #path::Pallet::<#runtime #(, #path::#instance)*>::error_metadata() - } + quote! { + #path::Pallet::<#runtime #(, #path::#instance)*>::error_metadata() + } } fn expand_pallet_metadata_docs(runtime: &Ident, decl: &Pallet) -> TokenStream { - let path = &decl.path; - let instance = decl.instance.as_ref().into_iter(); + let path = &decl.path; + let instance = decl.instance.as_ref().into_iter(); - quote! { - #path::Pallet::<#runtime #(, #path::#instance)*>::pallet_documentation_metadata() - } + quote! { + #path::Pallet::<#runtime #(, #path::#instance)*>::pallet_documentation_metadata() + } } diff --git a/support/procedural-fork/src/construct_runtime/expand/origin.rs b/support/procedural-fork/src/construct_runtime/expand/origin.rs index 83049919d..2d50777bf 100644 --- a/support/procedural-fork/src/construct_runtime/expand/origin.rs +++ b/support/procedural-fork/src/construct_runtime/expand/origin.rs @@ -22,434 +22,448 @@ use std::str::FromStr; use syn::{Generics, Ident}; pub fn expand_outer_origin( - runtime: &Ident, - system_pallet: &Pallet, - pallets: &[Pallet], - scrate: &TokenStream, + runtime: &Ident, + system_pallet: &Pallet, + pallets: &[Pallet], + scrate: &TokenStream, ) -> syn::Result { - let mut caller_variants = TokenStream::new(); - let mut pallet_conversions = TokenStream::new(); - let mut query_origin_part_macros = Vec::new(); - - for pallet_decl in pallets.iter().filter(|pallet| pallet.name != SYSTEM_PALLET_NAME) { - if let Some(pallet_entry) = pallet_decl.find_part("Origin") { - let instance = pallet_decl.instance.as_ref(); - let index = pallet_decl.index; - let generics = &pallet_entry.generics; - let name = &pallet_decl.name; - let path = &pallet_decl.path; - - if instance.is_some() && generics.params.is_empty() { - let msg = format!( - "Instantiable pallet with no generic `Origin` cannot \ + let mut caller_variants = TokenStream::new(); + let mut pallet_conversions = TokenStream::new(); + let mut query_origin_part_macros = Vec::new(); + + for pallet_decl in pallets + .iter() + .filter(|pallet| pallet.name != SYSTEM_PALLET_NAME) + { + if let Some(pallet_entry) = pallet_decl.find_part("Origin") { + let instance = pallet_decl.instance.as_ref(); + let index = pallet_decl.index; + let generics = &pallet_entry.generics; + let name = &pallet_decl.name; + let path = &pallet_decl.path; + + if instance.is_some() && generics.params.is_empty() { + let msg = format!( + "Instantiable pallet with no generic `Origin` cannot \ be constructed: pallet `{}` must have generic `Origin`", - name - ); - return Err(syn::Error::new(name.span(), msg)) - } - - caller_variants.extend(expand_origin_caller_variant( - runtime, - pallet_decl, - index, - instance, - generics, - )); - pallet_conversions.extend(expand_origin_pallet_conversions( - scrate, - runtime, - pallet_decl, - instance, - generics, - )); - query_origin_part_macros.push(quote! { - #path::__substrate_origin_check::is_origin_part_defined!(#name); - }); - } - } - - let system_path = &system_pallet.path; - - let system_index = system_pallet.index; - - let system_path_name = system_path.module_name(); - - let doc_string = get_intra_doc_string( - "Origin is always created with the base filter configured in", - &system_path_name, - ); - - let doc_string_none_origin = - get_intra_doc_string("Create with system none origin and", &system_path_name); - - let doc_string_root_origin = - get_intra_doc_string("Create with system root origin and", &system_path_name); - - let doc_string_signed_origin = - get_intra_doc_string("Create with system signed origin and", &system_path_name); - - let doc_string_runtime_origin = - get_intra_doc_string("Convert to runtime origin, using as filter:", &system_path_name); - - let doc_string_runtime_origin_with_caller = get_intra_doc_string( - "Convert to runtime origin with caller being system signed or none and use filter", - &system_path_name, - ); - - Ok(quote! { - #( #query_origin_part_macros )* - - /// The runtime origin type representing the origin of a call. - /// - #[doc = #doc_string] - #[derive(Clone)] - pub struct RuntimeOrigin { - pub caller: OriginCaller, - filter: #scrate::__private::sp_std::rc::Rc::RuntimeCall) -> bool>>, - } - - #[cfg(not(feature = "std"))] - impl #scrate::__private::sp_std::fmt::Debug for RuntimeOrigin { - fn fmt( - &self, - fmt: &mut #scrate::__private::sp_std::fmt::Formatter, - ) -> #scrate::__private::sp_std::result::Result<(), #scrate::__private::sp_std::fmt::Error> { - fmt.write_str("") - } - } - - #[cfg(feature = "std")] - impl #scrate::__private::sp_std::fmt::Debug for RuntimeOrigin { - fn fmt( - &self, - fmt: &mut #scrate::__private::sp_std::fmt::Formatter, - ) -> #scrate::__private::sp_std::result::Result<(), #scrate::__private::sp_std::fmt::Error> { - fmt.debug_struct("Origin") - .field("caller", &self.caller) - .field("filter", &"[function ptr]") - .finish() - } - } - - impl #scrate::traits::OriginTrait for RuntimeOrigin { - type Call = <#runtime as #system_path::Config>::RuntimeCall; - type PalletsOrigin = OriginCaller; - type AccountId = <#runtime as #system_path::Config>::AccountId; - - fn add_filter(&mut self, filter: impl Fn(&Self::Call) -> bool + 'static) { - let f = self.filter.clone(); - - self.filter = #scrate::__private::sp_std::rc::Rc::new(Box::new(move |call| { - f(call) && filter(call) - })); - } - - fn reset_filter(&mut self) { - let filter = < - <#runtime as #system_path::Config>::BaseCallFilter - as #scrate::traits::Contains<<#runtime as #system_path::Config>::RuntimeCall> - >::contains; - - self.filter = #scrate::__private::sp_std::rc::Rc::new(Box::new(filter)); - } - - fn set_caller_from(&mut self, other: impl Into) { - self.caller = other.into().caller; - } - - fn filter_call(&self, call: &Self::Call) -> bool { - match self.caller { - // Root bypasses all filters - OriginCaller::system(#system_path::Origin::<#runtime>::Root) => true, - _ => (self.filter)(call), - } - } - - fn caller(&self) -> &Self::PalletsOrigin { - &self.caller - } - - fn into_caller(self) -> Self::PalletsOrigin { - self.caller - } - - fn try_with_caller( - mut self, - f: impl FnOnce(Self::PalletsOrigin) -> Result, - ) -> Result { - match f(self.caller) { - Ok(r) => Ok(r), - Err(caller) => { self.caller = caller; Err(self) } - } - } - - fn none() -> Self { - #system_path::RawOrigin::None.into() - } - - fn root() -> Self { - #system_path::RawOrigin::Root.into() - } - - fn signed(by: Self::AccountId) -> Self { - #system_path::RawOrigin::Signed(by).into() - } - } - - #[derive( - Clone, PartialEq, Eq, #scrate::__private::RuntimeDebug, #scrate::__private::codec::Encode, - #scrate::__private::codec::Decode, #scrate::__private::scale_info::TypeInfo, #scrate::__private::codec::MaxEncodedLen, - )] - #[allow(non_camel_case_types)] - pub enum OriginCaller { - #[codec(index = #system_index)] - system(#system_path::Origin<#runtime>), - #caller_variants - #[allow(dead_code)] - Void(#scrate::__private::Void) - } - - // For backwards compatibility and ease of accessing these functions. - #[allow(dead_code)] - impl RuntimeOrigin { - #[doc = #doc_string_none_origin] - pub fn none() -> Self { - ::none() - } - - #[doc = #doc_string_root_origin] - pub fn root() -> Self { - ::root() - } - - #[doc = #doc_string_signed_origin] - pub fn signed(by: <#runtime as #system_path::Config>::AccountId) -> Self { - ::signed(by) - } - } - - impl From<#system_path::Origin<#runtime>> for OriginCaller { - fn from(x: #system_path::Origin<#runtime>) -> Self { - OriginCaller::system(x) - } - } - - impl #scrate::traits::CallerTrait<<#runtime as #system_path::Config>::AccountId> for OriginCaller { - fn into_system(self) -> Option<#system_path::RawOrigin<<#runtime as #system_path::Config>::AccountId>> { - match self { - OriginCaller::system(x) => Some(x), - _ => None, - } - } - fn as_system_ref(&self) -> Option<&#system_path::RawOrigin<<#runtime as #system_path::Config>::AccountId>> { - match &self { - OriginCaller::system(o) => Some(o), - _ => None, - } - } - } - - impl TryFrom for #system_path::Origin<#runtime> { - type Error = OriginCaller; - fn try_from(x: OriginCaller) - -> #scrate::__private::sp_std::result::Result<#system_path::Origin<#runtime>, OriginCaller> - { - if let OriginCaller::system(l) = x { - Ok(l) - } else { - Err(x) - } - } - } - - impl From<#system_path::Origin<#runtime>> for RuntimeOrigin { - - #[doc = #doc_string_runtime_origin] - fn from(x: #system_path::Origin<#runtime>) -> Self { - let o: OriginCaller = x.into(); - o.into() - } - } - - impl From for RuntimeOrigin { - fn from(x: OriginCaller) -> Self { - let mut o = RuntimeOrigin { - caller: x, - filter: #scrate::__private::sp_std::rc::Rc::new(Box::new(|_| true)), - }; - - #scrate::traits::OriginTrait::reset_filter(&mut o); - - o - } - } - - impl From for #scrate::__private::sp_std::result::Result<#system_path::Origin<#runtime>, RuntimeOrigin> { - /// NOTE: converting to pallet origin loses the origin filter information. - fn from(val: RuntimeOrigin) -> Self { - if let OriginCaller::system(l) = val.caller { - Ok(l) - } else { - Err(val) - } - } - } - impl From::AccountId>> for RuntimeOrigin { - #[doc = #doc_string_runtime_origin_with_caller] - fn from(x: Option<<#runtime as #system_path::Config>::AccountId>) -> Self { - <#system_path::Origin<#runtime>>::from(x).into() - } - } - - #pallet_conversions - }) + name + ); + return Err(syn::Error::new(name.span(), msg)); + } + + caller_variants.extend(expand_origin_caller_variant( + runtime, + pallet_decl, + index, + instance, + generics, + )); + pallet_conversions.extend(expand_origin_pallet_conversions( + scrate, + runtime, + pallet_decl, + instance, + generics, + )); + query_origin_part_macros.push(quote! { + #path::__substrate_origin_check::is_origin_part_defined!(#name); + }); + } + } + + let system_path = &system_pallet.path; + + let system_index = system_pallet.index; + + let system_path_name = system_path.module_name(); + + let doc_string = get_intra_doc_string( + "Origin is always created with the base filter configured in", + &system_path_name, + ); + + let doc_string_none_origin = + get_intra_doc_string("Create with system none origin and", &system_path_name); + + let doc_string_root_origin = + get_intra_doc_string("Create with system root origin and", &system_path_name); + + let doc_string_signed_origin = + get_intra_doc_string("Create with system signed origin and", &system_path_name); + + let doc_string_runtime_origin = get_intra_doc_string( + "Convert to runtime origin, using as filter:", + &system_path_name, + ); + + let doc_string_runtime_origin_with_caller = get_intra_doc_string( + "Convert to runtime origin with caller being system signed or none and use filter", + &system_path_name, + ); + + Ok(quote! { + #( #query_origin_part_macros )* + + /// The runtime origin type representing the origin of a call. + /// + #[doc = #doc_string] + #[derive(Clone)] + pub struct RuntimeOrigin { + pub caller: OriginCaller, + filter: #scrate::__private::sp_std::rc::Rc::RuntimeCall) -> bool>>, + } + + #[cfg(not(feature = "std"))] + impl #scrate::__private::sp_std::fmt::Debug for RuntimeOrigin { + fn fmt( + &self, + fmt: &mut #scrate::__private::sp_std::fmt::Formatter, + ) -> #scrate::__private::sp_std::result::Result<(), #scrate::__private::sp_std::fmt::Error> { + fmt.write_str("") + } + } + + #[cfg(feature = "std")] + impl #scrate::__private::sp_std::fmt::Debug for RuntimeOrigin { + fn fmt( + &self, + fmt: &mut #scrate::__private::sp_std::fmt::Formatter, + ) -> #scrate::__private::sp_std::result::Result<(), #scrate::__private::sp_std::fmt::Error> { + fmt.debug_struct("Origin") + .field("caller", &self.caller) + .field("filter", &"[function ptr]") + .finish() + } + } + + impl #scrate::traits::OriginTrait for RuntimeOrigin { + type Call = <#runtime as #system_path::Config>::RuntimeCall; + type PalletsOrigin = OriginCaller; + type AccountId = <#runtime as #system_path::Config>::AccountId; + + fn add_filter(&mut self, filter: impl Fn(&Self::Call) -> bool + 'static) { + let f = self.filter.clone(); + + self.filter = #scrate::__private::sp_std::rc::Rc::new(Box::new(move |call| { + f(call) && filter(call) + })); + } + + fn reset_filter(&mut self) { + let filter = < + <#runtime as #system_path::Config>::BaseCallFilter + as #scrate::traits::Contains<<#runtime as #system_path::Config>::RuntimeCall> + >::contains; + + self.filter = #scrate::__private::sp_std::rc::Rc::new(Box::new(filter)); + } + + fn set_caller_from(&mut self, other: impl Into) { + self.caller = other.into().caller; + } + + fn filter_call(&self, call: &Self::Call) -> bool { + match self.caller { + // Root bypasses all filters + OriginCaller::system(#system_path::Origin::<#runtime>::Root) => true, + _ => (self.filter)(call), + } + } + + fn caller(&self) -> &Self::PalletsOrigin { + &self.caller + } + + fn into_caller(self) -> Self::PalletsOrigin { + self.caller + } + + fn try_with_caller( + mut self, + f: impl FnOnce(Self::PalletsOrigin) -> Result, + ) -> Result { + match f(self.caller) { + Ok(r) => Ok(r), + Err(caller) => { self.caller = caller; Err(self) } + } + } + + fn none() -> Self { + #system_path::RawOrigin::None.into() + } + + fn root() -> Self { + #system_path::RawOrigin::Root.into() + } + + fn signed(by: Self::AccountId) -> Self { + #system_path::RawOrigin::Signed(by).into() + } + } + + #[derive( + Clone, PartialEq, Eq, #scrate::__private::RuntimeDebug, #scrate::__private::codec::Encode, + #scrate::__private::codec::Decode, #scrate::__private::scale_info::TypeInfo, #scrate::__private::codec::MaxEncodedLen, + )] + #[allow(non_camel_case_types)] + pub enum OriginCaller { + #[codec(index = #system_index)] + system(#system_path::Origin<#runtime>), + #caller_variants + #[allow(dead_code)] + Void(#scrate::__private::Void) + } + + // For backwards compatibility and ease of accessing these functions. + #[allow(dead_code)] + impl RuntimeOrigin { + #[doc = #doc_string_none_origin] + pub fn none() -> Self { + ::none() + } + + #[doc = #doc_string_root_origin] + pub fn root() -> Self { + ::root() + } + + #[doc = #doc_string_signed_origin] + pub fn signed(by: <#runtime as #system_path::Config>::AccountId) -> Self { + ::signed(by) + } + } + + impl From<#system_path::Origin<#runtime>> for OriginCaller { + fn from(x: #system_path::Origin<#runtime>) -> Self { + OriginCaller::system(x) + } + } + + impl #scrate::traits::CallerTrait<<#runtime as #system_path::Config>::AccountId> for OriginCaller { + fn into_system(self) -> Option<#system_path::RawOrigin<<#runtime as #system_path::Config>::AccountId>> { + match self { + OriginCaller::system(x) => Some(x), + _ => None, + } + } + fn as_system_ref(&self) -> Option<&#system_path::RawOrigin<<#runtime as #system_path::Config>::AccountId>> { + match &self { + OriginCaller::system(o) => Some(o), + _ => None, + } + } + } + + impl TryFrom for #system_path::Origin<#runtime> { + type Error = OriginCaller; + fn try_from(x: OriginCaller) + -> #scrate::__private::sp_std::result::Result<#system_path::Origin<#runtime>, OriginCaller> + { + if let OriginCaller::system(l) = x { + Ok(l) + } else { + Err(x) + } + } + } + + impl From<#system_path::Origin<#runtime>> for RuntimeOrigin { + + #[doc = #doc_string_runtime_origin] + fn from(x: #system_path::Origin<#runtime>) -> Self { + let o: OriginCaller = x.into(); + o.into() + } + } + + impl From for RuntimeOrigin { + fn from(x: OriginCaller) -> Self { + let mut o = RuntimeOrigin { + caller: x, + filter: #scrate::__private::sp_std::rc::Rc::new(Box::new(|_| true)), + }; + + #scrate::traits::OriginTrait::reset_filter(&mut o); + + o + } + } + + impl From for #scrate::__private::sp_std::result::Result<#system_path::Origin<#runtime>, RuntimeOrigin> { + /// NOTE: converting to pallet origin loses the origin filter information. + fn from(val: RuntimeOrigin) -> Self { + if let OriginCaller::system(l) = val.caller { + Ok(l) + } else { + Err(val) + } + } + } + impl From::AccountId>> for RuntimeOrigin { + #[doc = #doc_string_runtime_origin_with_caller] + fn from(x: Option<<#runtime as #system_path::Config>::AccountId>) -> Self { + <#system_path::Origin<#runtime>>::from(x).into() + } + } + + #pallet_conversions + }) } fn expand_origin_caller_variant( - runtime: &Ident, - pallet: &Pallet, - index: u8, - instance: Option<&Ident>, - generics: &Generics, + runtime: &Ident, + pallet: &Pallet, + index: u8, + instance: Option<&Ident>, + generics: &Generics, ) -> TokenStream { - let part_is_generic = !generics.params.is_empty(); - let variant_name = &pallet.name; - let path = &pallet.path; - let attr = pallet.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { - let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) - .expect("was successfully parsed before; qed"); - quote! { - #acc - #attr - } - }); - - match instance { - Some(inst) if part_is_generic => quote! { - #attr - #[codec(index = #index)] - #variant_name(#path::Origin<#runtime, #path::#inst>), - }, - Some(inst) => quote! { - #attr - #[codec(index = #index)] - #variant_name(#path::Origin<#path::#inst>), - }, - None if part_is_generic => quote! { - #attr - #[codec(index = #index)] - #variant_name(#path::Origin<#runtime>), - }, - None => quote! { - #attr - #[codec(index = #index)] - #variant_name(#path::Origin), - }, - } + let part_is_generic = !generics.params.is_empty(); + let variant_name = &pallet.name; + let path = &pallet.path; + let attr = pallet + .cfg_pattern + .iter() + .fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); + + match instance { + Some(inst) if part_is_generic => quote! { + #attr + #[codec(index = #index)] + #variant_name(#path::Origin<#runtime, #path::#inst>), + }, + Some(inst) => quote! { + #attr + #[codec(index = #index)] + #variant_name(#path::Origin<#path::#inst>), + }, + None if part_is_generic => quote! { + #attr + #[codec(index = #index)] + #variant_name(#path::Origin<#runtime>), + }, + None => quote! { + #attr + #[codec(index = #index)] + #variant_name(#path::Origin), + }, + } } fn expand_origin_pallet_conversions( - scrate: &TokenStream, - runtime: &Ident, - pallet: &Pallet, - instance: Option<&Ident>, - generics: &Generics, + scrate: &TokenStream, + runtime: &Ident, + pallet: &Pallet, + instance: Option<&Ident>, + generics: &Generics, ) -> TokenStream { - let path = &pallet.path; - let variant_name = &pallet.name; - - let part_is_generic = !generics.params.is_empty(); - let pallet_origin = match instance { - Some(inst) if part_is_generic => quote!(#path::Origin<#runtime, #path::#inst>), - Some(inst) => quote!(#path::Origin<#path::#inst>), - None if part_is_generic => quote!(#path::Origin<#runtime>), - None => quote!(#path::Origin), - }; - - let doc_string = get_intra_doc_string(" Convert to runtime origin using", &path.module_name()); - let attr = pallet.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { - let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) - .expect("was successfully parsed before; qed"); - quote! { - #acc - #attr - } - }); - - quote! { - #attr - impl From<#pallet_origin> for OriginCaller { - fn from(x: #pallet_origin) -> Self { - OriginCaller::#variant_name(x) - } - } - - #attr - impl From<#pallet_origin> for RuntimeOrigin { - #[doc = #doc_string] - fn from(x: #pallet_origin) -> Self { - let x: OriginCaller = x.into(); - x.into() - } - } - - #attr - impl From for #scrate::__private::sp_std::result::Result<#pallet_origin, RuntimeOrigin> { - /// NOTE: converting to pallet origin loses the origin filter information. - fn from(val: RuntimeOrigin) -> Self { - if let OriginCaller::#variant_name(l) = val.caller { - Ok(l) - } else { - Err(val) - } - } - } - - #attr - impl TryFrom for #pallet_origin { - type Error = OriginCaller; - fn try_from( - x: OriginCaller, - ) -> #scrate::__private::sp_std::result::Result<#pallet_origin, OriginCaller> { - if let OriginCaller::#variant_name(l) = x { - Ok(l) - } else { - Err(x) - } - } - } - - #attr - impl<'a> TryFrom<&'a OriginCaller> for &'a #pallet_origin { - type Error = (); - fn try_from( - x: &'a OriginCaller, - ) -> #scrate::__private::sp_std::result::Result<&'a #pallet_origin, ()> { - if let OriginCaller::#variant_name(l) = x { - Ok(&l) - } else { - Err(()) - } - } - } - - #attr - impl<'a> TryFrom<&'a RuntimeOrigin> for &'a #pallet_origin { - type Error = (); - fn try_from( - x: &'a RuntimeOrigin, - ) -> #scrate::__private::sp_std::result::Result<&'a #pallet_origin, ()> { - if let OriginCaller::#variant_name(l) = &x.caller { - Ok(&l) - } else { - Err(()) - } - } - } - } + let path = &pallet.path; + let variant_name = &pallet.name; + + let part_is_generic = !generics.params.is_empty(); + let pallet_origin = match instance { + Some(inst) if part_is_generic => quote!(#path::Origin<#runtime, #path::#inst>), + Some(inst) => quote!(#path::Origin<#path::#inst>), + None if part_is_generic => quote!(#path::Origin<#runtime>), + None => quote!(#path::Origin), + }; + + let doc_string = get_intra_doc_string(" Convert to runtime origin using", &path.module_name()); + let attr = pallet + .cfg_pattern + .iter() + .fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); + + quote! { + #attr + impl From<#pallet_origin> for OriginCaller { + fn from(x: #pallet_origin) -> Self { + OriginCaller::#variant_name(x) + } + } + + #attr + impl From<#pallet_origin> for RuntimeOrigin { + #[doc = #doc_string] + fn from(x: #pallet_origin) -> Self { + let x: OriginCaller = x.into(); + x.into() + } + } + + #attr + impl From for #scrate::__private::sp_std::result::Result<#pallet_origin, RuntimeOrigin> { + /// NOTE: converting to pallet origin loses the origin filter information. + fn from(val: RuntimeOrigin) -> Self { + if let OriginCaller::#variant_name(l) = val.caller { + Ok(l) + } else { + Err(val) + } + } + } + + #attr + impl TryFrom for #pallet_origin { + type Error = OriginCaller; + fn try_from( + x: OriginCaller, + ) -> #scrate::__private::sp_std::result::Result<#pallet_origin, OriginCaller> { + if let OriginCaller::#variant_name(l) = x { + Ok(l) + } else { + Err(x) + } + } + } + + #attr + impl<'a> TryFrom<&'a OriginCaller> for &'a #pallet_origin { + type Error = (); + fn try_from( + x: &'a OriginCaller, + ) -> #scrate::__private::sp_std::result::Result<&'a #pallet_origin, ()> { + if let OriginCaller::#variant_name(l) = x { + Ok(&l) + } else { + Err(()) + } + } + } + + #attr + impl<'a> TryFrom<&'a RuntimeOrigin> for &'a #pallet_origin { + type Error = (); + fn try_from( + x: &'a RuntimeOrigin, + ) -> #scrate::__private::sp_std::result::Result<&'a #pallet_origin, ()> { + if let OriginCaller::#variant_name(l) = &x.caller { + Ok(&l) + } else { + Err(()) + } + } + } + } } // Get the actual documentation using the doc information and system path name fn get_intra_doc_string(doc_info: &str, system_path_name: &String) -> String { - format!(" {} [`{}::Config::BaseCallFilter`].", doc_info, system_path_name) + format!( + " {} [`{}::Config::BaseCallFilter`].", + doc_info, system_path_name + ) } diff --git a/support/procedural-fork/src/construct_runtime/expand/outer_enums.rs b/support/procedural-fork/src/construct_runtime/expand/outer_enums.rs index 80b242ccb..28e39c7a2 100644 --- a/support/procedural-fork/src/construct_runtime/expand/outer_enums.rs +++ b/support/procedural-fork/src/construct_runtime/expand/outer_enums.rs @@ -24,37 +24,37 @@ use syn::{Generics, Ident}; /// Represents the types supported for creating an outer enum. #[derive(Clone, Copy, PartialEq)] pub enum OuterEnumType { - /// Collects the Event enums from all pallets. - Event, - /// Collects the Error enums from all pallets. - Error, + /// Collects the Event enums from all pallets. + Event, + /// Collects the Error enums from all pallets. + Error, } impl OuterEnumType { - /// The name of the structure this enum represents. - fn struct_name(&self) -> &str { - match self { - OuterEnumType::Event => "RuntimeEvent", - OuterEnumType::Error => "RuntimeError", - } - } + /// The name of the structure this enum represents. + fn struct_name(&self) -> &str { + match self { + OuterEnumType::Event => "RuntimeEvent", + OuterEnumType::Error => "RuntimeError", + } + } - /// The name of the variant (ie `Event` or `Error`). - fn variant_name(&self) -> &str { - match self { - OuterEnumType::Event => "Event", - OuterEnumType::Error => "Error", - } - } + /// The name of the variant (ie `Event` or `Error`). + fn variant_name(&self) -> &str { + match self { + OuterEnumType::Event => "Event", + OuterEnumType::Error => "Error", + } + } } impl ToTokens for OuterEnumType { - fn to_tokens(&self, tokens: &mut TokenStream) { - match self { - OuterEnumType::Event => quote!(Event).to_tokens(tokens), - OuterEnumType::Error => quote!(Error).to_tokens(tokens), - } - } + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + OuterEnumType::Event => quote!(Event).to_tokens(tokens), + OuterEnumType::Error => quote!(Error).to_tokens(tokens), + } + } } /// Create an outer enum that encapsulates all pallets as variants. @@ -84,196 +84,207 @@ impl ToTokens for OuterEnumType { /// /// Notice that the pallet index is preserved using the `#[codec(index = ..)]` attribute. pub fn expand_outer_enum( - runtime: &Ident, - pallet_decls: &[Pallet], - scrate: &TokenStream, - enum_ty: OuterEnumType, + runtime: &Ident, + pallet_decls: &[Pallet], + scrate: &TokenStream, + enum_ty: OuterEnumType, ) -> syn::Result { - // Stores all pallet variants. - let mut enum_variants = TokenStream::new(); - // Generates the enum conversion between the `Runtime` outer enum and the pallet's enum. - let mut enum_conversions = TokenStream::new(); - // Specific for events to query via `is_event_part_defined!`. - let mut query_enum_part_macros = Vec::new(); + // Stores all pallet variants. + let mut enum_variants = TokenStream::new(); + // Generates the enum conversion between the `Runtime` outer enum and the pallet's enum. + let mut enum_conversions = TokenStream::new(); + // Specific for events to query via `is_event_part_defined!`. + let mut query_enum_part_macros = Vec::new(); - let enum_name_str = enum_ty.variant_name(); - let enum_name_ident = Ident::new(enum_ty.struct_name(), Span::call_site()); + let enum_name_str = enum_ty.variant_name(); + let enum_name_ident = Ident::new(enum_ty.struct_name(), Span::call_site()); - for pallet_decl in pallet_decls { - let Some(pallet_entry) = pallet_decl.find_part(enum_name_str) else { continue }; + for pallet_decl in pallet_decls { + let Some(pallet_entry) = pallet_decl.find_part(enum_name_str) else { + continue; + }; - let path = &pallet_decl.path; - let pallet_name = &pallet_decl.name; - let index = pallet_decl.index; - let instance = pallet_decl.instance.as_ref(); - let generics = &pallet_entry.generics; + let path = &pallet_decl.path; + let pallet_name = &pallet_decl.name; + let index = pallet_decl.index; + let instance = pallet_decl.instance.as_ref(); + let generics = &pallet_entry.generics; - if instance.is_some() && generics.params.is_empty() { - let msg = format!( - "Instantiable pallet with no generic `{}` cannot \ + if instance.is_some() && generics.params.is_empty() { + let msg = format!( + "Instantiable pallet with no generic `{}` cannot \ be constructed: pallet `{}` must have generic `{}`", - enum_name_str, pallet_name, enum_name_str, - ); - return Err(syn::Error::new(pallet_name.span(), msg)) - } + enum_name_str, pallet_name, enum_name_str, + ); + return Err(syn::Error::new(pallet_name.span(), msg)); + } - let part_is_generic = !generics.params.is_empty(); - let pallet_enum = match (instance, part_is_generic) { - (Some(inst), true) => quote!(#path::#enum_ty::<#runtime, #path::#inst>), - (Some(inst), false) => quote!(#path::#enum_ty::<#path::#inst>), - (None, true) => quote!(#path::#enum_ty::<#runtime>), - (None, false) => quote!(#path::#enum_ty), - }; + let part_is_generic = !generics.params.is_empty(); + let pallet_enum = match (instance, part_is_generic) { + (Some(inst), true) => quote!(#path::#enum_ty::<#runtime, #path::#inst>), + (Some(inst), false) => quote!(#path::#enum_ty::<#path::#inst>), + (None, true) => quote!(#path::#enum_ty::<#runtime>), + (None, false) => quote!(#path::#enum_ty), + }; - enum_variants.extend(expand_enum_variant( - runtime, - pallet_decl, - index, - instance, - generics, - enum_ty, - )); - enum_conversions.extend(expand_enum_conversion( - pallet_decl, - &pallet_enum, - &enum_name_ident, - )); + enum_variants.extend(expand_enum_variant( + runtime, + pallet_decl, + index, + instance, + generics, + enum_ty, + )); + enum_conversions.extend(expand_enum_conversion( + pallet_decl, + &pallet_enum, + &enum_name_ident, + )); - if enum_ty == OuterEnumType::Event { - query_enum_part_macros.push(quote! { - #path::__substrate_event_check::is_event_part_defined!(#pallet_name); - }); - } - } + if enum_ty == OuterEnumType::Event { + query_enum_part_macros.push(quote! { + #path::__substrate_event_check::is_event_part_defined!(#pallet_name); + }); + } + } - // Derives specific for the event. - let event_custom_derives = - if enum_ty == OuterEnumType::Event { quote!(Clone, PartialEq, Eq,) } else { quote!() }; + // Derives specific for the event. + let event_custom_derives = if enum_ty == OuterEnumType::Event { + quote!(Clone, PartialEq, Eq,) + } else { + quote!() + }; - // Implementation specific for errors. - let error_custom_impl = generate_error_impl(scrate, enum_ty); + // Implementation specific for errors. + let error_custom_impl = generate_error_impl(scrate, enum_ty); - Ok(quote! { - #( #query_enum_part_macros )* + Ok(quote! { + #( #query_enum_part_macros )* - #[derive( - #event_custom_derives - #scrate::__private::codec::Encode, - #scrate::__private::codec::Decode, - #scrate::__private::scale_info::TypeInfo, - #scrate::__private::RuntimeDebug, - )] - #[allow(non_camel_case_types)] - pub enum #enum_name_ident { - #enum_variants - } + #[derive( + #event_custom_derives + #scrate::__private::codec::Encode, + #scrate::__private::codec::Decode, + #scrate::__private::scale_info::TypeInfo, + #scrate::__private::RuntimeDebug, + )] + #[allow(non_camel_case_types)] + pub enum #enum_name_ident { + #enum_variants + } - #enum_conversions + #enum_conversions - #error_custom_impl - }) + #error_custom_impl + }) } fn expand_enum_variant( - runtime: &Ident, - pallet: &Pallet, - index: u8, - instance: Option<&Ident>, - generics: &Generics, - enum_ty: OuterEnumType, + runtime: &Ident, + pallet: &Pallet, + index: u8, + instance: Option<&Ident>, + generics: &Generics, + enum_ty: OuterEnumType, ) -> TokenStream { - let path = &pallet.path; - let variant_name = &pallet.name; - let part_is_generic = !generics.params.is_empty(); - let attr = pallet.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { - let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) - .expect("was successfully parsed before; qed"); - quote! { - #acc - #attr - } - }); + let path = &pallet.path; + let variant_name = &pallet.name; + let part_is_generic = !generics.params.is_empty(); + let attr = pallet + .cfg_pattern + .iter() + .fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); - match instance { - Some(inst) if part_is_generic => quote! { - #attr - #[codec(index = #index)] - #variant_name(#path::#enum_ty<#runtime, #path::#inst>), - }, - Some(inst) => quote! { - #attr - #[codec(index = #index)] - #variant_name(#path::#enum_ty<#path::#inst>), - }, - None if part_is_generic => quote! { - #attr - #[codec(index = #index)] - #variant_name(#path::#enum_ty<#runtime>), - }, - None => quote! { - #attr - #[codec(index = #index)] - #variant_name(#path::#enum_ty), - }, - } + match instance { + Some(inst) if part_is_generic => quote! { + #attr + #[codec(index = #index)] + #variant_name(#path::#enum_ty<#runtime, #path::#inst>), + }, + Some(inst) => quote! { + #attr + #[codec(index = #index)] + #variant_name(#path::#enum_ty<#path::#inst>), + }, + None if part_is_generic => quote! { + #attr + #[codec(index = #index)] + #variant_name(#path::#enum_ty<#runtime>), + }, + None => quote! { + #attr + #[codec(index = #index)] + #variant_name(#path::#enum_ty), + }, + } } fn expand_enum_conversion( - pallet: &Pallet, - pallet_enum: &TokenStream, - enum_name_ident: &Ident, + pallet: &Pallet, + pallet_enum: &TokenStream, + enum_name_ident: &Ident, ) -> TokenStream { - let variant_name = &pallet.name; - let attr = pallet.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { - let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) - .expect("was successfully parsed before; qed"); - quote! { - #acc - #attr - } - }); + let variant_name = &pallet.name; + let attr = pallet + .cfg_pattern + .iter() + .fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); - quote! { - #attr - impl From<#pallet_enum> for #enum_name_ident { - fn from(x: #pallet_enum) -> Self { - #enum_name_ident - ::#variant_name(x) - } - } - #attr - impl TryInto<#pallet_enum> for #enum_name_ident { - type Error = (); + quote! { + #attr + impl From<#pallet_enum> for #enum_name_ident { + fn from(x: #pallet_enum) -> Self { + #enum_name_ident + ::#variant_name(x) + } + } + #attr + impl TryInto<#pallet_enum> for #enum_name_ident { + type Error = (); - fn try_into(self) -> ::core::result::Result<#pallet_enum, Self::Error> { - match self { - Self::#variant_name(evt) => Ok(evt), - _ => Err(()), - } - } - } - } + fn try_into(self) -> ::core::result::Result<#pallet_enum, Self::Error> { + match self { + Self::#variant_name(evt) => Ok(evt), + _ => Err(()), + } + } + } + } } fn generate_error_impl(scrate: &TokenStream, enum_ty: OuterEnumType) -> TokenStream { - // Implementation is specific to `Error`s. - if enum_ty == OuterEnumType::Event { - return quote! {} - } + // Implementation is specific to `Error`s. + if enum_ty == OuterEnumType::Event { + return quote! {}; + } - let enum_name_ident = Ident::new(enum_ty.struct_name(), Span::call_site()); + let enum_name_ident = Ident::new(enum_ty.struct_name(), Span::call_site()); - quote! { - impl #enum_name_ident { - /// Optionally convert the `DispatchError` into the `RuntimeError`. - /// - /// Returns `Some` if the error matches the `DispatchError::Module` variant, otherwise `None`. - pub fn from_dispatch_error(err: #scrate::sp_runtime::DispatchError) -> Option { - let #scrate::sp_runtime::DispatchError::Module(module_error) = err else { return None }; + quote! { + impl #enum_name_ident { + /// Optionally convert the `DispatchError` into the `RuntimeError`. + /// + /// Returns `Some` if the error matches the `DispatchError::Module` variant, otherwise `None`. + pub fn from_dispatch_error(err: #scrate::sp_runtime::DispatchError) -> Option { + let #scrate::sp_runtime::DispatchError::Module(module_error) = err else { return None }; - let bytes = #scrate::__private::codec::Encode::encode(&module_error); - #scrate::__private::codec::Decode::decode(&mut &bytes[..]).ok() - } - } - } + let bytes = #scrate::__private::codec::Encode::encode(&module_error); + #scrate::__private::codec::Decode::decode(&mut &bytes[..]).ok() + } + } + } } diff --git a/support/procedural-fork/src/construct_runtime/expand/slash_reason.rs b/support/procedural-fork/src/construct_runtime/expand/slash_reason.rs index 892b842b1..0695d8102 100644 --- a/support/procedural-fork/src/construct_runtime/expand/slash_reason.rs +++ b/support/procedural-fork/src/construct_runtime/expand/slash_reason.rs @@ -21,44 +21,44 @@ use proc_macro2::TokenStream; use quote::quote; pub fn expand_outer_slash_reason(pallet_decls: &[Pallet], scrate: &TokenStream) -> TokenStream { - let mut conversion_fns = Vec::new(); - let mut slash_reason_variants = Vec::new(); - for decl in pallet_decls { - if let Some(_) = decl.find_part("SlashReason") { - let variant_name = &decl.name; - let path = &decl.path; - let index = decl.index; - let instance = decl.instance.as_ref(); + let mut conversion_fns = Vec::new(); + let mut slash_reason_variants = Vec::new(); + for decl in pallet_decls { + if let Some(_) = decl.find_part("SlashReason") { + let variant_name = &decl.name; + let path = &decl.path; + let index = decl.index; + let instance = decl.instance.as_ref(); - conversion_fns.push(composite_helper::expand_conversion_fn( - "SlashReason", - path, - instance, - variant_name, - )); + conversion_fns.push(composite_helper::expand_conversion_fn( + "SlashReason", + path, + instance, + variant_name, + )); - slash_reason_variants.push(composite_helper::expand_variant( - "SlashReason", - index, - path, - instance, - variant_name, - )); - } - } + slash_reason_variants.push(composite_helper::expand_variant( + "SlashReason", + index, + path, + instance, + variant_name, + )); + } + } - quote! { - /// A reason for slashing funds. - #[derive( - Copy, Clone, Eq, PartialEq, - #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, - #scrate::__private::scale_info::TypeInfo, - #scrate::__private::RuntimeDebug, - )] - pub enum RuntimeSlashReason { - #( #slash_reason_variants )* - } + quote! { + /// A reason for slashing funds. + #[derive( + Copy, Clone, Eq, PartialEq, + #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, + #scrate::__private::scale_info::TypeInfo, + #scrate::__private::RuntimeDebug, + )] + pub enum RuntimeSlashReason { + #( #slash_reason_variants )* + } - #( #conversion_fns )* - } + #( #conversion_fns )* + } } diff --git a/support/procedural-fork/src/construct_runtime/expand/task.rs b/support/procedural-fork/src/construct_runtime/expand/task.rs index 6531c0e9e..94a5f52bb 100644 --- a/support/procedural-fork/src/construct_runtime/expand/task.rs +++ b/support/procedural-fork/src/construct_runtime/expand/task.rs @@ -21,111 +21,111 @@ use quote::quote; /// Expands aggregate `RuntimeTask` enum. pub fn expand_outer_task( - runtime_name: &Ident, - pallet_decls: &[Pallet], - scrate: &TokenStream2, + runtime_name: &Ident, + pallet_decls: &[Pallet], + scrate: &TokenStream2, ) -> TokenStream2 { - let mut from_impls = Vec::new(); - let mut task_variants = Vec::new(); - let mut variant_names = Vec::new(); - let mut task_paths = Vec::new(); - for decl in pallet_decls { - if decl.find_part("Task").is_none() { - continue - } - - let variant_name = &decl.name; - let path = &decl.path; - let index = decl.index; - - from_impls.push(quote! { - impl From<#path::Task<#runtime_name>> for RuntimeTask { - fn from(hr: #path::Task<#runtime_name>) -> Self { - RuntimeTask::#variant_name(hr) - } - } - - impl TryInto<#path::Task<#runtime_name>> for RuntimeTask { - type Error = (); - - fn try_into(self) -> Result<#path::Task<#runtime_name>, Self::Error> { - match self { - RuntimeTask::#variant_name(hr) => Ok(hr), - _ => Err(()), - } - } - } - }); - - task_variants.push(quote! { - #[codec(index = #index)] - #variant_name(#path::Task<#runtime_name>), - }); - - variant_names.push(quote!(#variant_name)); - - task_paths.push(quote!(#path::Task)); - } - - let prelude = quote!(#scrate::traits::tasks::__private); - - const INCOMPLETE_MATCH_QED: &'static str = - "cannot have an instantiated RuntimeTask without some Task variant in the runtime. QED"; - - let output = quote! { - /// An aggregation of all `Task` enums across all pallets included in the current runtime. - #[derive( - Clone, Eq, PartialEq, - #scrate::__private::codec::Encode, - #scrate::__private::codec::Decode, - #scrate::__private::scale_info::TypeInfo, - #scrate::__private::RuntimeDebug, - )] - pub enum RuntimeTask { - #( #task_variants )* - } - - #[automatically_derived] - impl #scrate::traits::Task for RuntimeTask { - type Enumeration = #prelude::IntoIter; - - fn is_valid(&self) -> bool { - match self { - #(RuntimeTask::#variant_names(val) => val.is_valid(),)* - _ => unreachable!(#INCOMPLETE_MATCH_QED), - } - } - - fn run(&self) -> Result<(), #scrate::traits::tasks::__private::DispatchError> { - match self { - #(RuntimeTask::#variant_names(val) => val.run(),)* - _ => unreachable!(#INCOMPLETE_MATCH_QED), - } - } - - fn weight(&self) -> #scrate::pallet_prelude::Weight { - match self { - #(RuntimeTask::#variant_names(val) => val.weight(),)* - _ => unreachable!(#INCOMPLETE_MATCH_QED), - } - } - - fn task_index(&self) -> u32 { - match self { - #(RuntimeTask::#variant_names(val) => val.task_index(),)* - _ => unreachable!(#INCOMPLETE_MATCH_QED), - } - } - - fn iter() -> Self::Enumeration { - let mut all_tasks = Vec::new(); - #(all_tasks.extend(#task_paths::iter().map(RuntimeTask::from).collect::>());)* - all_tasks.into_iter() - } - } - - #( #from_impls )* - }; - - output + let mut from_impls = Vec::new(); + let mut task_variants = Vec::new(); + let mut variant_names = Vec::new(); + let mut task_paths = Vec::new(); + for decl in pallet_decls { + if decl.find_part("Task").is_none() { + continue; + } + + let variant_name = &decl.name; + let path = &decl.path; + let index = decl.index; + + from_impls.push(quote! { + impl From<#path::Task<#runtime_name>> for RuntimeTask { + fn from(hr: #path::Task<#runtime_name>) -> Self { + RuntimeTask::#variant_name(hr) + } + } + + impl TryInto<#path::Task<#runtime_name>> for RuntimeTask { + type Error = (); + + fn try_into(self) -> Result<#path::Task<#runtime_name>, Self::Error> { + match self { + RuntimeTask::#variant_name(hr) => Ok(hr), + _ => Err(()), + } + } + } + }); + + task_variants.push(quote! { + #[codec(index = #index)] + #variant_name(#path::Task<#runtime_name>), + }); + + variant_names.push(quote!(#variant_name)); + + task_paths.push(quote!(#path::Task)); + } + + let prelude = quote!(#scrate::traits::tasks::__private); + + const INCOMPLETE_MATCH_QED: &'static str = + "cannot have an instantiated RuntimeTask without some Task variant in the runtime. QED"; + + let output = quote! { + /// An aggregation of all `Task` enums across all pallets included in the current runtime. + #[derive( + Clone, Eq, PartialEq, + #scrate::__private::codec::Encode, + #scrate::__private::codec::Decode, + #scrate::__private::scale_info::TypeInfo, + #scrate::__private::RuntimeDebug, + )] + pub enum RuntimeTask { + #( #task_variants )* + } + + #[automatically_derived] + impl #scrate::traits::Task for RuntimeTask { + type Enumeration = #prelude::IntoIter; + + fn is_valid(&self) -> bool { + match self { + #(RuntimeTask::#variant_names(val) => val.is_valid(),)* + _ => unreachable!(#INCOMPLETE_MATCH_QED), + } + } + + fn run(&self) -> Result<(), #scrate::traits::tasks::__private::DispatchError> { + match self { + #(RuntimeTask::#variant_names(val) => val.run(),)* + _ => unreachable!(#INCOMPLETE_MATCH_QED), + } + } + + fn weight(&self) -> #scrate::pallet_prelude::Weight { + match self { + #(RuntimeTask::#variant_names(val) => val.weight(),)* + _ => unreachable!(#INCOMPLETE_MATCH_QED), + } + } + + fn task_index(&self) -> u32 { + match self { + #(RuntimeTask::#variant_names(val) => val.task_index(),)* + _ => unreachable!(#INCOMPLETE_MATCH_QED), + } + } + + fn iter() -> Self::Enumeration { + let mut all_tasks = Vec::new(); + #(all_tasks.extend(#task_paths::iter().map(RuntimeTask::from).collect::>());)* + all_tasks.into_iter() + } + } + + #( #from_impls )* + }; + + output } diff --git a/support/procedural-fork/src/construct_runtime/expand/unsigned.rs b/support/procedural-fork/src/construct_runtime/expand/unsigned.rs index 33aadba0d..109f7081c 100644 --- a/support/procedural-fork/src/construct_runtime/expand/unsigned.rs +++ b/support/procedural-fork/src/construct_runtime/expand/unsigned.rs @@ -22,68 +22,71 @@ use std::str::FromStr; use syn::Ident; pub fn expand_outer_validate_unsigned( - runtime: &Ident, - pallet_decls: &[Pallet], - scrate: &TokenStream, + runtime: &Ident, + pallet_decls: &[Pallet], + scrate: &TokenStream, ) -> TokenStream { - let mut pallet_names = Vec::new(); - let mut pallet_attrs = Vec::new(); - let mut query_validate_unsigned_part_macros = Vec::new(); + let mut pallet_names = Vec::new(); + let mut pallet_attrs = Vec::new(); + let mut query_validate_unsigned_part_macros = Vec::new(); - for pallet_decl in pallet_decls { - if pallet_decl.exists_part("ValidateUnsigned") { - let name = &pallet_decl.name; - let path = &pallet_decl.path; - let attr = pallet_decl.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| { - let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) - .expect("was successfully parsed before; qed"); - quote! { - #acc - #attr - } - }); + for pallet_decl in pallet_decls { + if pallet_decl.exists_part("ValidateUnsigned") { + let name = &pallet_decl.name; + let path = &pallet_decl.path; + let attr = pallet_decl + .cfg_pattern + .iter() + .fold(TokenStream::new(), |acc, pattern| { + let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }); - pallet_names.push(name); - pallet_attrs.push(attr); - query_validate_unsigned_part_macros.push(quote! { + pallet_names.push(name); + pallet_attrs.push(attr); + query_validate_unsigned_part_macros.push(quote! { #path::__substrate_validate_unsigned_check::is_validate_unsigned_part_defined!(#name); }); - } - } + } + } - quote! { - #( #query_validate_unsigned_part_macros )* + quote! { + #( #query_validate_unsigned_part_macros )* - impl #scrate::unsigned::ValidateUnsigned for #runtime { - type Call = RuntimeCall; + impl #scrate::unsigned::ValidateUnsigned for #runtime { + type Call = RuntimeCall; - fn pre_dispatch(call: &Self::Call) -> Result<(), #scrate::unsigned::TransactionValidityError> { - #[allow(unreachable_patterns)] - match call { - #( - #pallet_attrs - RuntimeCall::#pallet_names(inner_call) => #pallet_names::pre_dispatch(inner_call), - )* - // pre-dispatch should not stop inherent extrinsics, validation should prevent - // including arbitrary (non-inherent) extrinsics to blocks. - _ => Ok(()), - } - } + fn pre_dispatch(call: &Self::Call) -> Result<(), #scrate::unsigned::TransactionValidityError> { + #[allow(unreachable_patterns)] + match call { + #( + #pallet_attrs + RuntimeCall::#pallet_names(inner_call) => #pallet_names::pre_dispatch(inner_call), + )* + // pre-dispatch should not stop inherent extrinsics, validation should prevent + // including arbitrary (non-inherent) extrinsics to blocks. + _ => Ok(()), + } + } - fn validate_unsigned( - #[allow(unused_variables)] - source: #scrate::unsigned::TransactionSource, - call: &Self::Call, - ) -> #scrate::unsigned::TransactionValidity { - #[allow(unreachable_patterns)] - match call { - #( - #pallet_attrs - RuntimeCall::#pallet_names(inner_call) => #pallet_names::validate_unsigned(source, inner_call), - )* - _ => #scrate::unsigned::UnknownTransaction::NoUnsignedValidator.into(), - } - } - } - } + fn validate_unsigned( + #[allow(unused_variables)] + source: #scrate::unsigned::TransactionSource, + call: &Self::Call, + ) -> #scrate::unsigned::TransactionValidity { + #[allow(unreachable_patterns)] + match call { + #( + #pallet_attrs + RuntimeCall::#pallet_names(inner_call) => #pallet_names::validate_unsigned(source, inner_call), + )* + _ => #scrate::unsigned::UnknownTransaction::NoUnsignedValidator.into(), + } + } + } + } } diff --git a/support/procedural-fork/src/construct_runtime/mod.rs b/support/procedural-fork/src/construct_runtime/mod.rs index b083abbb2..de688b3d6 100644 --- a/support/procedural-fork/src/construct_runtime/mod.rs +++ b/support/procedural-fork/src/construct_runtime/mod.rs @@ -214,7 +214,7 @@ pub(crate) mod parse; use crate::pallet::parse::helper::two128_str; use cfg_expr::Predicate; use frame_support_procedural_tools::{ - generate_access_from_frame_or_crate, generate_crate_access, generate_hidden_includes, + generate_access_from_frame_or_crate, generate_crate_access, generate_hidden_includes, }; use itertools::Itertools; use parse::{ExplicitRuntimeDeclaration, ImplicitRuntimeDeclaration, Pallet, RuntimeDeclaration}; @@ -230,48 +230,48 @@ const SYSTEM_PALLET_NAME: &str = "System"; /// Implementation of `construct_runtime` macro. Either expand to some code which will call /// `construct_runtime` again, or expand to the final runtime definition. pub fn construct_runtime(input: TokenStream) -> TokenStream { - let input_copy = input.clone(); - let definition = syn::parse_macro_input!(input as RuntimeDeclaration); - - let (check_pallet_number_res, res) = match definition { - RuntimeDeclaration::Implicit(implicit_def) => ( - check_pallet_number(input_copy.clone().into(), implicit_def.pallets.len()), - construct_runtime_implicit_to_explicit(input_copy.into(), implicit_def), - ), - RuntimeDeclaration::Explicit(explicit_decl) => ( - check_pallet_number(input_copy.clone().into(), explicit_decl.pallets.len()), - construct_runtime_explicit_to_explicit_expanded(input_copy.into(), explicit_decl), - ), - RuntimeDeclaration::ExplicitExpanded(explicit_decl) => ( - check_pallet_number(input_copy.into(), explicit_decl.pallets.len()), - construct_runtime_final_expansion(explicit_decl), - ), - }; - - let res = res.unwrap_or_else(|e| e.to_compile_error()); - - // We want to provide better error messages to the user and thus, handle the error here - // separately. If there is an error, we print the error and still generate all of the code to - // get in overall less errors for the user. - let res = if let Err(error) = check_pallet_number_res { - let error = error.to_compile_error(); - - quote! { - #error - - #res - } - } else { - res - }; - - let res = expander::Expander::new("construct_runtime") - .dry(std::env::var("EXPAND_MACROS").is_err()) - .verbose(true) - .write_to_out_dir(res) - .expect("Does not fail because of IO in OUT_DIR; qed"); - - res.into() + let input_copy = input.clone(); + let definition = syn::parse_macro_input!(input as RuntimeDeclaration); + + let (check_pallet_number_res, res) = match definition { + RuntimeDeclaration::Implicit(implicit_def) => ( + check_pallet_number(input_copy.clone().into(), implicit_def.pallets.len()), + construct_runtime_implicit_to_explicit(input_copy.into(), implicit_def), + ), + RuntimeDeclaration::Explicit(explicit_decl) => ( + check_pallet_number(input_copy.clone().into(), explicit_decl.pallets.len()), + construct_runtime_explicit_to_explicit_expanded(input_copy.into(), explicit_decl), + ), + RuntimeDeclaration::ExplicitExpanded(explicit_decl) => ( + check_pallet_number(input_copy.into(), explicit_decl.pallets.len()), + construct_runtime_final_expansion(explicit_decl), + ), + }; + + let res = res.unwrap_or_else(|e| e.to_compile_error()); + + // We want to provide better error messages to the user and thus, handle the error here + // separately. If there is an error, we print the error and still generate all of the code to + // get in overall less errors for the user. + let res = if let Err(error) = check_pallet_number_res { + let error = error.to_compile_error(); + + quote! { + #error + + #res + } + } else { + res + }; + + let res = expander::Expander::new("construct_runtime") + .dry(std::env::var("EXPAND_MACROS").is_err()) + .verbose(true) + .write_to_out_dir(res) + .expect("Does not fail because of IO in OUT_DIR; qed"); + + res.into() } /// All pallets that have implicit pallet parts (ie `System: frame_system`) are @@ -282,30 +282,37 @@ pub fn construct_runtime(input: TokenStream) -> TokenStream { /// /// For more details, please refer to the root documentation. fn construct_runtime_implicit_to_explicit( - input: TokenStream2, - definition: ImplicitRuntimeDeclaration, + input: TokenStream2, + definition: ImplicitRuntimeDeclaration, ) -> Result { - let frame_support = generate_access_from_frame_or_crate("frame-support")?; - let mut expansion = quote::quote!( - #frame_support::construct_runtime! { #input } - ); - for pallet in definition.pallets.iter().filter(|pallet| pallet.pallet_parts.is_none()) { - let pallet_path = &pallet.path; - let pallet_name = &pallet.name; - let pallet_instance = pallet.instance.as_ref().map(|instance| quote::quote!(::<#instance>)); - expansion = quote::quote!( - #frame_support::__private::tt_call! { - macro = [{ #pallet_path::tt_default_parts }] - your_tt_return = [{ #frame_support::__private::tt_return }] - ~~> #frame_support::match_and_insert! { - target = [{ #expansion }] - pattern = [{ #pallet_name: #pallet_path #pallet_instance }] - } - } - ); - } - - Ok(expansion) + let frame_support = generate_access_from_frame_or_crate("frame-support")?; + let mut expansion = quote::quote!( + #frame_support::construct_runtime! { #input } + ); + for pallet in definition + .pallets + .iter() + .filter(|pallet| pallet.pallet_parts.is_none()) + { + let pallet_path = &pallet.path; + let pallet_name = &pallet.name; + let pallet_instance = pallet + .instance + .as_ref() + .map(|instance| quote::quote!(::<#instance>)); + expansion = quote::quote!( + #frame_support::__private::tt_call! { + macro = [{ #pallet_path::tt_default_parts }] + your_tt_return = [{ #frame_support::__private::tt_return }] + ~~> #frame_support::match_and_insert! { + target = [{ #expansion }] + pattern = [{ #pallet_name: #pallet_path #pallet_instance }] + } + } + ); + } + + Ok(expansion) } /// All pallets that have @@ -318,264 +325,283 @@ fn construct_runtime_implicit_to_explicit( /// /// For more details, please refer to the root documentation. fn construct_runtime_explicit_to_explicit_expanded( - input: TokenStream2, - definition: ExplicitRuntimeDeclaration, + input: TokenStream2, + definition: ExplicitRuntimeDeclaration, ) -> Result { - let frame_support = generate_access_from_frame_or_crate("frame-support")?; - let mut expansion = quote::quote!( - #frame_support::construct_runtime! { #input } - ); - for pallet in definition.pallets.iter().filter(|pallet| !pallet.is_expanded) { - let pallet_path = &pallet.path; - let pallet_name = &pallet.name; - let pallet_instance = pallet.instance.as_ref().map(|instance| quote::quote!(::<#instance>)); - expansion = quote::quote!( - #frame_support::__private::tt_call! { - macro = [{ #pallet_path::tt_extra_parts }] - your_tt_return = [{ #frame_support::__private::tt_return }] - ~~> #frame_support::match_and_insert! { - target = [{ #expansion }] - pattern = [{ #pallet_name: #pallet_path #pallet_instance }] - } - } - ); - } - - Ok(expansion) + let frame_support = generate_access_from_frame_or_crate("frame-support")?; + let mut expansion = quote::quote!( + #frame_support::construct_runtime! { #input } + ); + for pallet in definition + .pallets + .iter() + .filter(|pallet| !pallet.is_expanded) + { + let pallet_path = &pallet.path; + let pallet_name = &pallet.name; + let pallet_instance = pallet + .instance + .as_ref() + .map(|instance| quote::quote!(::<#instance>)); + expansion = quote::quote!( + #frame_support::__private::tt_call! { + macro = [{ #pallet_path::tt_extra_parts }] + your_tt_return = [{ #frame_support::__private::tt_return }] + ~~> #frame_support::match_and_insert! { + target = [{ #expansion }] + pattern = [{ #pallet_name: #pallet_path #pallet_instance }] + } + } + ); + } + + Ok(expansion) } /// All pallets have explicit definition of parts, this will expand to the runtime declaration. fn construct_runtime_final_expansion( - definition: ExplicitRuntimeDeclaration, + definition: ExplicitRuntimeDeclaration, ) -> Result { - let ExplicitRuntimeDeclaration { name, pallets, pallets_token, where_section } = definition; - - let system_pallet = - pallets.iter().find(|decl| decl.name == SYSTEM_PALLET_NAME).ok_or_else(|| { - syn::Error::new( - pallets_token.span.join(), - "`System` pallet declaration is missing. \ + let ExplicitRuntimeDeclaration { + name, + pallets, + pallets_token, + where_section, + } = definition; + + let system_pallet = pallets + .iter() + .find(|decl| decl.name == SYSTEM_PALLET_NAME) + .ok_or_else(|| { + syn::Error::new( + pallets_token.span.join(), + "`System` pallet declaration is missing. \ Please add this line: `System: frame_system,`", - ) - })?; - if !system_pallet.cfg_pattern.is_empty() { - return Err(syn::Error::new( - system_pallet.name.span(), - "`System` pallet declaration is feature gated, please remove any `#[cfg]` attributes", - )) - } - - let features = pallets - .iter() - .filter_map(|decl| { - (!decl.cfg_pattern.is_empty()).then(|| { - decl.cfg_pattern.iter().flat_map(|attr| { - attr.predicates().filter_map(|pred| match pred { - Predicate::Feature(feat) => Some(feat), - Predicate::Test => Some("test"), - _ => None, - }) - }) - }) - }) - .flatten() - .collect::>(); - - 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 frame_system = generate_access_from_frame_or_crate("frame-system")?; - let block = quote!(<#name as #frame_system::Config>::Block); - let unchecked_extrinsic = quote!(<#block as #scrate::sp_runtime::traits::Block>::Extrinsic); - - let outer_event = - expand::expand_outer_enum(&name, &pallets, &scrate, expand::OuterEnumType::Event)?; - let outer_error = - expand::expand_outer_enum(&name, &pallets, &scrate, expand::OuterEnumType::Error)?; - - let outer_origin = expand::expand_outer_origin(&name, system_pallet, &pallets, &scrate)?; - let all_pallets = decl_all_pallets(&name, pallets.iter(), &features); - let pallet_to_index = decl_pallet_runtime_setup(&name, &pallets, &scrate); - - let dispatch = expand::expand_outer_dispatch(&name, system_pallet, &pallets, &scrate); - let tasks = expand::expand_outer_task(&name, &pallets, &scrate); - let metadata = expand::expand_runtime_metadata( - &name, - &pallets, - &scrate, - &unchecked_extrinsic, - &system_pallet.path, - ); - let outer_config = expand::expand_outer_config(&name, &pallets, &scrate); - let inherent = - expand::expand_outer_inherent(&name, &block, &unchecked_extrinsic, &pallets, &scrate); - let validate_unsigned = expand::expand_outer_validate_unsigned(&name, &pallets, &scrate); - let freeze_reason = expand::expand_outer_freeze_reason(&pallets, &scrate); - let hold_reason = expand::expand_outer_hold_reason(&pallets, &scrate); - let lock_id = expand::expand_outer_lock_id(&pallets, &scrate); - let slash_reason = expand::expand_outer_slash_reason(&pallets, &scrate); - let integrity_test = decl_integrity_test(&scrate); - let static_assertions = decl_static_assertions(&name, &pallets, &scrate); - - let warning = where_section.map_or(None, |where_section| { - Some( - proc_macro_warning::Warning::new_deprecated("WhereSection") - .old("use a `where` clause in `construct_runtime`") - .new( - "use `frame_system::Config` to set the `Block` type and delete this clause. + ) + })?; + if !system_pallet.cfg_pattern.is_empty() { + return Err(syn::Error::new( + system_pallet.name.span(), + "`System` pallet declaration is feature gated, please remove any `#[cfg]` attributes", + )); + } + + let features = pallets + .iter() + .filter_map(|decl| { + (!decl.cfg_pattern.is_empty()).then(|| { + decl.cfg_pattern.iter().flat_map(|attr| { + attr.predicates().filter_map(|pred| match pred { + Predicate::Feature(feat) => Some(feat), + Predicate::Test => Some("test"), + _ => None, + }) + }) + }) + }) + .flatten() + .collect::>(); + + 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 frame_system = generate_access_from_frame_or_crate("frame-system")?; + let block = quote!(<#name as #frame_system::Config>::Block); + let unchecked_extrinsic = quote!(<#block as #scrate::sp_runtime::traits::Block>::Extrinsic); + + let outer_event = + expand::expand_outer_enum(&name, &pallets, &scrate, expand::OuterEnumType::Event)?; + let outer_error = + expand::expand_outer_enum(&name, &pallets, &scrate, expand::OuterEnumType::Error)?; + + let outer_origin = expand::expand_outer_origin(&name, system_pallet, &pallets, &scrate)?; + let all_pallets = decl_all_pallets(&name, pallets.iter(), &features); + let pallet_to_index = decl_pallet_runtime_setup(&name, &pallets, &scrate); + + let dispatch = expand::expand_outer_dispatch(&name, system_pallet, &pallets, &scrate); + let tasks = expand::expand_outer_task(&name, &pallets, &scrate); + let metadata = expand::expand_runtime_metadata( + &name, + &pallets, + &scrate, + &unchecked_extrinsic, + &system_pallet.path, + ); + let outer_config = expand::expand_outer_config(&name, &pallets, &scrate); + let inherent = + expand::expand_outer_inherent(&name, &block, &unchecked_extrinsic, &pallets, &scrate); + let validate_unsigned = expand::expand_outer_validate_unsigned(&name, &pallets, &scrate); + let freeze_reason = expand::expand_outer_freeze_reason(&pallets, &scrate); + let hold_reason = expand::expand_outer_hold_reason(&pallets, &scrate); + let lock_id = expand::expand_outer_lock_id(&pallets, &scrate); + let slash_reason = expand::expand_outer_slash_reason(&pallets, &scrate); + let integrity_test = decl_integrity_test(&scrate); + let static_assertions = decl_static_assertions(&name, &pallets, &scrate); + + let warning = where_section.map_or(None, |where_section| { + Some( + proc_macro_warning::Warning::new_deprecated("WhereSection") + .old("use a `where` clause in `construct_runtime`") + .new( + "use `frame_system::Config` to set the `Block` type and delete this clause. It is planned to be removed in December 2023", - ) - .help_links(&["https://github.com/paritytech/substrate/pull/14437"]) - .span(where_section.span) - .build_or_panic(), - ) - }); - - let res = quote!( - #warning - - #scrate_decl - - // Prevent UncheckedExtrinsic to print unused warning. - const _: () = { - #[allow(unused)] - type __hidden_use_of_unchecked_extrinsic = #unchecked_extrinsic; - }; - - #[derive( - Clone, Copy, PartialEq, Eq, #scrate::sp_runtime::RuntimeDebug, - #scrate::__private::scale_info::TypeInfo - )] - pub struct #name; - impl #scrate::sp_runtime::traits::GetRuntimeBlockType for #name { - type RuntimeBlock = #block; - } - - // Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata. - // The function is implemented by calling `impl_runtime_apis!`. - // - // However, the `construct_runtime!` may be called without calling `impl_runtime_apis!`. - // Rely on the `Deref` trait to differentiate between a runtime that implements - // APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro construct_runtime!). - // - // Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function. - // `InternalConstructRuntime` is implemented by the `construct_runtime!` for Runtime references (`& Runtime`), - // while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`). - // - // Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!` - // when both macros are called; and will resolve an empty `runtime_metadata` when only the `construct_runtime!` - // is called. - - #[doc(hidden)] - trait InternalConstructRuntime { - #[inline(always)] - fn runtime_metadata(&self) -> #scrate::__private::sp_std::vec::Vec<#scrate::__private::metadata_ir::RuntimeApiMetadataIR> { - Default::default() - } - } - #[doc(hidden)] - impl InternalConstructRuntime for &#name {} + ) + .help_links(&["https://github.com/paritytech/substrate/pull/14437"]) + .span(where_section.span) + .build_or_panic(), + ) + }); + + let res = quote!( + #warning + + #scrate_decl + + // Prevent UncheckedExtrinsic to print unused warning. + const _: () = { + #[allow(unused)] + type __hidden_use_of_unchecked_extrinsic = #unchecked_extrinsic; + }; - #outer_event + #[derive( + Clone, Copy, PartialEq, Eq, #scrate::sp_runtime::RuntimeDebug, + #scrate::__private::scale_info::TypeInfo + )] + pub struct #name; + impl #scrate::sp_runtime::traits::GetRuntimeBlockType for #name { + type RuntimeBlock = #block; + } - #outer_error + // Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata. + // The function is implemented by calling `impl_runtime_apis!`. + // + // However, the `construct_runtime!` may be called without calling `impl_runtime_apis!`. + // Rely on the `Deref` trait to differentiate between a runtime that implements + // APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro construct_runtime!). + // + // Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function. + // `InternalConstructRuntime` is implemented by the `construct_runtime!` for Runtime references (`& Runtime`), + // while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`). + // + // Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!` + // when both macros are called; and will resolve an empty `runtime_metadata` when only the `construct_runtime!` + // is called. - #outer_origin + #[doc(hidden)] + trait InternalConstructRuntime { + #[inline(always)] + fn runtime_metadata(&self) -> #scrate::__private::sp_std::vec::Vec<#scrate::__private::metadata_ir::RuntimeApiMetadataIR> { + Default::default() + } + } + #[doc(hidden)] + impl InternalConstructRuntime for &#name {} - #all_pallets + #outer_event - #pallet_to_index + #outer_error - #dispatch + #outer_origin - #tasks + #all_pallets - #metadata + #pallet_to_index - #outer_config + #dispatch - #inherent + #tasks - #validate_unsigned + #metadata - #freeze_reason + #outer_config - #hold_reason + #inherent - #lock_id + #validate_unsigned - #slash_reason + #freeze_reason - #integrity_test + #hold_reason - #static_assertions - ); + #lock_id - Ok(res) + #slash_reason + + #integrity_test + + #static_assertions + ); + + Ok(res) } pub(crate) fn decl_all_pallets<'a>( - runtime: &'a Ident, - pallet_declarations: impl Iterator, - features: &HashSet<&str>, + runtime: &'a Ident, + pallet_declarations: impl Iterator, + features: &HashSet<&str>, ) -> TokenStream2 { - let mut types = TokenStream2::new(); - - // Every feature set to the pallet names that should be included by this feature set. - let mut features_to_names = features - .iter() - .map(|f| *f) - .powerset() - .map(|feat| (HashSet::from_iter(feat), Vec::new())) - .collect::, Vec<_>)>>(); - - for pallet_declaration in pallet_declarations { - let type_name = &pallet_declaration.name; - let pallet = &pallet_declaration.path; - let mut generics = vec![quote!(#runtime)]; - generics.extend(pallet_declaration.instance.iter().map(|name| quote!(#pallet::#name))); - let mut attrs = Vec::new(); - for cfg in &pallet_declaration.cfg_pattern { - let feat = format!("#[cfg({})]\n", cfg.original()); - attrs.extend(TokenStream2::from_str(&feat).expect("was parsed successfully; qed")); - } - let type_decl = quote!( - #(#attrs)* - pub type #type_name = #pallet::Pallet <#(#generics),*>; - ); - types.extend(type_decl); - - if pallet_declaration.cfg_pattern.is_empty() { - for (_, names) in features_to_names.iter_mut() { - names.push(&pallet_declaration.name); - } - } else { - for (feature_set, names) in &mut features_to_names { - // Rust tidbit: if we have multiple `#[cfg]` feature on the same item, then the - // predicates listed in all `#[cfg]` attributes are effectively joined by `and()`, - // meaning that all of them must match in order to activate the item - let is_feature_active = pallet_declaration.cfg_pattern.iter().all(|expr| { - expr.eval(|pred| match pred { - Predicate::Feature(f) => feature_set.contains(f), - Predicate::Test => feature_set.contains(&"test"), - _ => false, - }) - }); - - if is_feature_active { - names.push(&pallet_declaration.name); - } - } - } - } - - // All possible features. This will be used below for the empty feature set. - let mut all_features = features_to_names - .iter() - .flat_map(|f| f.0.iter().cloned()) - .collect::>(); - let attribute_to_names = features_to_names + let mut types = TokenStream2::new(); + + // Every feature set to the pallet names that should be included by this feature set. + let mut features_to_names = features + .iter() + .map(|f| *f) + .powerset() + .map(|feat| (HashSet::from_iter(feat), Vec::new())) + .collect::, Vec<_>)>>(); + + for pallet_declaration in pallet_declarations { + let type_name = &pallet_declaration.name; + let pallet = &pallet_declaration.path; + let mut generics = vec![quote!(#runtime)]; + generics.extend( + pallet_declaration + .instance + .iter() + .map(|name| quote!(#pallet::#name)), + ); + let mut attrs = Vec::new(); + for cfg in &pallet_declaration.cfg_pattern { + let feat = format!("#[cfg({})]\n", cfg.original()); + attrs.extend(TokenStream2::from_str(&feat).expect("was parsed successfully; qed")); + } + let type_decl = quote!( + #(#attrs)* + pub type #type_name = #pallet::Pallet <#(#generics),*>; + ); + types.extend(type_decl); + + if pallet_declaration.cfg_pattern.is_empty() { + for (_, names) in features_to_names.iter_mut() { + names.push(&pallet_declaration.name); + } + } else { + for (feature_set, names) in &mut features_to_names { + // Rust tidbit: if we have multiple `#[cfg]` feature on the same item, then the + // predicates listed in all `#[cfg]` attributes are effectively joined by `and()`, + // meaning that all of them must match in order to activate the item + let is_feature_active = pallet_declaration.cfg_pattern.iter().all(|expr| { + expr.eval(|pred| match pred { + Predicate::Feature(f) => feature_set.contains(f), + Predicate::Test => feature_set.contains(&"test"), + _ => false, + }) + }); + + if is_feature_active { + names.push(&pallet_declaration.name); + } + } + } + } + + // All possible features. This will be used below for the empty feature set. + let mut all_features = features_to_names + .iter() + .flat_map(|f| f.0.iter().cloned()) + .collect::>(); + let attribute_to_names = features_to_names .into_iter() .map(|(mut features, names)| { // If this is the empty feature set, it needs to be changed to negate all available @@ -598,212 +624,222 @@ pub(crate) fn decl_all_pallets<'a>( }) .collect::>(); - let all_pallets_without_system = attribute_to_names.iter().map(|(attr, names)| { - let names = names.iter().filter(|n| **n != SYSTEM_PALLET_NAME); - quote! { - #attr - /// All pallets included in the runtime as a nested tuple of types. - /// Excludes the System pallet. - pub type AllPalletsWithoutSystem = ( #(#names,)* ); - } - }); - - let all_pallets_with_system = attribute_to_names.iter().map(|(attr, names)| { - quote! { - #attr - /// All pallets included in the runtime as a nested tuple of types. - pub type AllPalletsWithSystem = ( #(#names,)* ); - } - }); - - quote!( - #types - - #( #all_pallets_with_system )* - - #( #all_pallets_without_system )* - ) + let all_pallets_without_system = attribute_to_names.iter().map(|(attr, names)| { + let names = names.iter().filter(|n| **n != SYSTEM_PALLET_NAME); + quote! { + #attr + /// All pallets included in the runtime as a nested tuple of types. + /// Excludes the System pallet. + pub type AllPalletsWithoutSystem = ( #(#names,)* ); + } + }); + + let all_pallets_with_system = attribute_to_names.iter().map(|(attr, names)| { + quote! { + #attr + /// All pallets included in the runtime as a nested tuple of types. + pub type AllPalletsWithSystem = ( #(#names,)* ); + } + }); + + quote!( + #types + + #( #all_pallets_with_system )* + + #( #all_pallets_without_system )* + ) } pub(crate) fn decl_pallet_runtime_setup( - runtime: &Ident, - pallet_declarations: &[Pallet], - scrate: &TokenStream2, + runtime: &Ident, + pallet_declarations: &[Pallet], + scrate: &TokenStream2, ) -> TokenStream2 { - let names = pallet_declarations.iter().map(|d| &d.name).collect::>(); - let name_strings = pallet_declarations.iter().map(|d| d.name.to_string()); - let name_hashes = pallet_declarations.iter().map(|d| two128_str(&d.name.to_string())); - let module_names = pallet_declarations.iter().map(|d| d.path.module_name()); - let indices = pallet_declarations.iter().map(|pallet| pallet.index as usize); - let pallet_structs = pallet_declarations - .iter() - .map(|pallet| { - let path = &pallet.path; - match pallet.instance.as_ref() { - Some(inst) => quote!(#path::Pallet<#runtime, #path::#inst>), - None => quote!(#path::Pallet<#runtime>), - } - }) - .collect::>(); - let pallet_attrs = pallet_declarations - .iter() - .map(|pallet| { - pallet.cfg_pattern.iter().fold(TokenStream2::new(), |acc, pattern| { - let attr = TokenStream2::from_str(&format!("#[cfg({})]", pattern.original())) - .expect("was successfully parsed before; qed"); - quote! { - #acc - #attr - } - }) - }) - .collect::>(); - - quote!( - /// Provides an implementation of `PalletInfo` to provide information - /// about the pallet setup in the runtime. - pub struct PalletInfo; - - impl #scrate::traits::PalletInfo for PalletInfo { - - fn index() -> Option { - let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); - #( - #pallet_attrs - if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { - return Some(#indices) - } - )* - - None - } - - fn name() -> Option<&'static str> { - let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); - #( - #pallet_attrs - if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { - return Some(#name_strings) - } - )* - - None - } - - fn name_hash() -> Option<[u8; 16]> { - let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); - #( - #pallet_attrs - if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { - return Some(#name_hashes) - } - )* - - None - } - - fn module_name() -> Option<&'static str> { - let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); - #( - #pallet_attrs - if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { - return Some(#module_names) - } - )* - - None - } - - fn crate_version() -> Option<#scrate::traits::CrateVersion> { - let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); - #( - #pallet_attrs - if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { - return Some( - <#pallet_structs as #scrate::traits::PalletInfoAccess>::crate_version() - ) - } - )* - - None - } - } - ) + let names = pallet_declarations + .iter() + .map(|d| &d.name) + .collect::>(); + let name_strings = pallet_declarations.iter().map(|d| d.name.to_string()); + let name_hashes = pallet_declarations + .iter() + .map(|d| two128_str(&d.name.to_string())); + let module_names = pallet_declarations.iter().map(|d| d.path.module_name()); + let indices = pallet_declarations + .iter() + .map(|pallet| pallet.index as usize); + let pallet_structs = pallet_declarations + .iter() + .map(|pallet| { + let path = &pallet.path; + match pallet.instance.as_ref() { + Some(inst) => quote!(#path::Pallet<#runtime, #path::#inst>), + None => quote!(#path::Pallet<#runtime>), + } + }) + .collect::>(); + let pallet_attrs = pallet_declarations + .iter() + .map(|pallet| { + pallet + .cfg_pattern + .iter() + .fold(TokenStream2::new(), |acc, pattern| { + let attr = TokenStream2::from_str(&format!("#[cfg({})]", pattern.original())) + .expect("was successfully parsed before; qed"); + quote! { + #acc + #attr + } + }) + }) + .collect::>(); + + quote!( + /// Provides an implementation of `PalletInfo` to provide information + /// about the pallet setup in the runtime. + pub struct PalletInfo; + + impl #scrate::traits::PalletInfo for PalletInfo { + + fn index() -> Option { + let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); + #( + #pallet_attrs + if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { + return Some(#indices) + } + )* + + None + } + + fn name() -> Option<&'static str> { + let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); + #( + #pallet_attrs + if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { + return Some(#name_strings) + } + )* + + None + } + + fn name_hash() -> Option<[u8; 16]> { + let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); + #( + #pallet_attrs + if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { + return Some(#name_hashes) + } + )* + + None + } + + fn module_name() -> Option<&'static str> { + let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); + #( + #pallet_attrs + if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { + return Some(#module_names) + } + )* + + None + } + + fn crate_version() -> Option<#scrate::traits::CrateVersion> { + let type_id = #scrate::__private::sp_std::any::TypeId::of::

(); + #( + #pallet_attrs + if type_id == #scrate::__private::sp_std::any::TypeId::of::<#names>() { + return Some( + <#pallet_structs as #scrate::traits::PalletInfoAccess>::crate_version() + ) + } + )* + + None + } + } + ) } pub(crate) fn decl_integrity_test(scrate: &TokenStream2) -> TokenStream2 { - quote!( - #[cfg(test)] - mod __construct_runtime_integrity_test { - use super::*; - - #[test] - pub fn runtime_integrity_tests() { - #scrate::__private::sp_tracing::try_init_simple(); - ::integrity_test(); - } - } - ) + quote!( + #[cfg(test)] + mod __construct_runtime_integrity_test { + use super::*; + + #[test] + pub fn runtime_integrity_tests() { + #scrate::__private::sp_tracing::try_init_simple(); + ::integrity_test(); + } + } + ) } pub(crate) fn decl_static_assertions( - runtime: &Ident, - pallet_decls: &[Pallet], - scrate: &TokenStream2, + runtime: &Ident, + pallet_decls: &[Pallet], + scrate: &TokenStream2, ) -> TokenStream2 { - let error_encoded_size_check = pallet_decls.iter().map(|decl| { - let path = &decl.path; - let assert_message = format!( - "The maximum encoded size of the error type in the `{}` pallet exceeds \ + let error_encoded_size_check = pallet_decls.iter().map(|decl| { + let path = &decl.path; + let assert_message = format!( + "The maximum encoded size of the error type in the `{}` pallet exceeds \ `MAX_MODULE_ERROR_ENCODED_SIZE`", - decl.name, - ); - - quote! { - #scrate::__private::tt_call! { - macro = [{ #path::tt_error_token }] - your_tt_return = [{ #scrate::__private::tt_return }] - ~~> #scrate::assert_error_encoded_size! { - path = [{ #path }] - runtime = [{ #runtime }] - assert_message = [{ #assert_message }] - } - } - } - }); - - quote! { - #(#error_encoded_size_check)* - } + decl.name, + ); + + quote! { + #scrate::__private::tt_call! { + macro = [{ #path::tt_error_token }] + your_tt_return = [{ #scrate::__private::tt_return }] + ~~> #scrate::assert_error_encoded_size! { + path = [{ #path }] + runtime = [{ #runtime }] + assert_message = [{ #assert_message }] + } + } + } + }); + + quote! { + #(#error_encoded_size_check)* + } } pub(crate) fn check_pallet_number(input: TokenStream2, pallet_num: usize) -> Result<()> { - let max_pallet_num = { - if cfg!(feature = "tuples-96") { - 96 - } else if cfg!(feature = "tuples-128") { - 128 - } else { - 64 - } - }; - - if pallet_num > max_pallet_num { - let no_feature = max_pallet_num == 128; - return Err(syn::Error::new( - input.span(), - format!( - "{} To increase this limit, enable the tuples-{} feature of [frame_support]. {}", - "The number of pallets exceeds the maximum number of tuple elements.", - max_pallet_num + 32, - if no_feature { - "If the feature does not exist - it needs to be implemented." - } else { - "" - }, - ), - )) - } - - Ok(()) + let max_pallet_num = { + if cfg!(feature = "tuples-96") { + 96 + } else if cfg!(feature = "tuples-128") { + 128 + } else { + 64 + } + }; + + if pallet_num > max_pallet_num { + let no_feature = max_pallet_num == 128; + return Err(syn::Error::new( + input.span(), + format!( + "{} To increase this limit, enable the tuples-{} feature of [frame_support]. {}", + "The number of pallets exceeds the maximum number of tuple elements.", + max_pallet_num + 32, + if no_feature { + "If the feature does not exist - it needs to be implemented." + } else { + "" + }, + ), + )); + } + + Ok(()) } diff --git a/support/procedural-fork/src/construct_runtime/parse.rs b/support/procedural-fork/src/construct_runtime/parse.rs index 31866c787..173a8dd12 100644 --- a/support/procedural-fork/src/construct_runtime/parse.rs +++ b/support/procedural-fork/src/construct_runtime/parse.rs @@ -20,34 +20,34 @@ use proc_macro2::{Span, TokenStream}; use quote::ToTokens; use std::collections::{HashMap, HashSet}; use syn::{ - ext::IdentExt, - parse::{Parse, ParseStream}, - punctuated::Punctuated, - spanned::Spanned, - token, Attribute, Error, Ident, Path, Result, Token, + ext::IdentExt, + parse::{Parse, ParseStream}, + punctuated::Punctuated, + spanned::Spanned, + token, Attribute, Error, Ident, Path, Result, Token, }; mod keyword { - syn::custom_keyword!(Block); - syn::custom_keyword!(NodeBlock); - syn::custom_keyword!(UncheckedExtrinsic); - syn::custom_keyword!(Pallet); - syn::custom_keyword!(Call); - syn::custom_keyword!(Storage); - syn::custom_keyword!(Event); - syn::custom_keyword!(Error); - syn::custom_keyword!(Config); - syn::custom_keyword!(Origin); - syn::custom_keyword!(Inherent); - syn::custom_keyword!(ValidateUnsigned); - syn::custom_keyword!(FreezeReason); - syn::custom_keyword!(HoldReason); - syn::custom_keyword!(Task); - syn::custom_keyword!(LockId); - syn::custom_keyword!(SlashReason); - syn::custom_keyword!(exclude_parts); - syn::custom_keyword!(use_parts); - syn::custom_keyword!(expanded); + syn::custom_keyword!(Block); + syn::custom_keyword!(NodeBlock); + syn::custom_keyword!(UncheckedExtrinsic); + syn::custom_keyword!(Pallet); + syn::custom_keyword!(Call); + syn::custom_keyword!(Storage); + syn::custom_keyword!(Event); + syn::custom_keyword!(Error); + syn::custom_keyword!(Config); + syn::custom_keyword!(Origin); + syn::custom_keyword!(Inherent); + syn::custom_keyword!(ValidateUnsigned); + syn::custom_keyword!(FreezeReason); + syn::custom_keyword!(HoldReason); + syn::custom_keyword!(Task); + syn::custom_keyword!(LockId); + syn::custom_keyword!(SlashReason); + syn::custom_keyword!(exclude_parts); + syn::custom_keyword!(use_parts); + syn::custom_keyword!(expanded); } /// Declaration of a runtime. @@ -57,266 +57,298 @@ mod keyword { /// implicit. #[derive(Debug)] pub enum RuntimeDeclaration { - Implicit(ImplicitRuntimeDeclaration), - Explicit(ExplicitRuntimeDeclaration), - ExplicitExpanded(ExplicitRuntimeDeclaration), + Implicit(ImplicitRuntimeDeclaration), + Explicit(ExplicitRuntimeDeclaration), + ExplicitExpanded(ExplicitRuntimeDeclaration), } /// Declaration of a runtime with some pallet with implicit declaration of parts. #[derive(Debug)] pub struct ImplicitRuntimeDeclaration { - pub name: Ident, - pub where_section: Option, - pub pallets: Vec, + pub name: Ident, + pub where_section: Option, + pub pallets: Vec, } /// Declaration of a runtime with all pallet having explicit declaration of parts. #[derive(Debug)] pub struct ExplicitRuntimeDeclaration { - pub name: Ident, - pub where_section: Option, - pub pallets: Vec, - pub pallets_token: token::Brace, + pub name: Ident, + pub where_section: Option, + pub pallets: Vec, + pub pallets_token: token::Brace, } impl Parse for RuntimeDeclaration { - fn parse(input: ParseStream) -> Result { - input.parse::()?; - - // Support either `enum` or `struct`. - if input.peek(Token![struct]) { - input.parse::()?; - } else { - input.parse::()?; - } - - let name = input.parse::()?; - let where_section = if input.peek(token::Where) { Some(input.parse()?) } else { None }; - let pallets = - input.parse::>>()?; - let pallets_token = pallets.token; - - match convert_pallets(pallets.content.inner.into_iter().collect())? { - PalletsConversion::Implicit(pallets) => - Ok(RuntimeDeclaration::Implicit(ImplicitRuntimeDeclaration { - name, - where_section, - pallets, - })), - PalletsConversion::Explicit(pallets) => - Ok(RuntimeDeclaration::Explicit(ExplicitRuntimeDeclaration { - name, - where_section, - pallets, - pallets_token, - })), - PalletsConversion::ExplicitExpanded(pallets) => - Ok(RuntimeDeclaration::ExplicitExpanded(ExplicitRuntimeDeclaration { - name, - where_section, - pallets, - pallets_token, - })), - } - } + fn parse(input: ParseStream) -> Result { + input.parse::()?; + + // Support either `enum` or `struct`. + if input.peek(Token![struct]) { + input.parse::()?; + } else { + input.parse::()?; + } + + let name = input.parse::()?; + let where_section = if input.peek(token::Where) { + Some(input.parse()?) + } else { + None + }; + let pallets = + input.parse::>>()?; + let pallets_token = pallets.token; + + match convert_pallets(pallets.content.inner.into_iter().collect())? { + PalletsConversion::Implicit(pallets) => { + Ok(RuntimeDeclaration::Implicit(ImplicitRuntimeDeclaration { + name, + where_section, + pallets, + })) + } + PalletsConversion::Explicit(pallets) => { + Ok(RuntimeDeclaration::Explicit(ExplicitRuntimeDeclaration { + name, + where_section, + pallets, + pallets_token, + })) + } + PalletsConversion::ExplicitExpanded(pallets) => Ok( + RuntimeDeclaration::ExplicitExpanded(ExplicitRuntimeDeclaration { + name, + where_section, + pallets, + pallets_token, + }), + ), + } + } } #[derive(Debug)] pub struct WhereSection { - pub span: Span, - pub block: syn::TypePath, - pub node_block: syn::TypePath, - pub unchecked_extrinsic: syn::TypePath, + pub span: Span, + 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 declaration for `{:?}`.", - kind, kind - ); - return Err(Error::new(*kind_span, msg)) - } - Ok(Self { span: input.span(), block, node_block, unchecked_extrinsic }) - } + 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 declaration for `{:?}`.", + kind, kind + ); + return Err(Error::new(*kind_span, msg)); + } + Ok(Self { + span: input.span(), + block, + node_block, + unchecked_extrinsic, + }) + } } #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] pub enum WhereKind { - Block, - NodeBlock, - UncheckedExtrinsic, + Block, + NodeBlock, + UncheckedExtrinsic, } #[derive(Debug)] pub struct WhereDefinition { - pub kind_span: Span, - pub kind: WhereKind, - pub value: syn::TypePath, + 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()? - }, - }) - } + 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()? + }, + }) + } } /// The declaration of a pallet. #[derive(Debug, Clone)] pub struct PalletDeclaration { - /// Is this pallet fully expanded? - pub is_expanded: bool, - /// The name of the pallet, e.g.`System` in `System: frame_system`. - pub name: Ident, - /// Optional attributes tagged right above a pallet declaration. - pub attrs: Vec, - /// Optional fixed index, e.g. `MyPallet ... = 3,`. - pub index: Option, - /// The path of the pallet, e.g. `frame_system` in `System: frame_system`. - pub path: PalletPath, - /// The instance of the pallet, e.g. `Instance1` in `Council: pallet_collective::`. - pub instance: Option, - /// The declared pallet parts, - /// e.g. `Some([Pallet, Call])` for `System: system::{Pallet, Call}` - /// or `None` for `System: system`. - pub pallet_parts: Option>, - /// The specified parts, either use_parts or exclude_parts. - pub specified_parts: SpecifiedParts, + /// Is this pallet fully expanded? + pub is_expanded: bool, + /// The name of the pallet, e.g.`System` in `System: frame_system`. + pub name: Ident, + /// Optional attributes tagged right above a pallet declaration. + pub attrs: Vec, + /// Optional fixed index, e.g. `MyPallet ... = 3,`. + pub index: Option, + /// The path of the pallet, e.g. `frame_system` in `System: frame_system`. + pub path: PalletPath, + /// The instance of the pallet, e.g. `Instance1` in `Council: pallet_collective::`. + pub instance: Option, + /// The declared pallet parts, + /// e.g. `Some([Pallet, Call])` for `System: system::{Pallet, Call}` + /// or `None` for `System: system`. + pub pallet_parts: Option>, + /// The specified parts, either use_parts or exclude_parts. + pub specified_parts: SpecifiedParts, } /// The possible declaration of pallet parts to use. #[derive(Debug, Clone)] pub enum SpecifiedParts { - /// Use all the pallet parts except those specified. - Exclude(Vec), - /// Use only the specified pallet parts. - Use(Vec), - /// Use the all the pallet parts. - All, + /// Use all the pallet parts except those specified. + Exclude(Vec), + /// Use only the specified pallet parts. + Use(Vec), + /// Use the all the pallet parts. + All, } impl Parse for PalletDeclaration { - fn parse(input: ParseStream) -> Result { - let attrs = input.call(Attribute::parse_outer)?; - - let name = input.parse()?; - let _: Token![:] = input.parse()?; - let path = input.parse()?; - - // Parse for instance. - 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 if !(input.peek(Token![::]) && input.peek3(token::Brace)) && - !input.peek(keyword::expanded) && - !input.peek(keyword::exclude_parts) && - !input.peek(keyword::use_parts) && - !input.peek(Token![=]) && - !input.peek(Token![,]) && - !input.is_empty() - { - return Err(input.error( + fn parse(input: ParseStream) -> Result { + let attrs = input.call(Attribute::parse_outer)?; + + let name = input.parse()?; + let _: Token![:] = input.parse()?; + let path = input.parse()?; + + // Parse for instance. + 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 if !(input.peek(Token![::]) && input.peek3(token::Brace)) + && !input.peek(keyword::expanded) + && !input.peek(keyword::exclude_parts) + && !input.peek(keyword::use_parts) + && !input.peek(Token![=]) + && !input.peek(Token![,]) + && !input.is_empty() + { + return Err(input.error( "Unexpected tokens, expected one of `::$ident` `::{`, `exclude_parts`, `use_parts`, `=`, `,`", )); - } else { - None - }; - - // Check if the pallet is fully expanded. - let (is_expanded, extra_parts) = if input.peek(keyword::expanded) { - let _: keyword::expanded = input.parse()?; - let _: Token![::] = input.parse()?; - (true, parse_pallet_parts(input)?) - } else { - (false, vec![]) - }; - - // Parse for explicit parts - let pallet_parts = if input.peek(Token![::]) && input.peek3(token::Brace) { - let _: Token![::] = input.parse()?; - let mut parts = parse_pallet_parts(input)?; - parts.extend(extra_parts.into_iter()); - Some(parts) - } else if !input.peek(keyword::exclude_parts) && - !input.peek(keyword::use_parts) && - !input.peek(Token![=]) && - !input.peek(Token![,]) && - !input.is_empty() - { - return Err(input.error( - "Unexpected tokens, expected one of `::{`, `exclude_parts`, `use_parts`, `=`, `,`", - )) - } else { - is_expanded.then_some(extra_parts) - }; - - // Parse for specified parts - let specified_parts = if input.peek(keyword::exclude_parts) { - let _: keyword::exclude_parts = input.parse()?; - SpecifiedParts::Exclude(parse_pallet_parts_no_generic(input)?) - } else if input.peek(keyword::use_parts) { - let _: keyword::use_parts = input.parse()?; - SpecifiedParts::Use(parse_pallet_parts_no_generic(input)?) - } else if !input.peek(Token![=]) && !input.peek(Token![,]) && !input.is_empty() { - return Err(input.error("Unexpected tokens, expected one of `exclude_parts`, `=`, `,`")) - } else { - SpecifiedParts::All - }; - - // Parse for pallet index - let index = if input.peek(Token![=]) { - input.parse::()?; - let index = input.parse::()?; - let index = index.base10_parse::()?; - Some(index) - } else if !input.peek(Token![,]) && !input.is_empty() { - return Err(input.error("Unexpected tokens, expected one of `=`, `,`")) - } else { - None - }; - - Ok(Self { is_expanded, attrs, name, path, instance, pallet_parts, specified_parts, index }) - } + } else { + None + }; + + // Check if the pallet is fully expanded. + let (is_expanded, extra_parts) = if input.peek(keyword::expanded) { + let _: keyword::expanded = input.parse()?; + let _: Token![::] = input.parse()?; + (true, parse_pallet_parts(input)?) + } else { + (false, vec![]) + }; + + // Parse for explicit parts + let pallet_parts = if input.peek(Token![::]) && input.peek3(token::Brace) { + let _: Token![::] = input.parse()?; + let mut parts = parse_pallet_parts(input)?; + parts.extend(extra_parts.into_iter()); + Some(parts) + } else if !input.peek(keyword::exclude_parts) + && !input.peek(keyword::use_parts) + && !input.peek(Token![=]) + && !input.peek(Token![,]) + && !input.is_empty() + { + return Err(input.error( + "Unexpected tokens, expected one of `::{`, `exclude_parts`, `use_parts`, `=`, `,`", + )); + } else { + is_expanded.then_some(extra_parts) + }; + + // Parse for specified parts + let specified_parts = if input.peek(keyword::exclude_parts) { + let _: keyword::exclude_parts = input.parse()?; + SpecifiedParts::Exclude(parse_pallet_parts_no_generic(input)?) + } else if input.peek(keyword::use_parts) { + let _: keyword::use_parts = input.parse()?; + SpecifiedParts::Use(parse_pallet_parts_no_generic(input)?) + } else if !input.peek(Token![=]) && !input.peek(Token![,]) && !input.is_empty() { + return Err(input.error("Unexpected tokens, expected one of `exclude_parts`, `=`, `,`")); + } else { + SpecifiedParts::All + }; + + // Parse for pallet index + let index = if input.peek(Token![=]) { + input.parse::()?; + let index = input.parse::()?; + let index = index.base10_parse::()?; + Some(index) + } else if !input.peek(Token![,]) && !input.is_empty() { + return Err(input.error("Unexpected tokens, expected one of `=`, `,`")); + } else { + None + }; + + Ok(Self { + is_expanded, + attrs, + name, + path, + instance, + pallet_parts, + specified_parts, + index, + }) + } } /// A struct representing a path to a pallet. `PalletPath` is almost identical to the standard @@ -325,303 +357,312 @@ impl Parse for PalletDeclaration { /// - Path segments can only consist of identifiers separated by colons #[derive(Debug, Clone)] pub struct PalletPath { - pub inner: Path, + pub inner: Path, } impl PalletPath { - pub fn module_name(&self) -> String { - self.inner.segments.iter().fold(String::new(), |mut acc, segment| { - if !acc.is_empty() { - acc.push_str("::"); - } - acc.push_str(&segment.ident.to_string()); - acc - }) - } + pub fn module_name(&self) -> String { + self.inner + .segments + .iter() + .fold(String::new(), |mut acc, segment| { + if !acc.is_empty() { + acc.push_str("::"); + } + acc.push_str(&segment.ident.to_string()); + acc + }) + } } impl Parse for PalletPath { - fn parse(input: ParseStream) -> Result { - let mut res = - PalletPath { inner: Path { leading_colon: None, segments: Punctuated::new() } }; - - let lookahead = input.lookahead1(); - if lookahead.peek(Token![crate]) || - lookahead.peek(Token![self]) || - lookahead.peek(Token![super]) || - lookahead.peek(Ident) - { - let ident = input.call(Ident::parse_any)?; - res.inner.segments.push(ident.into()); - } else { - return Err(lookahead.error()) - } - - while input.peek(Token![::]) && input.peek3(Ident) { - input.parse::()?; - let ident = input.parse::()?; - res.inner.segments.push(ident.into()); - } - Ok(res) - } + fn parse(input: ParseStream) -> Result { + let mut res = PalletPath { + inner: Path { + leading_colon: None, + segments: Punctuated::new(), + }, + }; + + let lookahead = input.lookahead1(); + if lookahead.peek(Token![crate]) + || lookahead.peek(Token![self]) + || lookahead.peek(Token![super]) + || lookahead.peek(Ident) + { + let ident = input.call(Ident::parse_any)?; + res.inner.segments.push(ident.into()); + } else { + return Err(lookahead.error()); + } + + while input.peek(Token![::]) && input.peek3(Ident) { + input.parse::()?; + let ident = input.parse::()?; + res.inner.segments.push(ident.into()); + } + Ok(res) + } } impl quote::ToTokens for PalletPath { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.inner.to_tokens(tokens); - } + fn to_tokens(&self, tokens: &mut TokenStream) { + self.inner.to_tokens(tokens); + } } /// Parse [`PalletPart`]'s from a braces enclosed list that is split by commas, e.g. /// /// `{ Call, Event }` fn parse_pallet_parts(input: ParseStream) -> Result> { - let pallet_parts: ext::Braces> = input.parse()?; + let pallet_parts: ext::Braces> = input.parse()?; - let mut resolved = HashSet::new(); - for part in pallet_parts.content.inner.iter() { - if !resolved.insert(part.name()) { - let msg = format!( - "`{}` was already declared before. Please remove the duplicate declaration", - part.name(), - ); - return Err(Error::new(part.keyword.span(), msg)) - } - } + let mut resolved = HashSet::new(); + for part in pallet_parts.content.inner.iter() { + if !resolved.insert(part.name()) { + let msg = format!( + "`{}` was already declared before. Please remove the duplicate declaration", + part.name(), + ); + return Err(Error::new(part.keyword.span(), msg)); + } + } - Ok(pallet_parts.content.inner.into_iter().collect()) + Ok(pallet_parts.content.inner.into_iter().collect()) } #[derive(Debug, Clone)] pub enum PalletPartKeyword { - Pallet(keyword::Pallet), - Call(keyword::Call), - Storage(keyword::Storage), - Event(keyword::Event), - Error(keyword::Error), - Config(keyword::Config), - Origin(keyword::Origin), - Inherent(keyword::Inherent), - ValidateUnsigned(keyword::ValidateUnsigned), - FreezeReason(keyword::FreezeReason), - HoldReason(keyword::HoldReason), - Task(keyword::Task), - LockId(keyword::LockId), - SlashReason(keyword::SlashReason), + Pallet(keyword::Pallet), + Call(keyword::Call), + Storage(keyword::Storage), + Event(keyword::Event), + Error(keyword::Error), + Config(keyword::Config), + Origin(keyword::Origin), + Inherent(keyword::Inherent), + ValidateUnsigned(keyword::ValidateUnsigned), + FreezeReason(keyword::FreezeReason), + HoldReason(keyword::HoldReason), + Task(keyword::Task), + LockId(keyword::LockId), + SlashReason(keyword::SlashReason), } impl Parse for PalletPartKeyword { - fn parse(input: ParseStream) -> Result { - let lookahead = input.lookahead1(); - - if lookahead.peek(keyword::Pallet) { - Ok(Self::Pallet(input.parse()?)) - } else if lookahead.peek(keyword::Call) { - Ok(Self::Call(input.parse()?)) - } else if lookahead.peek(keyword::Storage) { - Ok(Self::Storage(input.parse()?)) - } else if lookahead.peek(keyword::Event) { - Ok(Self::Event(input.parse()?)) - } else if lookahead.peek(keyword::Error) { - Ok(Self::Error(input.parse()?)) - } else if lookahead.peek(keyword::Config) { - Ok(Self::Config(input.parse()?)) - } else if lookahead.peek(keyword::Origin) { - Ok(Self::Origin(input.parse()?)) - } else if lookahead.peek(keyword::Inherent) { - Ok(Self::Inherent(input.parse()?)) - } else if lookahead.peek(keyword::ValidateUnsigned) { - Ok(Self::ValidateUnsigned(input.parse()?)) - } else if lookahead.peek(keyword::FreezeReason) { - Ok(Self::FreezeReason(input.parse()?)) - } else if lookahead.peek(keyword::HoldReason) { - Ok(Self::HoldReason(input.parse()?)) - } else if lookahead.peek(keyword::Task) { - Ok(Self::Task(input.parse()?)) - } else if lookahead.peek(keyword::LockId) { - Ok(Self::LockId(input.parse()?)) - } else if lookahead.peek(keyword::SlashReason) { - Ok(Self::SlashReason(input.parse()?)) - } else { - Err(lookahead.error()) - } - } + fn parse(input: ParseStream) -> Result { + let lookahead = input.lookahead1(); + + if lookahead.peek(keyword::Pallet) { + Ok(Self::Pallet(input.parse()?)) + } else if lookahead.peek(keyword::Call) { + Ok(Self::Call(input.parse()?)) + } else if lookahead.peek(keyword::Storage) { + Ok(Self::Storage(input.parse()?)) + } else if lookahead.peek(keyword::Event) { + Ok(Self::Event(input.parse()?)) + } else if lookahead.peek(keyword::Error) { + Ok(Self::Error(input.parse()?)) + } else if lookahead.peek(keyword::Config) { + Ok(Self::Config(input.parse()?)) + } else if lookahead.peek(keyword::Origin) { + Ok(Self::Origin(input.parse()?)) + } else if lookahead.peek(keyword::Inherent) { + Ok(Self::Inherent(input.parse()?)) + } else if lookahead.peek(keyword::ValidateUnsigned) { + Ok(Self::ValidateUnsigned(input.parse()?)) + } else if lookahead.peek(keyword::FreezeReason) { + Ok(Self::FreezeReason(input.parse()?)) + } else if lookahead.peek(keyword::HoldReason) { + Ok(Self::HoldReason(input.parse()?)) + } else if lookahead.peek(keyword::Task) { + Ok(Self::Task(input.parse()?)) + } else if lookahead.peek(keyword::LockId) { + Ok(Self::LockId(input.parse()?)) + } else if lookahead.peek(keyword::SlashReason) { + Ok(Self::SlashReason(input.parse()?)) + } else { + Err(lookahead.error()) + } + } } impl PalletPartKeyword { - /// Returns the name of `Self`. - fn name(&self) -> &'static str { - match self { - Self::Pallet(_) => "Pallet", - Self::Call(_) => "Call", - Self::Storage(_) => "Storage", - Self::Event(_) => "Event", - Self::Error(_) => "Error", - Self::Config(_) => "Config", - Self::Origin(_) => "Origin", - Self::Inherent(_) => "Inherent", - Self::ValidateUnsigned(_) => "ValidateUnsigned", - Self::FreezeReason(_) => "FreezeReason", - Self::HoldReason(_) => "HoldReason", - Self::Task(_) => "Task", - Self::LockId(_) => "LockId", - Self::SlashReason(_) => "SlashReason", - } - } - - /// Returns `true` if this pallet part is allowed to have generic arguments. - fn allows_generic(&self) -> bool { - Self::all_generic_arg().iter().any(|n| *n == self.name()) - } - - /// Returns the names of all pallet parts that allow to have a generic argument. - fn all_generic_arg() -> &'static [&'static str] { - &["Event", "Error", "Origin", "Config", "Task"] - } + /// Returns the name of `Self`. + fn name(&self) -> &'static str { + match self { + Self::Pallet(_) => "Pallet", + Self::Call(_) => "Call", + Self::Storage(_) => "Storage", + Self::Event(_) => "Event", + Self::Error(_) => "Error", + Self::Config(_) => "Config", + Self::Origin(_) => "Origin", + Self::Inherent(_) => "Inherent", + Self::ValidateUnsigned(_) => "ValidateUnsigned", + Self::FreezeReason(_) => "FreezeReason", + Self::HoldReason(_) => "HoldReason", + Self::Task(_) => "Task", + Self::LockId(_) => "LockId", + Self::SlashReason(_) => "SlashReason", + } + } + + /// Returns `true` if this pallet part is allowed to have generic arguments. + fn allows_generic(&self) -> bool { + Self::all_generic_arg().iter().any(|n| *n == self.name()) + } + + /// Returns the names of all pallet parts that allow to have a generic argument. + fn all_generic_arg() -> &'static [&'static str] { + &["Event", "Error", "Origin", "Config", "Task"] + } } impl ToTokens for PalletPartKeyword { - fn to_tokens(&self, tokens: &mut TokenStream) { - match self { - Self::Pallet(inner) => inner.to_tokens(tokens), - Self::Call(inner) => inner.to_tokens(tokens), - Self::Storage(inner) => inner.to_tokens(tokens), - Self::Event(inner) => inner.to_tokens(tokens), - Self::Error(inner) => inner.to_tokens(tokens), - Self::Config(inner) => inner.to_tokens(tokens), - Self::Origin(inner) => inner.to_tokens(tokens), - Self::Inherent(inner) => inner.to_tokens(tokens), - Self::ValidateUnsigned(inner) => inner.to_tokens(tokens), - Self::FreezeReason(inner) => inner.to_tokens(tokens), - Self::HoldReason(inner) => inner.to_tokens(tokens), - Self::Task(inner) => inner.to_tokens(tokens), - Self::LockId(inner) => inner.to_tokens(tokens), - Self::SlashReason(inner) => inner.to_tokens(tokens), - } - } + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + Self::Pallet(inner) => inner.to_tokens(tokens), + Self::Call(inner) => inner.to_tokens(tokens), + Self::Storage(inner) => inner.to_tokens(tokens), + Self::Event(inner) => inner.to_tokens(tokens), + Self::Error(inner) => inner.to_tokens(tokens), + Self::Config(inner) => inner.to_tokens(tokens), + Self::Origin(inner) => inner.to_tokens(tokens), + Self::Inherent(inner) => inner.to_tokens(tokens), + Self::ValidateUnsigned(inner) => inner.to_tokens(tokens), + Self::FreezeReason(inner) => inner.to_tokens(tokens), + Self::HoldReason(inner) => inner.to_tokens(tokens), + Self::Task(inner) => inner.to_tokens(tokens), + Self::LockId(inner) => inner.to_tokens(tokens), + Self::SlashReason(inner) => inner.to_tokens(tokens), + } + } } #[derive(Debug, Clone)] pub struct PalletPart { - pub keyword: PalletPartKeyword, - pub generics: syn::Generics, + pub keyword: PalletPartKeyword, + pub generics: syn::Generics, } impl Parse for PalletPart { - fn parse(input: ParseStream) -> Result { - let keyword: PalletPartKeyword = input.parse()?; - - let generics: syn::Generics = input.parse()?; - if !generics.params.is_empty() && !keyword.allows_generic() { - let valid_generics = PalletPart::format_names(PalletPartKeyword::all_generic_arg()); - let msg = format!( - "`{}` is not allowed to have generics. \ + fn parse(input: ParseStream) -> Result { + let keyword: PalletPartKeyword = input.parse()?; + + let generics: syn::Generics = input.parse()?; + if !generics.params.is_empty() && !keyword.allows_generic() { + let valid_generics = PalletPart::format_names(PalletPartKeyword::all_generic_arg()); + let msg = format!( + "`{}` is not allowed to have generics. \ Only the following pallets are allowed to have generics: {}.", - keyword.name(), - valid_generics, - ); - return Err(syn::Error::new(keyword.span(), msg)) - } + keyword.name(), + valid_generics, + ); + return Err(syn::Error::new(keyword.span(), msg)); + } - Ok(Self { keyword, generics }) - } + Ok(Self { keyword, generics }) + } } impl PalletPart { - pub fn format_names(names: &[&'static str]) -> String { - let res: Vec<_> = names.iter().map(|s| format!("`{}`", s)).collect(); - res.join(", ") - } + pub fn format_names(names: &[&'static str]) -> String { + let res: Vec<_> = names.iter().map(|s| format!("`{}`", s)).collect(); + res.join(", ") + } - /// The name of this pallet part. - pub fn name(&self) -> &'static str { - self.keyword.name() - } + /// The name of this pallet part. + pub fn name(&self) -> &'static str { + self.keyword.name() + } } fn remove_kind( - input: ParseStream, - kind: WhereKind, - definitions: &mut Vec, + 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)) - } + 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)) + } } /// The declaration of a part without its generics #[derive(Debug, Clone)] pub struct PalletPartNoGeneric { - keyword: PalletPartKeyword, + keyword: PalletPartKeyword, } impl Parse for PalletPartNoGeneric { - fn parse(input: ParseStream) -> Result { - Ok(Self { keyword: input.parse()? }) - } + fn parse(input: ParseStream) -> Result { + Ok(Self { + keyword: input.parse()?, + }) + } } /// Parse [`PalletPartNoGeneric`]'s from a braces enclosed list that is split by commas, e.g. /// /// `{ Call, Event }` fn parse_pallet_parts_no_generic(input: ParseStream) -> Result> { - let pallet_parts: ext::Braces> = - input.parse()?; + let pallet_parts: ext::Braces> = + input.parse()?; - let mut resolved = HashSet::new(); - for part in pallet_parts.content.inner.iter() { - if !resolved.insert(part.keyword.name()) { - let msg = format!( - "`{}` was already declared before. Please remove the duplicate declaration", - part.keyword.name(), - ); - return Err(Error::new(part.keyword.span(), msg)) - } - } + let mut resolved = HashSet::new(); + for part in pallet_parts.content.inner.iter() { + if !resolved.insert(part.keyword.name()) { + let msg = format!( + "`{}` was already declared before. Please remove the duplicate declaration", + part.keyword.name(), + ); + return Err(Error::new(part.keyword.span(), msg)); + } + } - Ok(pallet_parts.content.inner.into_iter().collect()) + Ok(pallet_parts.content.inner.into_iter().collect()) } /// The final definition of a pallet with the resulting fixed index and explicit parts. #[derive(Debug, Clone)] pub struct Pallet { - /// Is this pallet fully expanded? - pub is_expanded: bool, - /// The name of the pallet, e.g.`System` in `System: frame_system`. - pub name: Ident, - /// Either automatically inferred, or defined (e.g. `MyPallet ... = 3,`). - pub index: u8, - /// The path of the pallet, e.g. `frame_system` in `System: frame_system`. - pub path: PalletPath, - /// The instance of the pallet, e.g. `Instance1` in `Council: pallet_collective::`. - pub instance: Option, - /// The pallet parts to use for the pallet. - pub pallet_parts: Vec, - /// Expressions specified inside of a #[cfg] attribute. - pub cfg_pattern: Vec, + /// Is this pallet fully expanded? + pub is_expanded: bool, + /// The name of the pallet, e.g.`System` in `System: frame_system`. + pub name: Ident, + /// Either automatically inferred, or defined (e.g. `MyPallet ... = 3,`). + pub index: u8, + /// The path of the pallet, e.g. `frame_system` in `System: frame_system`. + pub path: PalletPath, + /// The instance of the pallet, e.g. `Instance1` in `Council: pallet_collective::`. + pub instance: Option, + /// The pallet parts to use for the pallet. + pub pallet_parts: Vec, + /// Expressions specified inside of a #[cfg] attribute. + pub cfg_pattern: Vec, } impl Pallet { - /// Get resolved pallet parts - pub fn pallet_parts(&self) -> &[PalletPart] { - &self.pallet_parts - } + /// Get resolved pallet parts + pub fn pallet_parts(&self) -> &[PalletPart] { + &self.pallet_parts + } - /// Find matching parts - pub fn find_part(&self, name: &str) -> Option<&PalletPart> { - self.pallet_parts.iter().find(|part| part.name() == name) - } + /// Find matching parts + pub fn find_part(&self, name: &str) -> Option<&PalletPart> { + self.pallet_parts.iter().find(|part| part.name() == name) + } - /// Return whether pallet contains part - pub fn exists_part(&self, name: &str) -> bool { - self.find_part(name).is_some() - } + /// Return whether pallet contains part + pub fn exists_part(&self, name: &str) -> bool { + self.find_part(name).is_some() + } } /// Result of a conversion of a declaration of pallets. @@ -634,26 +675,26 @@ impl Pallet { /// +----------+ +----------+ +------------------+ /// ``` enum PalletsConversion { - /// Pallets implicitly declare parts. - /// - /// `System: frame_system`. - Implicit(Vec), - /// Pallets explicitly declare parts. - /// - /// `System: frame_system::{Pallet, Call}` - /// - /// However, for backwards compatibility with Polkadot/Kusama - /// we must propagate some other parts to the pallet by default. - Explicit(Vec), - /// Pallets explicitly declare parts that are fully expanded. - /// - /// This is the end state that contains extra parts included by - /// default by Substrate. - /// - /// `System: frame_system expanded::{Error} ::{Pallet, Call}` - /// - /// For this example, the `Pallet`, `Call` and `Error` parts are collected. - ExplicitExpanded(Vec), + /// Pallets implicitly declare parts. + /// + /// `System: frame_system`. + Implicit(Vec), + /// Pallets explicitly declare parts. + /// + /// `System: frame_system::{Pallet, Call}` + /// + /// However, for backwards compatibility with Polkadot/Kusama + /// we must propagate some other parts to the pallet by default. + Explicit(Vec), + /// Pallets explicitly declare parts that are fully expanded. + /// + /// This is the end state that contains extra parts included by + /// default by Substrate. + /// + /// `System: frame_system expanded::{Error} ::{Pallet, Call}` + /// + /// For this example, the `Pallet`, `Call` and `Error` parts are collected. + ExplicitExpanded(Vec), } /// Convert from the parsed pallet declaration to their final information. @@ -662,125 +703,137 @@ enum PalletsConversion { /// pallet using same rules as rust for fieldless enum. I.e. implicit are assigned number /// incrementally from last explicit or 0. fn convert_pallets(pallets: Vec) -> syn::Result { - if pallets.iter().any(|pallet| pallet.pallet_parts.is_none()) { - return Ok(PalletsConversion::Implicit(pallets)) - } - - let mut indices = HashMap::new(); - let mut last_index: Option = None; - let mut names = HashMap::new(); - let mut is_expanded = true; - - let pallets = pallets - .into_iter() - .map(|pallet| { - let final_index = match pallet.index { - Some(i) => i, - None => last_index.map_or(Some(0), |i| i.checked_add(1)).ok_or_else(|| { - let msg = "Pallet index doesn't fit into u8, index is 256"; - syn::Error::new(pallet.name.span(), msg) - })?, - }; - - last_index = Some(final_index); - - if let Some(used_pallet) = indices.insert(final_index, pallet.name.clone()) { - let msg = format!( - "Pallet indices are conflicting: Both pallets {} and {} are at index {}", - used_pallet, pallet.name, final_index, - ); - let mut err = syn::Error::new(used_pallet.span(), &msg); - err.combine(syn::Error::new(pallet.name.span(), msg)); - return Err(err) - } - - if let Some(used_pallet) = names.insert(pallet.name.clone(), pallet.name.span()) { - let msg = "Two pallets with the same name!"; - - let mut err = syn::Error::new(used_pallet, &msg); - err.combine(syn::Error::new(pallet.name.span(), &msg)); - return Err(err) - } - - let mut pallet_parts = pallet.pallet_parts.expect("Checked above"); - - let available_parts = - pallet_parts.iter().map(|part| part.keyword.name()).collect::>(); - - // Check parts are correctly specified - match &pallet.specified_parts { - SpecifiedParts::Exclude(parts) | SpecifiedParts::Use(parts) => - for part in parts { - if !available_parts.contains(part.keyword.name()) { - let msg = format!( - "Invalid pallet part specified, the pallet `{}` doesn't have the \ + if pallets.iter().any(|pallet| pallet.pallet_parts.is_none()) { + return Ok(PalletsConversion::Implicit(pallets)); + } + + let mut indices = HashMap::new(); + let mut last_index: Option = None; + let mut names = HashMap::new(); + let mut is_expanded = true; + + let pallets = pallets + .into_iter() + .map(|pallet| { + let final_index = match pallet.index { + Some(i) => i, + None => last_index + .map_or(Some(0), |i| i.checked_add(1)) + .ok_or_else(|| { + let msg = "Pallet index doesn't fit into u8, index is 256"; + syn::Error::new(pallet.name.span(), msg) + })?, + }; + + last_index = Some(final_index); + + if let Some(used_pallet) = indices.insert(final_index, pallet.name.clone()) { + let msg = format!( + "Pallet indices are conflicting: Both pallets {} and {} are at index {}", + used_pallet, pallet.name, final_index, + ); + let mut err = syn::Error::new(used_pallet.span(), &msg); + err.combine(syn::Error::new(pallet.name.span(), msg)); + return Err(err); + } + + if let Some(used_pallet) = names.insert(pallet.name.clone(), pallet.name.span()) { + let msg = "Two pallets with the same name!"; + + let mut err = syn::Error::new(used_pallet, &msg); + err.combine(syn::Error::new(pallet.name.span(), &msg)); + return Err(err); + } + + let mut pallet_parts = pallet.pallet_parts.expect("Checked above"); + + let available_parts = pallet_parts + .iter() + .map(|part| part.keyword.name()) + .collect::>(); + + // Check parts are correctly specified + match &pallet.specified_parts { + SpecifiedParts::Exclude(parts) | SpecifiedParts::Use(parts) => { + for part in parts { + if !available_parts.contains(part.keyword.name()) { + let msg = format!( + "Invalid pallet part specified, the pallet `{}` doesn't have the \ `{}` part. Available parts are: {}.", - pallet.name, - part.keyword.name(), - pallet_parts.iter().fold(String::new(), |fold, part| { - if fold.is_empty() { - format!("`{}`", part.keyword.name()) - } else { - format!("{}, `{}`", fold, part.keyword.name()) - } - }) - ); - return Err(syn::Error::new(part.keyword.span(), msg)) - } - }, - SpecifiedParts::All => (), - } - - // Set only specified parts. - match pallet.specified_parts { - SpecifiedParts::Exclude(excluded_parts) => pallet_parts.retain(|part| { - !excluded_parts - .iter() - .any(|excluded_part| excluded_part.keyword.name() == part.keyword.name()) - }), - SpecifiedParts::Use(used_parts) => pallet_parts.retain(|part| { - used_parts.iter().any(|use_part| use_part.keyword.name() == part.keyword.name()) - }), - SpecifiedParts::All => (), - } - - let cfg_pattern = pallet - .attrs - .iter() - .map(|attr| { - if attr.path().segments.first().map_or(false, |s| s.ident != "cfg") { - let msg = "Unsupported attribute, only #[cfg] is supported on pallet \ + pallet.name, + part.keyword.name(), + pallet_parts.iter().fold(String::new(), |fold, part| { + if fold.is_empty() { + format!("`{}`", part.keyword.name()) + } else { + format!("{}, `{}`", fold, part.keyword.name()) + } + }) + ); + return Err(syn::Error::new(part.keyword.span(), msg)); + } + } + } + SpecifiedParts::All => (), + } + + // Set only specified parts. + match pallet.specified_parts { + SpecifiedParts::Exclude(excluded_parts) => pallet_parts.retain(|part| { + !excluded_parts + .iter() + .any(|excluded_part| excluded_part.keyword.name() == part.keyword.name()) + }), + SpecifiedParts::Use(used_parts) => pallet_parts.retain(|part| { + used_parts + .iter() + .any(|use_part| use_part.keyword.name() == part.keyword.name()) + }), + SpecifiedParts::All => (), + } + + let cfg_pattern = pallet + .attrs + .iter() + .map(|attr| { + if attr + .path() + .segments + .first() + .map_or(false, |s| s.ident != "cfg") + { + let msg = "Unsupported attribute, only #[cfg] is supported on pallet \ declarations in `construct_runtime`"; - return Err(syn::Error::new(attr.span(), msg)) - } - - attr.parse_args_with(|input: syn::parse::ParseStream| { - // Required, otherwise the parse stream doesn't advance and will result in - // an error. - let input = input.parse::()?; - cfg_expr::Expression::parse(&input.to_string()) - .map_err(|e| syn::Error::new(attr.span(), e.to_string())) - }) - }) - .collect::>>()?; - - is_expanded &= pallet.is_expanded; - - Ok(Pallet { - is_expanded: pallet.is_expanded, - name: pallet.name, - index: final_index, - path: pallet.path, - instance: pallet.instance, - cfg_pattern, - pallet_parts, - }) - }) - .collect::>>()?; - - if is_expanded { - Ok(PalletsConversion::ExplicitExpanded(pallets)) - } else { - Ok(PalletsConversion::Explicit(pallets)) - } + return Err(syn::Error::new(attr.span(), msg)); + } + + attr.parse_args_with(|input: syn::parse::ParseStream| { + // Required, otherwise the parse stream doesn't advance and will result in + // an error. + let input = input.parse::()?; + cfg_expr::Expression::parse(&input.to_string()) + .map_err(|e| syn::Error::new(attr.span(), e.to_string())) + }) + }) + .collect::>>()?; + + is_expanded &= pallet.is_expanded; + + Ok(Pallet { + is_expanded: pallet.is_expanded, + name: pallet.name, + index: final_index, + path: pallet.path, + instance: pallet.instance, + cfg_pattern, + pallet_parts, + }) + }) + .collect::>>()?; + + if is_expanded { + Ok(PalletsConversion::ExplicitExpanded(pallets)) + } else { + Ok(PalletsConversion::Explicit(pallets)) + } } diff --git a/support/procedural-fork/src/crate_version.rs b/support/procedural-fork/src/crate_version.rs index 8c8975a42..63e7c7279 100644 --- a/support/procedural-fork/src/crate_version.rs +++ b/support/procedural-fork/src/crate_version.rs @@ -24,31 +24,31 @@ use syn::{Error, Result}; /// Create an error that will be shown by rustc at the call site of the macro. fn create_error(message: &str) -> Error { - Error::new(Span::call_site(), message) + Error::new(Span::call_site(), message) } /// Implementation of the `crate_to_crate_version!` macro. pub fn crate_to_crate_version(input: proc_macro::TokenStream) -> Result { - if !input.is_empty() { - return Err(create_error("No arguments expected!")) - } + if !input.is_empty() { + return Err(create_error("No arguments expected!")); + } - let major_version = get_cargo_env_var::("CARGO_PKG_VERSION_MAJOR") - .map_err(|_| create_error("Major version needs to fit into `u16`"))?; + let major_version = get_cargo_env_var::("CARGO_PKG_VERSION_MAJOR") + .map_err(|_| create_error("Major version needs to fit into `u16`"))?; - let minor_version = get_cargo_env_var::("CARGO_PKG_VERSION_MINOR") - .map_err(|_| create_error("Minor version needs to fit into `u8`"))?; + let minor_version = get_cargo_env_var::("CARGO_PKG_VERSION_MINOR") + .map_err(|_| create_error("Minor version needs to fit into `u8`"))?; - let patch_version = get_cargo_env_var::("CARGO_PKG_VERSION_PATCH") - .map_err(|_| create_error("Patch version needs to fit into `u8`"))?; + let patch_version = get_cargo_env_var::("CARGO_PKG_VERSION_PATCH") + .map_err(|_| create_error("Patch version needs to fit into `u8`"))?; - let crate_ = generate_access_from_frame_or_crate("frame-support")?; + let crate_ = generate_access_from_frame_or_crate("frame-support")?; - Ok(quote::quote! { - #crate_::traits::CrateVersion { - major: #major_version, - minor: #minor_version, - patch: #patch_version, - } - }) + Ok(quote::quote! { + #crate_::traits::CrateVersion { + major: #major_version, + minor: #minor_version, + patch: #patch_version, + } + }) } diff --git a/support/procedural-fork/src/derive_impl.rs b/support/procedural-fork/src/derive_impl.rs index 54755f116..e91f9c534 100644 --- a/support/procedural-fork/src/derive_impl.rs +++ b/support/procedural-fork/src/derive_impl.rs @@ -23,65 +23,67 @@ use proc_macro2::TokenStream as TokenStream2; use quote::{quote, ToTokens}; use std::collections::HashSet; use syn::{ - parse2, parse_quote, spanned::Spanned, token, Ident, ImplItem, ItemImpl, Path, Result, Token, + parse2, parse_quote, spanned::Spanned, token, Ident, ImplItem, ItemImpl, Path, Result, Token, }; mod keyword { - syn::custom_keyword!(inject_runtime_type); - syn::custom_keyword!(no_aggregated_types); + syn::custom_keyword!(inject_runtime_type); + syn::custom_keyword!(no_aggregated_types); } #[derive(derive_syn_parse::Parse, PartialEq, Eq)] pub enum PalletAttrType { - #[peek(keyword::inject_runtime_type, name = "inject_runtime_type")] - RuntimeType(keyword::inject_runtime_type), + #[peek(keyword::inject_runtime_type, name = "inject_runtime_type")] + RuntimeType(keyword::inject_runtime_type), } #[derive(derive_syn_parse::Parse)] pub struct PalletAttr { - _pound: Token![#], - #[bracket] - _bracket: token::Bracket, - #[inside(_bracket)] - typ: PalletAttrType, + _pound: Token![#], + #[bracket] + _bracket: token::Bracket, + #[inside(_bracket)] + typ: PalletAttrType, } fn is_runtime_type(item: &syn::ImplItemType) -> bool { - item.attrs.iter().any(|attr| { - if let Ok(PalletAttr { typ: PalletAttrType::RuntimeType(_), .. }) = - parse2::(attr.into_token_stream()) - { - return true - } - false - }) + item.attrs.iter().any(|attr| { + if let Ok(PalletAttr { + typ: PalletAttrType::RuntimeType(_), + .. + }) = parse2::(attr.into_token_stream()) + { + return true; + } + false + }) } #[derive(Parse, Debug)] pub struct DeriveImplAttrArgs { - pub default_impl_path: Path, - _as: Option, - #[parse_if(_as.is_some())] - pub disambiguation_path: Option, - _comma: Option, - #[parse_if(_comma.is_some())] - pub no_aggregated_types: Option, + pub default_impl_path: Path, + _as: Option, + #[parse_if(_as.is_some())] + pub disambiguation_path: Option, + _comma: Option, + #[parse_if(_comma.is_some())] + pub no_aggregated_types: Option, } impl ForeignPath for DeriveImplAttrArgs { - fn foreign_path(&self) -> &Path { - &self.default_impl_path - } + fn foreign_path(&self) -> &Path { + &self.default_impl_path + } } impl ToTokens for DeriveImplAttrArgs { - fn to_tokens(&self, tokens: &mut TokenStream2) { - tokens.extend(self.default_impl_path.to_token_stream()); - tokens.extend(self._as.to_token_stream()); - tokens.extend(self.disambiguation_path.to_token_stream()); - tokens.extend(self._comma.to_token_stream()); - tokens.extend(self.no_aggregated_types.to_token_stream()); - } + fn to_tokens(&self, tokens: &mut TokenStream2) { + tokens.extend(self.default_impl_path.to_token_stream()); + tokens.extend(self._as.to_token_stream()); + tokens.extend(self.disambiguation_path.to_token_stream()); + tokens.extend(self._comma.to_token_stream()); + tokens.extend(self.no_aggregated_types.to_token_stream()); + } } /// Gets the [`Ident`] representation of the given [`ImplItem`], if one exists. Otherwise @@ -90,13 +92,13 @@ impl ToTokens for DeriveImplAttrArgs { /// Used by [`combine_impls`] to determine whether we can compare [`ImplItem`]s by [`Ident`] /// or not. fn impl_item_ident(impl_item: &ImplItem) -> Option<&Ident> { - match impl_item { - ImplItem::Const(item) => Some(&item.ident), - ImplItem::Fn(item) => Some(&item.sig.ident), - ImplItem::Type(item) => Some(&item.ident), - ImplItem::Macro(item) => item.mac.path.get_ident(), - _ => None, - } + match impl_item { + ImplItem::Const(item) => Some(&item.ident), + ImplItem::Fn(item) => Some(&item.sig.ident), + ImplItem::Type(item) => Some(&item.ident), + ImplItem::Macro(item) => item.mac.path.get_ident(), + _ => None, + } } /// The real meat behind `derive_impl`. Takes in a `local_impl`, which is the impl for which we @@ -112,64 +114,68 @@ fn impl_item_ident(impl_item: &ImplItem) -> Option<&Ident> { /// into `local_impl`. Items that lack an ident and also exist verbatim in `local_impl` are not /// copied over. fn combine_impls( - local_impl: ItemImpl, - foreign_impl: ItemImpl, - default_impl_path: Path, - disambiguation_path: Path, - inject_runtime_types: bool, + local_impl: ItemImpl, + foreign_impl: ItemImpl, + default_impl_path: Path, + disambiguation_path: Path, + inject_runtime_types: bool, ) -> ItemImpl { - let (existing_local_keys, existing_unsupported_items): (HashSet, HashSet) = - local_impl - .items - .iter() - .cloned() - .partition(|impl_item| impl_item_ident(impl_item).is_some()); - let existing_local_keys: HashSet = existing_local_keys - .into_iter() - .filter_map(|item| impl_item_ident(&item).cloned()) - .collect(); - let mut final_impl = local_impl; - let extended_items = foreign_impl.items.into_iter().filter_map(|item| { - if let Some(ident) = impl_item_ident(&item) { - if existing_local_keys.contains(&ident) { - // do not copy colliding items that have an ident - return None - } - if let ImplItem::Type(typ) = item.clone() { - let cfg_attrs = typ - .attrs - .iter() - .filter(|attr| attr.path().get_ident().map_or(false, |ident| ident == "cfg")) - .map(|attr| attr.to_token_stream()); - if is_runtime_type(&typ) { - let item: ImplItem = if inject_runtime_types { - parse_quote! { - #( #cfg_attrs )* - type #ident = #ident; - } - } else { - item - }; - return Some(item) - } - // modify and insert uncolliding type items - let modified_item: ImplItem = parse_quote! { - #( #cfg_attrs )* - type #ident = <#default_impl_path as #disambiguation_path>::#ident; - }; - return Some(modified_item) - } - // copy uncolliding non-type items that have an ident - Some(item) - } else { - // do not copy colliding items that lack an ident - (!existing_unsupported_items.contains(&item)) - // copy uncolliding items without an ident verbatim - .then_some(item) - } - }); - final_impl.items.extend(extended_items); - final_impl + let (existing_local_keys, existing_unsupported_items): (HashSet, HashSet) = + local_impl + .items + .iter() + .cloned() + .partition(|impl_item| impl_item_ident(impl_item).is_some()); + let existing_local_keys: HashSet = existing_local_keys + .into_iter() + .filter_map(|item| impl_item_ident(&item).cloned()) + .collect(); + let mut final_impl = local_impl; + let extended_items = foreign_impl.items.into_iter().filter_map(|item| { + if let Some(ident) = impl_item_ident(&item) { + if existing_local_keys.contains(&ident) { + // do not copy colliding items that have an ident + return None; + } + if let ImplItem::Type(typ) = item.clone() { + let cfg_attrs = typ + .attrs + .iter() + .filter(|attr| { + attr.path() + .get_ident() + .map_or(false, |ident| ident == "cfg") + }) + .map(|attr| attr.to_token_stream()); + if is_runtime_type(&typ) { + let item: ImplItem = if inject_runtime_types { + parse_quote! { + #( #cfg_attrs )* + type #ident = #ident; + } + } else { + item + }; + return Some(item); + } + // modify and insert uncolliding type items + let modified_item: ImplItem = parse_quote! { + #( #cfg_attrs )* + type #ident = <#default_impl_path as #disambiguation_path>::#ident; + }; + return Some(modified_item); + } + // copy uncolliding non-type items that have an ident + Some(item) + } else { + // do not copy colliding items that lack an ident + (!existing_unsupported_items.contains(&item)) + // copy uncolliding items without an ident verbatim + .then_some(item) + } + }); + final_impl.items.extend(extended_items); + final_impl } /// Computes the disambiguation path for the `derive_impl` attribute macro. @@ -178,25 +184,26 @@ fn combine_impls( /// disambiguation is used as is. If not, we infer the disambiguation path from the /// `foreign_impl_path` and the computed scope. fn compute_disambiguation_path( - disambiguation_path: Option, - foreign_impl: ItemImpl, - default_impl_path: Path, + disambiguation_path: Option, + foreign_impl: ItemImpl, + default_impl_path: Path, ) -> Result { - match (disambiguation_path, foreign_impl.clone().trait_) { - (Some(disambiguation_path), _) => Ok(disambiguation_path), - (None, Some((_, foreign_impl_path, _))) => - if default_impl_path.segments.len() > 1 { - let scope = default_impl_path.segments.first(); - Ok(parse_quote!(#scope :: #foreign_impl_path)) - } else { - Ok(foreign_impl_path) - }, - _ => Err(syn::Error::new( - default_impl_path.span(), - "Impl statement must have a defined type being implemented \ + match (disambiguation_path, foreign_impl.clone().trait_) { + (Some(disambiguation_path), _) => Ok(disambiguation_path), + (None, Some((_, foreign_impl_path, _))) => { + if default_impl_path.segments.len() > 1 { + let scope = default_impl_path.segments.first(); + Ok(parse_quote!(#scope :: #foreign_impl_path)) + } else { + Ok(foreign_impl_path) + } + } + _ => Err(syn::Error::new( + default_impl_path.span(), + "Impl statement must have a defined type being implemented \ for a defined type such as `impl A for B`", - )), - } + )), + } } /// Internal implementation behind [`#[derive_impl(..)]`](`macro@crate::derive_impl`). @@ -211,93 +218,99 @@ fn compute_disambiguation_path( /// `disambiguation_path`: the module path of the external trait we will use to qualify /// defaults imported from the external `impl` statement pub fn derive_impl( - default_impl_path: TokenStream2, - foreign_tokens: TokenStream2, - local_tokens: TokenStream2, - disambiguation_path: Option, - no_aggregated_types: Option, + default_impl_path: TokenStream2, + foreign_tokens: TokenStream2, + local_tokens: TokenStream2, + disambiguation_path: Option, + no_aggregated_types: Option, ) -> Result { - let local_impl = parse2::(local_tokens)?; - let foreign_impl = parse2::(foreign_tokens)?; - let default_impl_path = parse2::(default_impl_path)?; + let local_impl = parse2::(local_tokens)?; + let foreign_impl = parse2::(foreign_tokens)?; + let default_impl_path = parse2::(default_impl_path)?; - let disambiguation_path = compute_disambiguation_path( - disambiguation_path, - foreign_impl.clone(), - default_impl_path.clone(), - )?; + let disambiguation_path = compute_disambiguation_path( + disambiguation_path, + foreign_impl.clone(), + default_impl_path.clone(), + )?; - // generate the combined impl - let combined_impl = combine_impls( - local_impl, - foreign_impl, - default_impl_path, - disambiguation_path, - no_aggregated_types.is_none(), - ); + // generate the combined impl + let combined_impl = combine_impls( + local_impl, + foreign_impl, + default_impl_path, + disambiguation_path, + no_aggregated_types.is_none(), + ); - Ok(quote!(#combined_impl)) + Ok(quote!(#combined_impl)) } #[test] fn test_derive_impl_attr_args_parsing() { - parse2::(quote!( - some::path::TestDefaultConfig as some::path::DefaultConfig - )) - .unwrap(); - parse2::(quote!( - frame_system::prelude::testing::TestDefaultConfig as DefaultConfig - )) - .unwrap(); - parse2::(quote!(Something as some::path::DefaultConfig)).unwrap(); - parse2::(quote!(Something as DefaultConfig)).unwrap(); - parse2::(quote!(DefaultConfig)).unwrap(); - assert!(parse2::(quote!()).is_err()); - assert!(parse2::(quote!(Config Config)).is_err()); + parse2::(quote!( + some::path::TestDefaultConfig as some::path::DefaultConfig + )) + .unwrap(); + parse2::(quote!( + frame_system::prelude::testing::TestDefaultConfig as DefaultConfig + )) + .unwrap(); + parse2::(quote!(Something as some::path::DefaultConfig)).unwrap(); + parse2::(quote!(Something as DefaultConfig)).unwrap(); + parse2::(quote!(DefaultConfig)).unwrap(); + assert!(parse2::(quote!()).is_err()); + assert!(parse2::(quote!(Config Config)).is_err()); } #[test] fn test_runtime_type_with_doc() { - trait TestTrait { - type Test; - } - #[allow(unused)] - struct TestStruct; - let p = parse2::(quote!( - impl TestTrait for TestStruct { - /// Some doc - #[inject_runtime_type] - type Test = u32; - } - )) - .unwrap(); - for item in p.items { - if let ImplItem::Type(typ) = item { - assert_eq!(is_runtime_type(&typ), true); - } - } + trait TestTrait { + type Test; + } + #[allow(unused)] + struct TestStruct; + let p = parse2::(quote!( + impl TestTrait for TestStruct { + /// Some doc + #[inject_runtime_type] + type Test = u32; + } + )) + .unwrap(); + for item in p.items { + if let ImplItem::Type(typ) = item { + assert_eq!(is_runtime_type(&typ), true); + } + } } #[test] fn test_disambiguation_path() { - let foreign_impl: ItemImpl = parse_quote!(impl SomeTrait for SomeType {}); - let default_impl_path: Path = parse_quote!(SomeScope::SomeType); + let foreign_impl: ItemImpl = parse_quote!(impl SomeTrait for SomeType {}); + let default_impl_path: Path = parse_quote!(SomeScope::SomeType); - // disambiguation path is specified - let disambiguation_path = compute_disambiguation_path( - Some(parse_quote!(SomeScope::SomePath)), - foreign_impl.clone(), - default_impl_path.clone(), - ); - assert_eq!(disambiguation_path.unwrap(), parse_quote!(SomeScope::SomePath)); + // disambiguation path is specified + let disambiguation_path = compute_disambiguation_path( + Some(parse_quote!(SomeScope::SomePath)), + foreign_impl.clone(), + default_impl_path.clone(), + ); + assert_eq!( + disambiguation_path.unwrap(), + parse_quote!(SomeScope::SomePath) + ); - // disambiguation path is not specified and the default_impl_path has more than one segment - let disambiguation_path = - compute_disambiguation_path(None, foreign_impl.clone(), default_impl_path.clone()); - assert_eq!(disambiguation_path.unwrap(), parse_quote!(SomeScope::SomeTrait)); + // disambiguation path is not specified and the default_impl_path has more than one segment + let disambiguation_path = + compute_disambiguation_path(None, foreign_impl.clone(), default_impl_path.clone()); + assert_eq!( + disambiguation_path.unwrap(), + parse_quote!(SomeScope::SomeTrait) + ); - // disambiguation path is not specified and the default_impl_path has only one segment - let disambiguation_path = - compute_disambiguation_path(None, foreign_impl.clone(), parse_quote!(SomeType)); - assert_eq!(disambiguation_path.unwrap(), parse_quote!(SomeTrait)); + // disambiguation path is not specified and the default_impl_path has only one segment + let disambiguation_path = + compute_disambiguation_path(None, foreign_impl.clone(), parse_quote!(SomeType)); + assert_eq!(disambiguation_path.unwrap(), parse_quote!(SomeTrait)); } diff --git a/support/procedural-fork/src/dummy_part_checker.rs b/support/procedural-fork/src/dummy_part_checker.rs index 34d9a3e23..6bed541d1 100644 --- a/support/procedural-fork/src/dummy_part_checker.rs +++ b/support/procedural-fork/src/dummy_part_checker.rs @@ -19,61 +19,63 @@ use crate::COUNTER; use proc_macro::TokenStream; pub fn generate_dummy_part_checker(input: TokenStream) -> TokenStream { - if !input.is_empty() { - return syn::Error::new(proc_macro2::Span::call_site(), "No arguments expected") - .to_compile_error() - .into() - } + if !input.is_empty() { + return syn::Error::new(proc_macro2::Span::call_site(), "No arguments expected") + .to_compile_error() + .into(); + } - let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); - let no_op_macro_ident = - syn::Ident::new(&format!("__dummy_part_checker_{}", count), proc_macro2::Span::call_site()); + let no_op_macro_ident = syn::Ident::new( + &format!("__dummy_part_checker_{}", count), + proc_macro2::Span::call_site(), + ); - quote::quote!( - #[macro_export] - #[doc(hidden)] - macro_rules! #no_op_macro_ident { - ( $( $tt:tt )* ) => {}; - } + quote::quote!( + #[macro_export] + #[doc(hidden)] + macro_rules! #no_op_macro_ident { + ( $( $tt:tt )* ) => {}; + } - #[doc(hidden)] - pub mod __substrate_genesis_config_check { - #[doc(hidden)] - pub use #no_op_macro_ident as is_genesis_config_defined; - #[doc(hidden)] - pub use #no_op_macro_ident as is_std_enabled_for_genesis; - } + #[doc(hidden)] + pub mod __substrate_genesis_config_check { + #[doc(hidden)] + pub use #no_op_macro_ident as is_genesis_config_defined; + #[doc(hidden)] + pub use #no_op_macro_ident as is_std_enabled_for_genesis; + } - #[doc(hidden)] - pub mod __substrate_event_check { - #[doc(hidden)] - pub use #no_op_macro_ident as is_event_part_defined; - } + #[doc(hidden)] + pub mod __substrate_event_check { + #[doc(hidden)] + pub use #no_op_macro_ident as is_event_part_defined; + } - #[doc(hidden)] - pub mod __substrate_inherent_check { - #[doc(hidden)] - pub use #no_op_macro_ident as is_inherent_part_defined; - } + #[doc(hidden)] + pub mod __substrate_inherent_check { + #[doc(hidden)] + pub use #no_op_macro_ident as is_inherent_part_defined; + } - #[doc(hidden)] - pub mod __substrate_validate_unsigned_check { - #[doc(hidden)] - pub use #no_op_macro_ident as is_validate_unsigned_part_defined; - } + #[doc(hidden)] + pub mod __substrate_validate_unsigned_check { + #[doc(hidden)] + pub use #no_op_macro_ident as is_validate_unsigned_part_defined; + } - #[doc(hidden)] - pub mod __substrate_call_check { - #[doc(hidden)] - pub use #no_op_macro_ident as is_call_part_defined; - } + #[doc(hidden)] + pub mod __substrate_call_check { + #[doc(hidden)] + pub use #no_op_macro_ident as is_call_part_defined; + } - #[doc(hidden)] - pub mod __substrate_origin_check { - #[doc(hidden)] - pub use #no_op_macro_ident as is_origin_part_defined; - } - ) - .into() + #[doc(hidden)] + pub mod __substrate_origin_check { + #[doc(hidden)] + pub use #no_op_macro_ident as is_origin_part_defined; + } + ) + .into() } diff --git a/support/procedural-fork/src/dynamic_params.rs b/support/procedural-fork/src/dynamic_params.rs index 29399a885..70a18bf34 100644 --- a/support/procedural-fork/src/dynamic_params.rs +++ b/support/procedural-fork/src/dynamic_params.rs @@ -26,123 +26,132 @@ use syn::{parse2, spanned::Spanned, visit_mut, visit_mut::VisitMut, Result, Toke /// Parse and expand a `#[dynamic_params(..)]` module. pub fn dynamic_params(attr: TokenStream, item: TokenStream) -> Result { - DynamicParamModAttr::parse(attr, item).map(ToTokens::into_token_stream) + DynamicParamModAttr::parse(attr, item).map(ToTokens::into_token_stream) } /// Parse and expand `#[dynamic_pallet_params(..)]` attribute. pub fn dynamic_pallet_params(attr: TokenStream, item: TokenStream) -> Result { - DynamicPalletParamAttr::parse(attr, item).map(ToTokens::into_token_stream) + DynamicPalletParamAttr::parse(attr, item).map(ToTokens::into_token_stream) } /// Parse and expand `#[dynamic_aggregated_params_internal]` attribute. pub fn dynamic_aggregated_params_internal( - _attr: TokenStream, - item: TokenStream, + _attr: TokenStream, + item: TokenStream, ) -> Result { - parse2::(item).map(ToTokens::into_token_stream) + parse2::(item).map(ToTokens::into_token_stream) } /// A top `#[dynamic_params(..)]` attribute together with a mod. #[derive(derive_syn_parse::Parse)] pub struct DynamicParamModAttr { - params_mod: syn::ItemMod, - meta: DynamicParamModAttrMeta, + params_mod: syn::ItemMod, + meta: DynamicParamModAttrMeta, } /// The inner meta of a `#[dynamic_params(..)]` attribute. #[derive(derive_syn_parse::Parse)] pub struct DynamicParamModAttrMeta { - name: syn::Ident, - _comma: Option, - #[parse_if(_comma.is_some())] - params_pallet: Option, + name: syn::Ident, + _comma: Option, + #[parse_if(_comma.is_some())] + params_pallet: Option, } impl DynamicParamModAttr { - pub fn parse(attr: TokenStream, item: TokenStream) -> Result { - let params_mod = parse2(item)?; - let meta = parse2(attr)?; - Ok(Self { params_mod, meta }) - } - - pub fn inner_mods(&self) -> Vec { - self.params_mod.content.as_ref().map_or(Vec::new(), |(_, items)| { - items - .iter() - .filter_map(|i| match i { - syn::Item::Mod(m) => Some(m), - _ => None, - }) - .cloned() - .collect() - }) - } + pub fn parse(attr: TokenStream, item: TokenStream) -> Result { + let params_mod = parse2(item)?; + let meta = parse2(attr)?; + Ok(Self { params_mod, meta }) + } + + pub fn inner_mods(&self) -> Vec { + self.params_mod + .content + .as_ref() + .map_or(Vec::new(), |(_, items)| { + items + .iter() + .filter_map(|i| match i { + syn::Item::Mod(m) => Some(m), + _ => None, + }) + .cloned() + .collect() + }) + } } impl ToTokens for DynamicParamModAttr { - fn to_tokens(&self, tokens: &mut TokenStream) { - let scrate = match crate_access() { - Ok(path) => path, - Err(err) => return tokens.extend(err), - }; - let (mut params_mod, name) = (self.params_mod.clone(), &self.meta.name); - let dynam_params_ident = ¶ms_mod.ident; - - let mut quoted_enum = quote! {}; - for m in self.inner_mods() { - let aggregate_name = - syn::Ident::new(&m.ident.to_string().to_class_case(), m.ident.span()); - let mod_name = &m.ident; - - let mut attrs = m.attrs.clone(); - attrs.retain(|attr| !attr.path().is_ident("dynamic_pallet_params")); - if let Err(err) = ensure_codec_index(&attrs, m.span()) { - tokens.extend(err.into_compile_error()); - return - } - - quoted_enum.extend(quote! { - #(#attrs)* - #aggregate_name(#dynam_params_ident::#mod_name::Parameters), - }); - } - - // Inject the outer args into the inner `#[dynamic_pallet_params(..)]` attribute. - if let Some(params_pallet) = &self.meta.params_pallet { - MacroInjectArgs { runtime_params: name.clone(), params_pallet: params_pallet.clone() } - .visit_item_mod_mut(&mut params_mod); - } - - tokens.extend(quote! { - #params_mod - - #[#scrate::dynamic_params::dynamic_aggregated_params_internal] - pub enum #name { - #quoted_enum - } - }); - } + fn to_tokens(&self, tokens: &mut TokenStream) { + let scrate = match crate_access() { + Ok(path) => path, + Err(err) => return tokens.extend(err), + }; + let (mut params_mod, name) = (self.params_mod.clone(), &self.meta.name); + let dynam_params_ident = ¶ms_mod.ident; + + let mut quoted_enum = quote! {}; + for m in self.inner_mods() { + let aggregate_name = + syn::Ident::new(&m.ident.to_string().to_class_case(), m.ident.span()); + let mod_name = &m.ident; + + let mut attrs = m.attrs.clone(); + attrs.retain(|attr| !attr.path().is_ident("dynamic_pallet_params")); + if let Err(err) = ensure_codec_index(&attrs, m.span()) { + tokens.extend(err.into_compile_error()); + return; + } + + quoted_enum.extend(quote! { + #(#attrs)* + #aggregate_name(#dynam_params_ident::#mod_name::Parameters), + }); + } + + // Inject the outer args into the inner `#[dynamic_pallet_params(..)]` attribute. + if let Some(params_pallet) = &self.meta.params_pallet { + MacroInjectArgs { + runtime_params: name.clone(), + params_pallet: params_pallet.clone(), + } + .visit_item_mod_mut(&mut params_mod); + } + + tokens.extend(quote! { + #params_mod + + #[#scrate::dynamic_params::dynamic_aggregated_params_internal] + pub enum #name { + #quoted_enum + } + }); + } } /// Ensure there is a `#[codec(index = ..)]` attribute. fn ensure_codec_index(attrs: &Vec, span: Span) -> Result<()> { - let mut found = false; - - for attr in attrs.iter() { - if attr.path().is_ident("codec") { - let meta: syn::ExprAssign = attr.parse_args()?; - if meta.left.to_token_stream().to_string() == "index" { - found = true; - break - } - } - } - - if !found { - Err(syn::Error::new(span, "Missing explicit `#[codec(index = ..)]` attribute")) - } else { - Ok(()) - } + let mut found = false; + + for attr in attrs.iter() { + if attr.path().is_ident("codec") { + let meta: syn::ExprAssign = attr.parse_args()?; + if meta.left.to_token_stream().to_string() == "index" { + found = true; + break; + } + } + } + + if !found { + Err(syn::Error::new( + span, + "Missing explicit `#[codec(index = ..)]` attribute", + )) + } else { + Ok(()) + } } /// Used to inject arguments into the inner `#[dynamic_pallet_params(..)]` attribute. @@ -150,110 +159,125 @@ fn ensure_codec_index(attrs: &Vec, span: Span) -> Result<()> { /// This allows the outer `#[dynamic_params(..)]` attribute to specify some arguments that don't /// need to be repeated every time. struct MacroInjectArgs { - runtime_params: syn::Ident, - params_pallet: syn::Type, + runtime_params: syn::Ident, + params_pallet: syn::Type, } impl VisitMut for MacroInjectArgs { - fn visit_item_mod_mut(&mut self, item: &mut syn::ItemMod) { - // Check if the mod has a `#[dynamic_pallet_params(..)]` attribute. - let attr = item.attrs.iter_mut().find(|attr| attr.path().is_ident("dynamic_pallet_params")); - - if let Some(attr) = attr { - match &attr.meta { - syn::Meta::Path(path) => - assert_eq!(path.to_token_stream().to_string(), "dynamic_pallet_params"), - _ => (), - } - - let runtime_params = &self.runtime_params; - let params_pallet = &self.params_pallet; - - attr.meta = syn::parse2::(quote! { - dynamic_pallet_params(#runtime_params, #params_pallet) - }) - .unwrap() - .into(); - } - - visit_mut::visit_item_mod_mut(self, item); - } + fn visit_item_mod_mut(&mut self, item: &mut syn::ItemMod) { + // Check if the mod has a `#[dynamic_pallet_params(..)]` attribute. + let attr = item + .attrs + .iter_mut() + .find(|attr| attr.path().is_ident("dynamic_pallet_params")); + + if let Some(attr) = attr { + match &attr.meta { + syn::Meta::Path(path) => { + assert_eq!(path.to_token_stream().to_string(), "dynamic_pallet_params") + } + _ => (), + } + + let runtime_params = &self.runtime_params; + let params_pallet = &self.params_pallet; + + attr.meta = syn::parse2::(quote! { + dynamic_pallet_params(#runtime_params, #params_pallet) + }) + .unwrap() + .into(); + } + + visit_mut::visit_item_mod_mut(self, item); + } } /// The helper attribute of a `#[dynamic_pallet_params(runtime_params, params_pallet)]` /// attribute. #[derive(derive_syn_parse::Parse)] pub struct DynamicPalletParamAttr { - inner_mod: syn::ItemMod, - meta: DynamicPalletParamAttrMeta, + inner_mod: syn::ItemMod, + meta: DynamicPalletParamAttrMeta, } /// The inner meta of a `#[dynamic_pallet_params(..)]` attribute. #[derive(derive_syn_parse::Parse)] pub struct DynamicPalletParamAttrMeta { - runtime_params: syn::Ident, - _comma: Token![,], - parameter_pallet: syn::Type, + runtime_params: syn::Ident, + _comma: Token![,], + parameter_pallet: syn::Type, } impl DynamicPalletParamAttr { - pub fn parse(attr: TokenStream, item: TokenStream) -> Result { - Ok(Self { inner_mod: parse2(item)?, meta: parse2(attr)? }) - } - - pub fn statics(&self) -> Vec { - self.inner_mod.content.as_ref().map_or(Vec::new(), |(_, items)| { - items - .iter() - .filter_map(|i| match i { - syn::Item::Static(s) => Some(s), - _ => None, - }) - .cloned() - .collect() - }) - } + pub fn parse(attr: TokenStream, item: TokenStream) -> Result { + Ok(Self { + inner_mod: parse2(item)?, + meta: parse2(attr)?, + }) + } + + pub fn statics(&self) -> Vec { + self.inner_mod + .content + .as_ref() + .map_or(Vec::new(), |(_, items)| { + items + .iter() + .filter_map(|i| match i { + syn::Item::Static(s) => Some(s), + _ => None, + }) + .cloned() + .collect() + }) + } } impl ToTokens for DynamicPalletParamAttr { - fn to_tokens(&self, tokens: &mut TokenStream) { - let scrate = match crate_access() { - Ok(path) => path, - Err(err) => return tokens.extend(err), - }; - let (params_mod, parameter_pallet, runtime_params) = - (&self.inner_mod, &self.meta.parameter_pallet, &self.meta.runtime_params); - - let aggregate_name = - syn::Ident::new(¶ms_mod.ident.to_string().to_class_case(), params_mod.ident.span()); - let (mod_name, vis) = (¶ms_mod.ident, ¶ms_mod.vis); - let statics = self.statics(); - - let (mut key_names, mut key_values, mut defaults, mut attrs, mut value_types): ( - Vec<_>, - Vec<_>, - Vec<_>, - Vec<_>, - Vec<_>, - ) = Default::default(); - - for s in statics.iter() { - if let Err(err) = ensure_codec_index(&s.attrs, s.span()) { - tokens.extend(err.into_compile_error()); - return - } - - key_names.push(&s.ident); - key_values.push(format_ident!("{}Value", &s.ident)); - defaults.push(&s.expr); - attrs.push(&s.attrs); - value_types.push(&s.ty); - } - - let key_ident = syn::Ident::new("ParametersKey", params_mod.ident.span()); - let value_ident = syn::Ident::new("ParametersValue", params_mod.ident.span()); - let runtime_key_ident = format_ident!("{}Key", runtime_params); - let runtime_value_ident = format_ident!("{}Value", runtime_params); - - tokens.extend(quote! { + fn to_tokens(&self, tokens: &mut TokenStream) { + let scrate = match crate_access() { + Ok(path) => path, + Err(err) => return tokens.extend(err), + }; + let (params_mod, parameter_pallet, runtime_params) = ( + &self.inner_mod, + &self.meta.parameter_pallet, + &self.meta.runtime_params, + ); + + let aggregate_name = syn::Ident::new( + ¶ms_mod.ident.to_string().to_class_case(), + params_mod.ident.span(), + ); + let (mod_name, vis) = (¶ms_mod.ident, ¶ms_mod.vis); + let statics = self.statics(); + + let (mut key_names, mut key_values, mut defaults, mut attrs, mut value_types): ( + Vec<_>, + Vec<_>, + Vec<_>, + Vec<_>, + Vec<_>, + ) = Default::default(); + + for s in statics.iter() { + if let Err(err) = ensure_codec_index(&s.attrs, s.span()) { + tokens.extend(err.into_compile_error()); + return; + } + + key_names.push(&s.ident); + key_values.push(format_ident!("{}Value", &s.ident)); + defaults.push(&s.expr); + attrs.push(&s.attrs); + value_types.push(&s.ty); + } + + let key_ident = syn::Ident::new("ParametersKey", params_mod.ident.span()); + let value_ident = syn::Ident::new("ParametersValue", params_mod.ident.span()); + let runtime_key_ident = format_ident!("{}Key", runtime_params); + let runtime_value_ident = format_ident!("{}Value", runtime_params); + + tokens.extend(quote! { pub mod #mod_name { use super::*; @@ -426,44 +450,44 @@ impl ToTokens for DynamicPalletParamAttr { )* } }); - } + } } #[derive(derive_syn_parse::Parse)] pub struct DynamicParamAggregatedEnum { - aggregated_enum: syn::ItemEnum, + aggregated_enum: syn::ItemEnum, } impl ToTokens for DynamicParamAggregatedEnum { - fn to_tokens(&self, tokens: &mut TokenStream) { - let scrate = match crate_access() { - Ok(path) => path, - Err(err) => return tokens.extend(err), - }; - let params_enum = &self.aggregated_enum; - let (name, vis) = (¶ms_enum.ident, ¶ms_enum.vis); - - let (mut indices, mut param_names, mut param_types): (Vec<_>, Vec<_>, Vec<_>) = - Default::default(); - let mut attributes = Vec::new(); - for (i, variant) in params_enum.variants.iter().enumerate() { - indices.push(i); - param_names.push(&variant.ident); - attributes.push(&variant.attrs); - - param_types.push(match &variant.fields { + fn to_tokens(&self, tokens: &mut TokenStream) { + let scrate = match crate_access() { + Ok(path) => path, + Err(err) => return tokens.extend(err), + }; + let params_enum = &self.aggregated_enum; + let (name, vis) = (¶ms_enum.ident, ¶ms_enum.vis); + + let (mut indices, mut param_names, mut param_types): (Vec<_>, Vec<_>, Vec<_>) = + Default::default(); + let mut attributes = Vec::new(); + for (i, variant) in params_enum.variants.iter().enumerate() { + indices.push(i); + param_names.push(&variant.ident); + attributes.push(&variant.attrs); + + param_types.push(match &variant.fields { syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty, _ => { *tokens = quote! { compile_error!("Only unnamed enum variants with one inner item are supported") }; return }, }); - } + } - let params_key_ident = format_ident!("{}Key", params_enum.ident); - let params_value_ident = format_ident!("{}Value", params_enum.ident); + let params_key_ident = format_ident!("{}Key", params_enum.ident); + let params_value_ident = format_ident!("{}Value", params_enum.ident); - tokens.extend(quote! { + tokens.extend(quote! { #[doc(hidden)] #[derive( Clone, @@ -554,10 +578,10 @@ impl ToTokens for DynamicParamAggregatedEnum { } )* }); - } + } } /// Get access to the current crate and convert the error to a compile error. fn crate_access() -> core::result::Result { - generate_access_from_frame_or_crate("frame-support").map_err(|e| e.to_compile_error()) + generate_access_from_frame_or_crate("frame-support").map_err(|e| e.to_compile_error()) } diff --git a/support/procedural-fork/src/key_prefix.rs b/support/procedural-fork/src/key_prefix.rs index 7f1ab6866..aea60ce3b 100644 --- a/support/procedural-fork/src/key_prefix.rs +++ b/support/procedural-fork/src/key_prefix.rs @@ -23,82 +23,84 @@ use syn::{Ident, Result}; const MAX_IDENTS: usize = 18; pub fn impl_key_prefix_for_tuples(input: proc_macro::TokenStream) -> Result { - if !input.is_empty() { - return Err(syn::Error::new(Span::call_site(), "No arguments expected")) - } - - let mut all_trait_impls = TokenStream::new(); - let frame_support = generate_access_from_frame_or_crate("frame-support")?; - - for i in 2..=MAX_IDENTS { - let current_tuple = (0..i) - .map(|n| Ident::new(&format!("Tuple{}", n), Span::call_site())) - .collect::>(); - - for prefix_count in 1..i { - let (prefixes, suffixes) = current_tuple.split_at(prefix_count); - - let hashers = current_tuple - .iter() - .map(|ident| format_ident!("Hasher{}", ident)) - .collect::>(); - let kargs = - prefixes.iter().map(|ident| format_ident!("KArg{}", ident)).collect::>(); - let partial_keygen = generate_keygen(prefixes); - let suffix_keygen = generate_keygen(suffixes); - let suffix_tuple = generate_tuple(suffixes); - - let trait_impls = quote! { - impl< - #(#current_tuple: FullCodec + StaticTypeInfo,)* - #(#hashers: StorageHasher,)* - #(#kargs: EncodeLike<#prefixes>),* - > HasKeyPrefix<( #( #kargs, )* )> for ( #( Key<#hashers, #current_tuple>, )* ) { - type Suffix = #suffix_tuple; - - fn partial_key(prefix: ( #( #kargs, )* )) -> Vec { - <#partial_keygen>::final_key(prefix) - } - } - - impl< - #(#current_tuple: FullCodec + StaticTypeInfo,)* - #(#hashers: ReversibleStorageHasher,)* - #(#kargs: EncodeLike<#prefixes>),* - > HasReversibleKeyPrefix<( #( #kargs, )* )> for ( #( Key<#hashers, #current_tuple>, )* ) { - fn decode_partial_key(key_material: &[u8]) -> Result< - Self::Suffix, - #frame_support::__private::codec::Error, - > { - <#suffix_keygen>::decode_final_key(key_material).map(|k| k.0) - } - } - }; - - all_trait_impls.extend(trait_impls); - } - } - - Ok(all_trait_impls) + if !input.is_empty() { + return Err(syn::Error::new(Span::call_site(), "No arguments expected")); + } + + let mut all_trait_impls = TokenStream::new(); + let frame_support = generate_access_from_frame_or_crate("frame-support")?; + + for i in 2..=MAX_IDENTS { + let current_tuple = (0..i) + .map(|n| Ident::new(&format!("Tuple{}", n), Span::call_site())) + .collect::>(); + + for prefix_count in 1..i { + let (prefixes, suffixes) = current_tuple.split_at(prefix_count); + + let hashers = current_tuple + .iter() + .map(|ident| format_ident!("Hasher{}", ident)) + .collect::>(); + let kargs = prefixes + .iter() + .map(|ident| format_ident!("KArg{}", ident)) + .collect::>(); + let partial_keygen = generate_keygen(prefixes); + let suffix_keygen = generate_keygen(suffixes); + let suffix_tuple = generate_tuple(suffixes); + + let trait_impls = quote! { + impl< + #(#current_tuple: FullCodec + StaticTypeInfo,)* + #(#hashers: StorageHasher,)* + #(#kargs: EncodeLike<#prefixes>),* + > HasKeyPrefix<( #( #kargs, )* )> for ( #( Key<#hashers, #current_tuple>, )* ) { + type Suffix = #suffix_tuple; + + fn partial_key(prefix: ( #( #kargs, )* )) -> Vec { + <#partial_keygen>::final_key(prefix) + } + } + + impl< + #(#current_tuple: FullCodec + StaticTypeInfo,)* + #(#hashers: ReversibleStorageHasher,)* + #(#kargs: EncodeLike<#prefixes>),* + > HasReversibleKeyPrefix<( #( #kargs, )* )> for ( #( Key<#hashers, #current_tuple>, )* ) { + fn decode_partial_key(key_material: &[u8]) -> Result< + Self::Suffix, + #frame_support::__private::codec::Error, + > { + <#suffix_keygen>::decode_final_key(key_material).map(|k| k.0) + } + } + }; + + all_trait_impls.extend(trait_impls); + } + } + + Ok(all_trait_impls) } fn generate_tuple(idents: &[Ident]) -> TokenStream { - if idents.len() == 1 { - idents[0].to_token_stream() - } else { - quote!((#(#idents),*)) - } + if idents.len() == 1 { + idents[0].to_token_stream() + } else { + quote!((#(#idents),*)) + } } fn generate_keygen(idents: &[Ident]) -> TokenStream { - if idents.len() == 1 { - let key = &idents[0]; - let hasher = format_ident!("Hasher{}", key); + if idents.len() == 1 { + let key = &idents[0]; + let hasher = format_ident!("Hasher{}", key); - quote!(Key<#hasher, #key>) - } else { - let hashers = idents.iter().map(|ident| format_ident!("Hasher{}", ident)); + quote!(Key<#hasher, #key>) + } else { + let hashers = idents.iter().map(|ident| format_ident!("Hasher{}", ident)); - quote!((#(Key<#hashers, #idents>),*)) - } + quote!((#(Key<#hashers, #idents>),*)) + } } diff --git a/support/procedural-fork/src/match_and_insert.rs b/support/procedural-fork/src/match_and_insert.rs index aa9cc56d7..a80b6e95f 100644 --- a/support/procedural-fork/src/match_and_insert.rs +++ b/support/procedural-fork/src/match_and_insert.rs @@ -22,138 +22,152 @@ use std::iter::once; use syn::spanned::Spanned; mod keyword { - syn::custom_keyword!(target); - syn::custom_keyword!(pattern); - syn::custom_keyword!(tokens); + syn::custom_keyword!(target); + syn::custom_keyword!(pattern); + syn::custom_keyword!(tokens); } pub fn match_and_insert(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let MatchAndInsertDef { pattern, tokens, target } = - syn::parse_macro_input!(input as MatchAndInsertDef); - - match expand_in_stream(&pattern, &mut Some(tokens), target) { - Ok(stream) => stream.into(), - Err(err) => err.to_compile_error().into(), - } + let MatchAndInsertDef { + pattern, + tokens, + target, + } = syn::parse_macro_input!(input as MatchAndInsertDef); + + match expand_in_stream(&pattern, &mut Some(tokens), target) { + Ok(stream) => stream.into(), + Err(err) => err.to_compile_error().into(), + } } struct MatchAndInsertDef { - // Token stream to search and insert tokens into. - target: TokenStream, - // Pattern to match against, this is ensured to have no TokenTree::Group nor TokenTree::Literal - // (i.e. contains only Punct or Ident), and not being empty. - pattern: Vec, - // Token stream to insert after the match pattern. - tokens: TokenStream, + // Token stream to search and insert tokens into. + target: TokenStream, + // Pattern to match against, this is ensured to have no TokenTree::Group nor TokenTree::Literal + // (i.e. contains only Punct or Ident), and not being empty. + pattern: Vec, + // Token stream to insert after the match pattern. + tokens: TokenStream, } impl syn::parse::Parse for MatchAndInsertDef { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let mut target; - let _ = input.parse::()?; - let _ = input.parse::()?; - let _replace_with_bracket: syn::token::Bracket = syn::bracketed!(target in input); - let _replace_with_brace: syn::token::Brace = syn::braced!(target in target); - let target = target.parse()?; - - let mut pattern; - let _ = input.parse::()?; - let _ = input.parse::()?; - let _replace_with_bracket: syn::token::Bracket = syn::bracketed!(pattern in input); - let _replace_with_brace: syn::token::Brace = syn::braced!(pattern in pattern); - let pattern = pattern.parse::()?.into_iter().collect::>(); - - if let Some(t) = pattern.iter().find(|t| matches!(t, TokenTree::Group(_))) { - return Err(syn::Error::new(t.span(), "Unexpected group token tree")) - } - if let Some(t) = pattern.iter().find(|t| matches!(t, TokenTree::Literal(_))) { - return Err(syn::Error::new(t.span(), "Unexpected literal token tree")) - } - - if pattern.is_empty() { - return Err(syn::Error::new(Span::call_site(), "empty match pattern is invalid")) - } - - let mut tokens; - let _ = input.parse::()?; - let _ = input.parse::()?; - let _replace_with_bracket: syn::token::Bracket = syn::bracketed!(tokens in input); - let _replace_with_brace: syn::token::Brace = syn::braced!(tokens in tokens); - let tokens = tokens.parse()?; - - Ok(Self { tokens, pattern, target }) - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut target; + let _ = input.parse::()?; + let _ = input.parse::()?; + let _replace_with_bracket: syn::token::Bracket = syn::bracketed!(target in input); + let _replace_with_brace: syn::token::Brace = syn::braced!(target in target); + let target = target.parse()?; + + let mut pattern; + let _ = input.parse::()?; + let _ = input.parse::()?; + let _replace_with_bracket: syn::token::Bracket = syn::bracketed!(pattern in input); + let _replace_with_brace: syn::token::Brace = syn::braced!(pattern in pattern); + let pattern = pattern + .parse::()? + .into_iter() + .collect::>(); + + if let Some(t) = pattern.iter().find(|t| matches!(t, TokenTree::Group(_))) { + return Err(syn::Error::new(t.span(), "Unexpected group token tree")); + } + if let Some(t) = pattern.iter().find(|t| matches!(t, TokenTree::Literal(_))) { + return Err(syn::Error::new(t.span(), "Unexpected literal token tree")); + } + + if pattern.is_empty() { + return Err(syn::Error::new( + Span::call_site(), + "empty match pattern is invalid", + )); + } + + let mut tokens; + let _ = input.parse::()?; + let _ = input.parse::()?; + let _replace_with_bracket: syn::token::Bracket = syn::bracketed!(tokens in input); + let _replace_with_brace: syn::token::Brace = syn::braced!(tokens in tokens); + let tokens = tokens.parse()?; + + Ok(Self { + tokens, + pattern, + target, + }) + } } // Insert `tokens` after the first matching `pattern`. // `tokens` must be some (Option is used for internal simplification). // `pattern` must not be empty and should only contain Ident or Punct. fn expand_in_stream( - pattern: &[TokenTree], - tokens: &mut Option, - stream: TokenStream, + pattern: &[TokenTree], + tokens: &mut Option, + stream: TokenStream, ) -> syn::Result { - assert!( - tokens.is_some(), - "`tokens` must be some, Option is used because `tokens` is used only once" - ); - assert!( - !pattern.is_empty(), - "`pattern` must not be empty, otherwise there is nothing to match against" - ); - - let stream_span = stream.span(); - let mut stream = stream.into_iter(); - let mut extended = TokenStream::new(); - let mut match_cursor = 0; - - while let Some(token) = stream.next() { - match token { - TokenTree::Group(group) => { - match_cursor = 0; - let group_stream = group.stream(); - match expand_in_stream(pattern, tokens, group_stream) { - Ok(s) => { - extended.extend(once(TokenTree::Group(Group::new(group.delimiter(), s)))); - extended.extend(stream); - return Ok(extended) - }, - Err(_) => { - extended.extend(once(TokenTree::Group(group))); - }, - } - }, - other => { - advance_match_cursor(&other, pattern, &mut match_cursor); - - extended.extend(once(other)); - - if match_cursor == pattern.len() { - extended - .extend(once(tokens.take().expect("tokens is used to replace only once"))); - extended.extend(stream); - return Ok(extended) - } - }, - } - } - // if we reach this point, it means the stream is empty and we haven't found a matching pattern - let msg = format!("Cannot find pattern `{:?}` in given token stream", pattern); - Err(syn::Error::new(stream_span, msg)) + assert!( + tokens.is_some(), + "`tokens` must be some, Option is used because `tokens` is used only once" + ); + assert!( + !pattern.is_empty(), + "`pattern` must not be empty, otherwise there is nothing to match against" + ); + + let stream_span = stream.span(); + let mut stream = stream.into_iter(); + let mut extended = TokenStream::new(); + let mut match_cursor = 0; + + while let Some(token) = stream.next() { + match token { + TokenTree::Group(group) => { + match_cursor = 0; + let group_stream = group.stream(); + match expand_in_stream(pattern, tokens, group_stream) { + Ok(s) => { + extended.extend(once(TokenTree::Group(Group::new(group.delimiter(), s)))); + extended.extend(stream); + return Ok(extended); + } + Err(_) => { + extended.extend(once(TokenTree::Group(group))); + } + } + } + other => { + advance_match_cursor(&other, pattern, &mut match_cursor); + + extended.extend(once(other)); + + if match_cursor == pattern.len() { + extended.extend(once( + tokens.take().expect("tokens is used to replace only once"), + )); + extended.extend(stream); + return Ok(extended); + } + } + } + } + // if we reach this point, it means the stream is empty and we haven't found a matching pattern + let msg = format!("Cannot find pattern `{:?}` in given token stream", pattern); + Err(syn::Error::new(stream_span, msg)) } fn advance_match_cursor(other: &TokenTree, pattern: &[TokenTree], match_cursor: &mut usize) { - use TokenTree::{Ident, Punct}; - - let does_match_other_pattern = match (other, &pattern[*match_cursor]) { - (Ident(i1), Ident(i2)) => i1 == i2, - (Punct(p1), Punct(p2)) => p1.as_char() == p2.as_char(), - _ => false, - }; - - if does_match_other_pattern { - *match_cursor += 1; - } else { - *match_cursor = 0; - } + use TokenTree::{Ident, Punct}; + + let does_match_other_pattern = match (other, &pattern[*match_cursor]) { + (Ident(i1), Ident(i2)) => i1 == i2, + (Punct(p1), Punct(p2)) => p1.as_char() == p2.as_char(), + _ => false, + }; + + if does_match_other_pattern { + *match_cursor += 1; + } else { + *match_cursor = 0; + } } diff --git a/support/procedural-fork/src/no_bound/clone.rs b/support/procedural-fork/src/no_bound/clone.rs index 346bf450f..17039bdc8 100644 --- a/support/procedural-fork/src/no_bound/clone.rs +++ b/support/procedural-fork/src/no_bound/clone.rs @@ -19,89 +19,93 @@ use syn::spanned::Spanned; /// Derive Clone but do not bound any generic. pub fn derive_clone_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input = syn::parse_macro_input!(input as syn::DeriveInput); + let input = syn::parse_macro_input!(input as syn::DeriveInput); - let name = &input.ident; - let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + let name = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - let impl_ = match input.data { - syn::Data::Struct(struct_) => match struct_.fields { - syn::Fields::Named(named) => { - let fields = named.named.iter().map(|i| &i.ident).map(|i| { - quote::quote_spanned!(i.span() => - #i: ::core::clone::Clone::clone(&self.#i) - ) - }); + let impl_ = match input.data { + syn::Data::Struct(struct_) => match struct_.fields { + syn::Fields::Named(named) => { + let fields = named.named.iter().map(|i| &i.ident).map(|i| { + quote::quote_spanned!(i.span() => + #i: ::core::clone::Clone::clone(&self.#i) + ) + }); - quote::quote!( Self { #( #fields, )* } ) - }, - syn::Fields::Unnamed(unnamed) => { - let fields = - unnamed.unnamed.iter().enumerate().map(|(i, _)| syn::Index::from(i)).map(|i| { - quote::quote_spanned!(i.span() => - ::core::clone::Clone::clone(&self.#i) - ) - }); + quote::quote!( Self { #( #fields, )* } ) + } + syn::Fields::Unnamed(unnamed) => { + let fields = unnamed + .unnamed + .iter() + .enumerate() + .map(|(i, _)| syn::Index::from(i)) + .map(|i| { + quote::quote_spanned!(i.span() => + ::core::clone::Clone::clone(&self.#i) + ) + }); - quote::quote!( Self ( #( #fields, )* ) ) - }, - syn::Fields::Unit => { - quote::quote!(Self) - }, - }, - syn::Data::Enum(enum_) => { - let variants = enum_.variants.iter().map(|variant| { - let ident = &variant.ident; - match &variant.fields { - syn::Fields::Named(named) => { - let captured = named.named.iter().map(|i| &i.ident); - let cloned = captured.clone().map(|i| { - ::quote::quote_spanned!(i.span() => - #i: ::core::clone::Clone::clone(#i) - ) - }); - quote::quote!( - Self::#ident { #( ref #captured, )* } => Self::#ident { #( #cloned, )*} - ) - }, - syn::Fields::Unnamed(unnamed) => { - let captured = unnamed - .unnamed - .iter() - .enumerate() - .map(|(i, f)| syn::Ident::new(&format!("_{}", i), f.span())); - let cloned = captured.clone().map(|i| { - quote::quote_spanned!(i.span() => - ::core::clone::Clone::clone(#i) - ) - }); - quote::quote!( - Self::#ident ( #( ref #captured, )* ) => Self::#ident ( #( #cloned, )*) - ) - }, - syn::Fields::Unit => quote::quote!( Self::#ident => Self::#ident ), - } - }); + quote::quote!( Self ( #( #fields, )* ) ) + } + syn::Fields::Unit => { + quote::quote!(Self) + } + }, + syn::Data::Enum(enum_) => { + let variants = enum_.variants.iter().map(|variant| { + let ident = &variant.ident; + match &variant.fields { + syn::Fields::Named(named) => { + let captured = named.named.iter().map(|i| &i.ident); + let cloned = captured.clone().map(|i| { + ::quote::quote_spanned!(i.span() => + #i: ::core::clone::Clone::clone(#i) + ) + }); + quote::quote!( + Self::#ident { #( ref #captured, )* } => Self::#ident { #( #cloned, )*} + ) + } + syn::Fields::Unnamed(unnamed) => { + let captured = unnamed + .unnamed + .iter() + .enumerate() + .map(|(i, f)| syn::Ident::new(&format!("_{}", i), f.span())); + let cloned = captured.clone().map(|i| { + quote::quote_spanned!(i.span() => + ::core::clone::Clone::clone(#i) + ) + }); + quote::quote!( + Self::#ident ( #( ref #captured, )* ) => Self::#ident ( #( #cloned, )*) + ) + } + syn::Fields::Unit => quote::quote!( Self::#ident => Self::#ident ), + } + }); - quote::quote!(match self { - #( #variants, )* - }) - }, - syn::Data::Union(_) => { - let msg = "Union type not supported by `derive(CloneNoBound)`"; - return syn::Error::new(input.span(), msg).to_compile_error().into() - }, - }; + quote::quote!(match self { + #( #variants, )* + }) + } + syn::Data::Union(_) => { + let msg = "Union type not supported by `derive(CloneNoBound)`"; + return syn::Error::new(input.span(), msg).to_compile_error().into(); + } + }; - quote::quote!( - const _: () = { - #[automatically_derived] - impl #impl_generics ::core::clone::Clone for #name #ty_generics #where_clause { - fn clone(&self) -> Self { - #impl_ - } - } - }; - ) - .into() + quote::quote!( + const _: () = { + #[automatically_derived] + impl #impl_generics ::core::clone::Clone for #name #ty_generics #where_clause { + fn clone(&self) -> Self { + #impl_ + } + } + }; + ) + .into() } diff --git a/support/procedural-fork/src/no_bound/debug.rs b/support/procedural-fork/src/no_bound/debug.rs index a1b3f4f0d..8034bb5ec 100644 --- a/support/procedural-fork/src/no_bound/debug.rs +++ b/support/procedural-fork/src/no_bound/debug.rs @@ -19,103 +19,103 @@ use syn::spanned::Spanned; /// Derive Debug but do not bound any generics. pub fn derive_debug_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input = syn::parse_macro_input!(input as syn::DeriveInput); + let input = syn::parse_macro_input!(input as syn::DeriveInput); - let input_ident = &input.ident; - let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + let input_ident = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - let impl_ = match input.data { - syn::Data::Struct(struct_) => match struct_.fields { - syn::Fields::Named(named) => { - let fields = - named.named.iter().map(|i| &i.ident).map( - |i| quote::quote_spanned!(i.span() => .field(stringify!(#i), &self.#i) ), - ); + let impl_ = match input.data { + syn::Data::Struct(struct_) => match struct_.fields { + syn::Fields::Named(named) => { + let fields = + named.named.iter().map(|i| &i.ident).map( + |i| quote::quote_spanned!(i.span() => .field(stringify!(#i), &self.#i) ), + ); - quote::quote!( - fmt.debug_struct(stringify!(#input_ident)) - #( #fields )* - .finish() - ) - }, - syn::Fields::Unnamed(unnamed) => { - let fields = unnamed - .unnamed - .iter() - .enumerate() - .map(|(i, _)| syn::Index::from(i)) - .map(|i| quote::quote_spanned!(i.span() => .field(&self.#i) )); + quote::quote!( + fmt.debug_struct(stringify!(#input_ident)) + #( #fields )* + .finish() + ) + } + syn::Fields::Unnamed(unnamed) => { + let fields = unnamed + .unnamed + .iter() + .enumerate() + .map(|(i, _)| syn::Index::from(i)) + .map(|i| quote::quote_spanned!(i.span() => .field(&self.#i) )); - quote::quote!( - fmt.debug_tuple(stringify!(#input_ident)) - #( #fields )* - .finish() - ) - }, - syn::Fields::Unit => quote::quote!(fmt.write_str(stringify!(#input_ident))), - }, - syn::Data::Enum(enum_) => { - let variants = enum_.variants.iter().map(|variant| { - let ident = &variant.ident; - let full_variant_str = format!("{}::{}", input_ident, ident); - match &variant.fields { - syn::Fields::Named(named) => { - let captured = named.named.iter().map(|i| &i.ident); - let debugged = captured.clone().map(|i| { - quote::quote_spanned!(i.span() => - .field(stringify!(#i), &#i) - ) - }); - quote::quote!( - Self::#ident { #( ref #captured, )* } => { - fmt.debug_struct(#full_variant_str) - #( #debugged )* - .finish() - } - ) - }, - syn::Fields::Unnamed(unnamed) => { - let captured = unnamed - .unnamed - .iter() - .enumerate() - .map(|(i, f)| syn::Ident::new(&format!("_{}", i), f.span())); - let debugged = captured - .clone() - .map(|i| quote::quote_spanned!(i.span() => .field(&#i))); - quote::quote!( - Self::#ident ( #( ref #captured, )* ) => { - fmt.debug_tuple(#full_variant_str) - #( #debugged )* - .finish() - } - ) - }, - syn::Fields::Unit => quote::quote!( - Self::#ident => fmt.write_str(#full_variant_str) - ), - } - }); + quote::quote!( + fmt.debug_tuple(stringify!(#input_ident)) + #( #fields )* + .finish() + ) + } + syn::Fields::Unit => quote::quote!(fmt.write_str(stringify!(#input_ident))), + }, + syn::Data::Enum(enum_) => { + let variants = enum_.variants.iter().map(|variant| { + let ident = &variant.ident; + let full_variant_str = format!("{}::{}", input_ident, ident); + match &variant.fields { + syn::Fields::Named(named) => { + let captured = named.named.iter().map(|i| &i.ident); + let debugged = captured.clone().map(|i| { + quote::quote_spanned!(i.span() => + .field(stringify!(#i), &#i) + ) + }); + quote::quote!( + Self::#ident { #( ref #captured, )* } => { + fmt.debug_struct(#full_variant_str) + #( #debugged )* + .finish() + } + ) + } + syn::Fields::Unnamed(unnamed) => { + let captured = unnamed + .unnamed + .iter() + .enumerate() + .map(|(i, f)| syn::Ident::new(&format!("_{}", i), f.span())); + let debugged = captured + .clone() + .map(|i| quote::quote_spanned!(i.span() => .field(&#i))); + quote::quote!( + Self::#ident ( #( ref #captured, )* ) => { + fmt.debug_tuple(#full_variant_str) + #( #debugged )* + .finish() + } + ) + } + syn::Fields::Unit => quote::quote!( + Self::#ident => fmt.write_str(#full_variant_str) + ), + } + }); - quote::quote!(match *self { - #( #variants, )* - }) - }, - syn::Data::Union(_) => { - let msg = "Union type not supported by `derive(DebugNoBound)`"; - return syn::Error::new(input.span(), msg).to_compile_error().into() - }, - }; + quote::quote!(match *self { + #( #variants, )* + }) + } + syn::Data::Union(_) => { + let msg = "Union type not supported by `derive(DebugNoBound)`"; + return syn::Error::new(input.span(), msg).to_compile_error().into(); + } + }; - quote::quote!( - const _: () = { - #[automatically_derived] - impl #impl_generics ::core::fmt::Debug for #input_ident #ty_generics #where_clause { - fn fmt(&self, fmt: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - #impl_ - } - } - }; - ) - .into() + quote::quote!( + const _: () = { + #[automatically_derived] + impl #impl_generics ::core::fmt::Debug for #input_ident #ty_generics #where_clause { + fn fmt(&self, fmt: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + #impl_ + } + } + }; + ) + .into() } diff --git a/support/procedural-fork/src/no_bound/default.rs b/support/procedural-fork/src/no_bound/default.rs index 0524247d2..1c0d90531 100644 --- a/support/procedural-fork/src/no_bound/default.rs +++ b/support/procedural-fork/src/no_bound/default.rs @@ -21,51 +21,57 @@ use syn::{spanned::Spanned, Data, DeriveInput, Fields}; /// Derive Default but do not bound any generic. pub fn derive_default_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input = syn::parse_macro_input!(input as DeriveInput); - - let name = &input.ident; - - let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - - let impl_ = match input.data { - Data::Struct(struct_) => match struct_.fields { - Fields::Named(named) => { - let fields = named.named.iter().map(|field| &field.ident).map(|ident| { - quote_spanned! {ident.span() => - #ident: ::core::default::Default::default() - } - }); - - quote!(Self { #( #fields, )* }) - }, - Fields::Unnamed(unnamed) => { - let fields = unnamed.unnamed.iter().map(|field| { - quote_spanned! {field.span()=> - ::core::default::Default::default() - } - }); - - quote!(Self( #( #fields, )* )) - }, - Fields::Unit => { - quote!(Self) - }, - }, - Data::Enum(enum_) => { - if enum_.variants.is_empty() { - return syn::Error::new_spanned(name, "cannot derive Default for an empty enum") - .to_compile_error() - .into() - } - - // all #[default] attrs with the variant they're on; i.e. a var - let default_variants = enum_ - .variants - .into_iter() - .filter(|variant| variant.attrs.iter().any(|attr| attr.path().is_ident("default"))) - .collect::>(); - - match &*default_variants { + let input = syn::parse_macro_input!(input as DeriveInput); + + let name = &input.ident; + + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + + let impl_ = + match input.data { + Data::Struct(struct_) => match struct_.fields { + Fields::Named(named) => { + let fields = named.named.iter().map(|field| &field.ident).map(|ident| { + quote_spanned! {ident.span() => + #ident: ::core::default::Default::default() + } + }); + + quote!(Self { #( #fields, )* }) + } + Fields::Unnamed(unnamed) => { + let fields = unnamed.unnamed.iter().map(|field| { + quote_spanned! {field.span()=> + ::core::default::Default::default() + } + }); + + quote!(Self( #( #fields, )* )) + } + Fields::Unit => { + quote!(Self) + } + }, + Data::Enum(enum_) => { + if enum_.variants.is_empty() { + return syn::Error::new_spanned(name, "cannot derive Default for an empty enum") + .to_compile_error() + .into(); + } + + // all #[default] attrs with the variant they're on; i.e. a var + let default_variants = enum_ + .variants + .into_iter() + .filter(|variant| { + variant + .attrs + .iter() + .any(|attr| attr.path().is_ident("default")) + }) + .collect::>(); + + match &*default_variants { [] => return syn::Error::new( name.clone().span(), "no default declared, make a variant default by placing `#[default]` above it", @@ -137,25 +143,26 @@ pub fn derive_default_no_bound(input: proc_macro::TokenStream) -> proc_macro::To return err.into_compile_error().into() }, } - }, - Data::Union(union_) => - return syn::Error::new_spanned( - union_.union_token, - "Union type not supported by `derive(DefaultNoBound)`", - ) - .to_compile_error() - .into(), - }; - - quote!( - const _: () = { - #[automatically_derived] - impl #impl_generics ::core::default::Default for #name #ty_generics #where_clause { - fn default() -> Self { - #impl_ - } - } - }; - ) - .into() + } + Data::Union(union_) => { + return syn::Error::new_spanned( + union_.union_token, + "Union type not supported by `derive(DefaultNoBound)`", + ) + .to_compile_error() + .into() + } + }; + + quote!( + const _: () = { + #[automatically_derived] + impl #impl_generics ::core::default::Default for #name #ty_generics #where_clause { + fn default() -> Self { + #impl_ + } + } + }; + ) + .into() } diff --git a/support/procedural-fork/src/no_bound/ord.rs b/support/procedural-fork/src/no_bound/ord.rs index b24d27c04..20f30eb9d 100644 --- a/support/procedural-fork/src/no_bound/ord.rs +++ b/support/procedural-fork/src/no_bound/ord.rs @@ -19,57 +19,57 @@ use syn::spanned::Spanned; /// Derive Ord but do not bound any generic. pub fn derive_ord_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input: syn::DeriveInput = match syn::parse(input) { - Ok(input) => input, - Err(e) => return e.to_compile_error().into(), - }; + let input: syn::DeriveInput = match syn::parse(input) { + Ok(input) => input, + Err(e) => return e.to_compile_error().into(), + }; - let name = &input.ident; - let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + let name = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - let impl_ = match input.data { - syn::Data::Struct(struct_) => match struct_.fields { - syn::Fields::Named(named) => { - let fields = named - .named - .iter() - .map(|i| &i.ident) - .map(|i| quote::quote_spanned!(i.span() => self.#i.cmp(&other.#i) )); + let impl_ = match input.data { + syn::Data::Struct(struct_) => match struct_.fields { + syn::Fields::Named(named) => { + let fields = named + .named + .iter() + .map(|i| &i.ident) + .map(|i| quote::quote_spanned!(i.span() => self.#i.cmp(&other.#i) )); - quote::quote!( core::cmp::Ordering::Equal #( .then_with(|| #fields) )* ) - }, - syn::Fields::Unnamed(unnamed) => { - let fields = unnamed - .unnamed - .iter() - .enumerate() - .map(|(i, _)| syn::Index::from(i)) - .map(|i| quote::quote_spanned!(i.span() => self.#i.cmp(&other.#i) )); + quote::quote!( core::cmp::Ordering::Equal #( .then_with(|| #fields) )* ) + } + syn::Fields::Unnamed(unnamed) => { + let fields = unnamed + .unnamed + .iter() + .enumerate() + .map(|(i, _)| syn::Index::from(i)) + .map(|i| quote::quote_spanned!(i.span() => self.#i.cmp(&other.#i) )); - quote::quote!( core::cmp::Ordering::Equal #( .then_with(|| #fields) )* ) - }, - syn::Fields::Unit => { - quote::quote!(core::cmp::Ordering::Equal) - }, - }, - syn::Data::Enum(_) => { - let msg = "Enum type not supported by `derive(OrdNoBound)`"; - return syn::Error::new(input.span(), msg).to_compile_error().into() - }, - syn::Data::Union(_) => { - let msg = "Union type not supported by `derive(OrdNoBound)`"; - return syn::Error::new(input.span(), msg).to_compile_error().into() - }, - }; + quote::quote!( core::cmp::Ordering::Equal #( .then_with(|| #fields) )* ) + } + syn::Fields::Unit => { + quote::quote!(core::cmp::Ordering::Equal) + } + }, + syn::Data::Enum(_) => { + let msg = "Enum type not supported by `derive(OrdNoBound)`"; + return syn::Error::new(input.span(), msg).to_compile_error().into(); + } + syn::Data::Union(_) => { + let msg = "Union type not supported by `derive(OrdNoBound)`"; + return syn::Error::new(input.span(), msg).to_compile_error().into(); + } + }; - quote::quote!( - const _: () = { - impl #impl_generics core::cmp::Ord for #name #ty_generics #where_clause { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - #impl_ - } - } - }; - ) - .into() + quote::quote!( + const _: () = { + impl #impl_generics core::cmp::Ord for #name #ty_generics #where_clause { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + #impl_ + } + } + }; + ) + .into() } diff --git a/support/procedural-fork/src/no_bound/partial_eq.rs b/support/procedural-fork/src/no_bound/partial_eq.rs index a1be71a96..8833f6e5f 100644 --- a/support/procedural-fork/src/no_bound/partial_eq.rs +++ b/support/procedural-fork/src/no_bound/partial_eq.rs @@ -19,119 +19,119 @@ use syn::spanned::Spanned; /// Derive PartialEq but do not bound any generic. pub fn derive_partial_eq_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input = syn::parse_macro_input!(input as syn::DeriveInput); + let input = syn::parse_macro_input!(input as syn::DeriveInput); - let name = &input.ident; - let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + let name = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - let impl_ = match input.data { - syn::Data::Struct(struct_) => match struct_.fields { - syn::Fields::Named(named) => { - let fields = named - .named - .iter() - .map(|i| &i.ident) - .map(|i| quote::quote_spanned!(i.span() => self.#i == other.#i )); + let impl_ = match input.data { + syn::Data::Struct(struct_) => match struct_.fields { + syn::Fields::Named(named) => { + let fields = named + .named + .iter() + .map(|i| &i.ident) + .map(|i| quote::quote_spanned!(i.span() => self.#i == other.#i )); - quote::quote!( true #( && #fields )* ) - }, - syn::Fields::Unnamed(unnamed) => { - let fields = unnamed - .unnamed - .iter() - .enumerate() - .map(|(i, _)| syn::Index::from(i)) - .map(|i| quote::quote_spanned!(i.span() => self.#i == other.#i )); + quote::quote!( true #( && #fields )* ) + } + syn::Fields::Unnamed(unnamed) => { + let fields = unnamed + .unnamed + .iter() + .enumerate() + .map(|(i, _)| syn::Index::from(i)) + .map(|i| quote::quote_spanned!(i.span() => self.#i == other.#i )); - quote::quote!( true #( && #fields )* ) - }, - syn::Fields::Unit => { - quote::quote!(true) - }, - }, - syn::Data::Enum(enum_) => { - let variants = - enum_.variants.iter().map(|variant| { - let ident = &variant.ident; - match &variant.fields { - syn::Fields::Named(named) => { - let names = named.named.iter().map(|i| &i.ident); - let other_names = names.clone().enumerate().map(|(n, ident)| { - syn::Ident::new(&format!("_{}", n), ident.span()) - }); + quote::quote!( true #( && #fields )* ) + } + syn::Fields::Unit => { + quote::quote!(true) + } + }, + syn::Data::Enum(enum_) => { + let variants = + enum_.variants.iter().map(|variant| { + let ident = &variant.ident; + match &variant.fields { + syn::Fields::Named(named) => { + let names = named.named.iter().map(|i| &i.ident); + let other_names = names.clone().enumerate().map(|(n, ident)| { + syn::Ident::new(&format!("_{}", n), ident.span()) + }); - let capture = names.clone(); - let other_capture = names - .clone() - .zip(other_names.clone()) - .map(|(i, other_i)| quote::quote!(#i: #other_i)); - let eq = names.zip(other_names).map( - |(i, other_i)| quote::quote_spanned!(i.span() => #i == #other_i), - ); - quote::quote!( - ( - Self::#ident { #( #capture, )* }, - Self::#ident { #( #other_capture, )* }, - ) => true #( && #eq )* - ) - }, - syn::Fields::Unnamed(unnamed) => { - let names = unnamed - .unnamed - .iter() - .enumerate() - .map(|(i, f)| syn::Ident::new(&format!("_{}", i), f.span())); - let other_names = - unnamed.unnamed.iter().enumerate().map(|(i, f)| { - syn::Ident::new(&format!("_{}_other", i), f.span()) - }); - let eq = names.clone().zip(other_names.clone()).map( - |(i, other_i)| quote::quote_spanned!(i.span() => #i == #other_i), - ); - quote::quote!( - ( - Self::#ident ( #( #names, )* ), - Self::#ident ( #( #other_names, )* ), - ) => true #( && #eq )* - ) - }, - syn::Fields::Unit => quote::quote!( (Self::#ident, Self::#ident) => true ), - } - }); + let capture = names.clone(); + let other_capture = names + .clone() + .zip(other_names.clone()) + .map(|(i, other_i)| quote::quote!(#i: #other_i)); + let eq = names.zip(other_names).map( + |(i, other_i)| quote::quote_spanned!(i.span() => #i == #other_i), + ); + quote::quote!( + ( + Self::#ident { #( #capture, )* }, + Self::#ident { #( #other_capture, )* }, + ) => true #( && #eq )* + ) + } + syn::Fields::Unnamed(unnamed) => { + let names = unnamed + .unnamed + .iter() + .enumerate() + .map(|(i, f)| syn::Ident::new(&format!("_{}", i), f.span())); + let other_names = + unnamed.unnamed.iter().enumerate().map(|(i, f)| { + syn::Ident::new(&format!("_{}_other", i), f.span()) + }); + let eq = names.clone().zip(other_names.clone()).map( + |(i, other_i)| quote::quote_spanned!(i.span() => #i == #other_i), + ); + quote::quote!( + ( + Self::#ident ( #( #names, )* ), + Self::#ident ( #( #other_names, )* ), + ) => true #( && #eq )* + ) + } + syn::Fields::Unit => quote::quote!( (Self::#ident, Self::#ident) => true ), + } + }); - let mut different_variants = vec![]; - for (i, i_variant) in enum_.variants.iter().enumerate() { - for (j, j_variant) in enum_.variants.iter().enumerate() { - if i != j { - let i_ident = &i_variant.ident; - let j_ident = &j_variant.ident; - different_variants.push(quote::quote!( - (Self::#i_ident { .. }, Self::#j_ident { .. }) => false - )) - } - } - } + let mut different_variants = vec![]; + for (i, i_variant) in enum_.variants.iter().enumerate() { + for (j, j_variant) in enum_.variants.iter().enumerate() { + if i != j { + let i_ident = &i_variant.ident; + let j_ident = &j_variant.ident; + different_variants.push(quote::quote!( + (Self::#i_ident { .. }, Self::#j_ident { .. }) => false + )) + } + } + } - quote::quote!( match (self, other) { - #( #variants, )* - #( #different_variants, )* - }) - }, - syn::Data::Union(_) => { - let msg = "Union type not supported by `derive(PartialEqNoBound)`"; - return syn::Error::new(input.span(), msg).to_compile_error().into() - }, - }; + quote::quote!( match (self, other) { + #( #variants, )* + #( #different_variants, )* + }) + } + syn::Data::Union(_) => { + let msg = "Union type not supported by `derive(PartialEqNoBound)`"; + return syn::Error::new(input.span(), msg).to_compile_error().into(); + } + }; - quote::quote!( - const _: () = { - #[automatically_derived] - impl #impl_generics ::core::cmp::PartialEq for #name #ty_generics #where_clause { - fn eq(&self, other: &Self) -> bool { - #impl_ - } - } - }; - ) - .into() + quote::quote!( + const _: () = { + #[automatically_derived] + impl #impl_generics ::core::cmp::PartialEq for #name #ty_generics #where_clause { + fn eq(&self, other: &Self) -> bool { + #impl_ + } + } + }; + ) + .into() } diff --git a/support/procedural-fork/src/no_bound/partial_ord.rs b/support/procedural-fork/src/no_bound/partial_ord.rs index 86aa42be9..c73199d4e 100644 --- a/support/procedural-fork/src/no_bound/partial_ord.rs +++ b/support/procedural-fork/src/no_bound/partial_ord.rs @@ -19,71 +19,72 @@ use syn::spanned::Spanned; /// Derive PartialOrd but do not bound any generic. pub fn derive_partial_ord_no_bound(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input: syn::DeriveInput = match syn::parse(input) { - Ok(input) => input, - Err(e) => return e.to_compile_error().into(), - }; + let input: syn::DeriveInput = match syn::parse(input) { + Ok(input) => input, + Err(e) => return e.to_compile_error().into(), + }; - let name = &input.ident; - let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + let name = &input.ident; + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - let impl_ = match input.data { - syn::Data::Struct(struct_) => - match struct_.fields { - syn::Fields::Named(named) => { - let fields = - named.named.iter().map(|i| &i.ident).map( - |i| quote::quote_spanned!(i.span() => self.#i.partial_cmp(&other.#i)), - ); + let impl_ = + match input.data { + syn::Data::Struct(struct_) => { + match struct_.fields { + syn::Fields::Named(named) => { + let fields = named.named.iter().map(|i| &i.ident).map( + |i| quote::quote_spanned!(i.span() => self.#i.partial_cmp(&other.#i)), + ); - quote::quote!( - Some(core::cmp::Ordering::Equal) - #( - .and_then(|order| { - let next_order = #fields?; - Some(order.then(next_order)) - }) - )* - ) - }, - syn::Fields::Unnamed(unnamed) => { - let fields = + quote::quote!( + Some(core::cmp::Ordering::Equal) + #( + .and_then(|order| { + let next_order = #fields?; + Some(order.then(next_order)) + }) + )* + ) + } + syn::Fields::Unnamed(unnamed) => { + let fields = unnamed.unnamed.iter().enumerate().map(|(i, _)| syn::Index::from(i)).map( |i| quote::quote_spanned!(i.span() => self.#i.partial_cmp(&other.#i)), ); - quote::quote!( - Some(core::cmp::Ordering::Equal) - #( - .and_then(|order| { - let next_order = #fields?; - Some(order.then(next_order)) - }) - )* - ) - }, - syn::Fields::Unit => { - quote::quote!(Some(core::cmp::Ordering::Equal)) - }, - }, - syn::Data::Enum(_) => { - let msg = "Enum type not supported by `derive(PartialOrdNoBound)`"; - return syn::Error::new(input.span(), msg).to_compile_error().into() - }, - syn::Data::Union(_) => { - let msg = "Union type not supported by `derive(PartialOrdNoBound)`"; - return syn::Error::new(input.span(), msg).to_compile_error().into() - }, - }; + quote::quote!( + Some(core::cmp::Ordering::Equal) + #( + .and_then(|order| { + let next_order = #fields?; + Some(order.then(next_order)) + }) + )* + ) + } + syn::Fields::Unit => { + quote::quote!(Some(core::cmp::Ordering::Equal)) + } + } + } + syn::Data::Enum(_) => { + let msg = "Enum type not supported by `derive(PartialOrdNoBound)`"; + return syn::Error::new(input.span(), msg).to_compile_error().into(); + } + syn::Data::Union(_) => { + let msg = "Union type not supported by `derive(PartialOrdNoBound)`"; + return syn::Error::new(input.span(), msg).to_compile_error().into(); + } + }; - quote::quote!( - const _: () = { - impl #impl_generics core::cmp::PartialOrd for #name #ty_generics #where_clause { - fn partial_cmp(&self, other: &Self) -> Option { - #impl_ - } - } - }; - ) - .into() + quote::quote!( + const _: () = { + impl #impl_generics core::cmp::PartialOrd for #name #ty_generics #where_clause { + fn partial_cmp(&self, other: &Self) -> Option { + #impl_ + } + } + }; + ) + .into() } diff --git a/support/procedural-fork/src/pallet/expand/call.rs b/support/procedural-fork/src/pallet/expand/call.rs index f395872c8..a39e81fd1 100644 --- a/support/procedural-fork/src/pallet/expand/call.rs +++ b/support/procedural-fork/src/pallet/expand/call.rs @@ -16,12 +16,12 @@ // limitations under the License. use crate::{ - pallet::{ - expand::warnings::{weight_constant_warning, weight_witness_warning}, - parse::call::CallWeightDef, - Def, - }, - COUNTER, + pallet::{ + expand::warnings::{weight_constant_warning, weight_witness_warning}, + parse::call::CallWeightDef, + Def, + }, + COUNTER, }; use proc_macro2::TokenStream as TokenStream2; use proc_macro_warning::Warning; @@ -32,45 +32,56 @@ use syn::spanned::Spanned; /// * Generate enum call and implement various trait on it. /// * Implement Callable and call_function on `Pallet` pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream { - let (span, where_clause, methods, docs) = match def.call.as_ref() { - Some(call) => { - let span = call.attr_span; - let where_clause = call.where_clause.clone(); - let methods = call.methods.clone(); - let docs = call.docs.clone(); - - (span, where_clause, methods, docs) - }, - None => (def.item.span(), def.config.where_clause.clone(), Vec::new(), Vec::new()), - }; - let frame_support = &def.frame_support; - let frame_system = &def.frame_system; - let type_impl_gen = &def.type_impl_generics(span); - let type_decl_bounded_gen = &def.type_decl_bounded_generics(span); - let type_use_gen = &def.type_use_generics(span); - let call_ident = syn::Ident::new("Call", span); - let pallet_ident = &def.pallet_struct.pallet; - - let fn_name = methods.iter().map(|method| &method.name).collect::>(); - let call_index = methods.iter().map(|method| method.call_index).collect::>(); - let new_call_variant_fn_name = fn_name - .iter() - .map(|fn_name| quote::format_ident!("new_call_variant_{}", fn_name)) - .collect::>(); - - let new_call_variant_doc = fn_name - .iter() - .map(|fn_name| format!("Create a call with the variant `{}`.", fn_name)) - .collect::>(); - - let mut call_index_warnings = Vec::new(); - // Emit a warning for each call that is missing `call_index` when not in dev-mode. - for method in &methods { - if method.explicit_call_index || def.dev_mode { - continue - } - - let warning = Warning::new_deprecated("ImplicitCallIndex") + let (span, where_clause, methods, docs) = match def.call.as_ref() { + Some(call) => { + let span = call.attr_span; + let where_clause = call.where_clause.clone(); + let methods = call.methods.clone(); + let docs = call.docs.clone(); + + (span, where_clause, methods, docs) + } + None => ( + def.item.span(), + def.config.where_clause.clone(), + Vec::new(), + Vec::new(), + ), + }; + let frame_support = &def.frame_support; + let frame_system = &def.frame_system; + let type_impl_gen = &def.type_impl_generics(span); + let type_decl_bounded_gen = &def.type_decl_bounded_generics(span); + let type_use_gen = &def.type_use_generics(span); + let call_ident = syn::Ident::new("Call", span); + let pallet_ident = &def.pallet_struct.pallet; + + let fn_name = methods + .iter() + .map(|method| &method.name) + .collect::>(); + let call_index = methods + .iter() + .map(|method| method.call_index) + .collect::>(); + let new_call_variant_fn_name = fn_name + .iter() + .map(|fn_name| quote::format_ident!("new_call_variant_{}", fn_name)) + .collect::>(); + + let new_call_variant_doc = fn_name + .iter() + .map(|fn_name| format!("Create a call with the variant `{}`.", fn_name)) + .collect::>(); + + let mut call_index_warnings = Vec::new(); + // Emit a warning for each call that is missing `call_index` when not in dev-mode. + for method in &methods { + if method.explicit_call_index || def.dev_mode { + continue; + } + + let warning = Warning::new_deprecated("ImplicitCallIndex") .index(call_index_warnings.len()) .old("use implicit call indices") .new("ensure that all calls have a `pallet::call_index` attribute or put the pallet into `dev` mode") @@ -80,373 +91,408 @@ pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream { ]) .span(method.name.span()) .build_or_panic(); - call_index_warnings.push(warning); - } - - let mut fn_weight = Vec::::new(); - let mut weight_warnings = Vec::new(); - for method in &methods { - match &method.weight { - CallWeightDef::DevModeDefault => fn_weight.push(syn::parse_quote!(0)), - CallWeightDef::Immediate(e) => { - weight_constant_warning(e, def.dev_mode, &mut weight_warnings); - weight_witness_warning(method, def.dev_mode, &mut weight_warnings); - - fn_weight.push(e.into_token_stream()); - }, - CallWeightDef::Inherited => { - let pallet_weight = def - .call - .as_ref() - .expect("we have methods; we have calls; qed") - .inherited_call_weight - .as_ref() - .expect("the parser prevents this"); - - // Expand `<::WeightInfo>::call_name()`. - let t = &pallet_weight.typename; - let n = &method.name; - fn_weight.push(quote!({ < #t > :: #n () })); - }, - } - } - debug_assert_eq!(fn_weight.len(), methods.len()); - - let fn_doc = methods.iter().map(|method| &method.docs).collect::>(); - - let args_name = methods - .iter() - .map(|method| method.args.iter().map(|(_, name, _)| name.clone()).collect::>()) - .collect::>(); - - let args_name_stripped = methods - .iter() - .map(|method| { - method - .args - .iter() - .map(|(_, name, _)| { - syn::Ident::new(name.to_string().trim_start_matches('_'), name.span()) - }) - .collect::>() - }) - .collect::>(); - - let make_args_name_pattern = |ref_tok| { - args_name - .iter() - .zip(args_name_stripped.iter()) - .map(|(args_name, args_name_stripped)| { - args_name - .iter() - .zip(args_name_stripped) - .map(|(args_name, args_name_stripped)| { - if args_name == args_name_stripped { - quote::quote!( #ref_tok #args_name ) - } else { - quote::quote!( #args_name_stripped: #ref_tok #args_name ) - } - }) - .collect::>() - }) - .collect::>() - }; - - let args_name_pattern = make_args_name_pattern(None); - let args_name_pattern_ref = make_args_name_pattern(Some(quote::quote!(ref))); - - let args_type = methods - .iter() - .map(|method| method.args.iter().map(|(_, _, type_)| type_.clone()).collect::>()) - .collect::>(); - - let args_compact_attr = methods.iter().map(|method| { - method - .args - .iter() - .map(|(is_compact, _, type_)| { - if *is_compact { - quote::quote_spanned!(type_.span() => #[codec(compact)] ) - } else { - quote::quote!() - } - }) - .collect::>() - }); - - let default_docs = - [syn::parse_quote!(r"Contains a variant per dispatchable extrinsic that this pallet has.")]; - let docs = if docs.is_empty() { &default_docs[..] } else { &docs[..] }; - - let maybe_compile_error = if def.call.is_none() { - quote::quote! { - compile_error!(concat!( - "`", - stringify!($pallet_name), - "` does not have #[pallet::call] defined, perhaps you should remove `Call` from \ - construct_runtime?", - )); - } - } else { - proc_macro2::TokenStream::new() - }; - - let count = COUNTER.with(|counter| counter.borrow_mut().inc()); - let macro_ident = syn::Ident::new(&format!("__is_call_part_defined_{}", count), span); - - let capture_docs = if cfg!(feature = "no-metadata-docs") { "never" } else { "always" }; - - // Wrap all calls inside of storage layers - if let Some(syn::Item::Impl(item_impl)) = def - .call - .as_ref() - .map(|c| &mut def.item.content.as_mut().expect("Checked by def parser").1[c.index]) - { - item_impl.items.iter_mut().for_each(|i| { - if let syn::ImplItem::Fn(method) = i { - let block = &method.block; - method.block = syn::parse_quote! {{ - // We execute all dispatchable in a new storage layer, allowing them - // to return an error at any point, and undoing any storage changes. - #frame_support::storage::with_storage_layer(|| #block) - }}; - } - }); - } - - // Extracts #[allow] attributes, necessary so that we don't run into compiler warnings - let maybe_allow_attrs = methods - .iter() - .map(|method| { - method - .attrs - .iter() - .find(|attr| attr.path().is_ident("allow")) - .map_or(proc_macro2::TokenStream::new(), |attr| attr.to_token_stream()) - }) - .collect::>(); - - let cfg_attrs = methods - .iter() - .map(|method| { - let attrs = - method.cfg_attrs.iter().map(|attr| attr.to_token_stream()).collect::>(); - quote::quote!( #( #attrs )* ) - }) - .collect::>(); - - let feeless_check = methods.iter().map(|method| &method.feeless_check).collect::>(); - let feeless_check_result = - feeless_check.iter().zip(args_name.iter()).map(|(feeless_check, arg_name)| { - if let Some(feeless_check) = feeless_check { - quote::quote!(#feeless_check(origin, #( #arg_name, )*)) - } else { - quote::quote!(false) - } - }); - - quote::quote_spanned!(span => - #[doc(hidden)] - mod warnings { - #( - #call_index_warnings - )* - #( - #weight_warnings - )* - } - - #[allow(unused_imports)] - #[doc(hidden)] - pub mod __substrate_call_check { - #[macro_export] - #[doc(hidden)] - macro_rules! #macro_ident { - ($pallet_name:ident) => { - #maybe_compile_error - }; - } - - #[doc(hidden)] - pub use #macro_ident as is_call_part_defined; - } - - #( #[doc = #docs] )* - #[derive( - #frame_support::RuntimeDebugNoBound, - #frame_support::CloneNoBound, - #frame_support::EqNoBound, - #frame_support::PartialEqNoBound, - #frame_support::__private::codec::Encode, - #frame_support::__private::codec::Decode, - #frame_support::__private::scale_info::TypeInfo, - )] - #[codec(encode_bound())] - #[codec(decode_bound())] - #[scale_info(skip_type_params(#type_use_gen), capture_docs = #capture_docs)] - #[allow(non_camel_case_types)] - pub enum #call_ident<#type_decl_bounded_gen> #where_clause { - #[doc(hidden)] - #[codec(skip)] - __Ignore( - ::core::marker::PhantomData<(#type_use_gen,)>, - #frame_support::Never, - ), - #( - #cfg_attrs - #( #[doc = #fn_doc] )* - #[codec(index = #call_index)] - #fn_name { - #( - #[allow(missing_docs)] - #args_compact_attr #args_name_stripped: #args_type - ),* - }, - )* - } - - impl<#type_impl_gen> #call_ident<#type_use_gen> #where_clause { - #( - #cfg_attrs - #[doc = #new_call_variant_doc] - pub fn #new_call_variant_fn_name( - #( #args_name_stripped: #args_type ),* - ) -> Self { - Self::#fn_name { - #( #args_name_stripped ),* - } - } - )* - } - - impl<#type_impl_gen> #frame_support::dispatch::GetDispatchInfo - for #call_ident<#type_use_gen> - #where_clause - { - fn get_dispatch_info(&self) -> #frame_support::dispatch::DispatchInfo { - match *self { - #( - #cfg_attrs - Self::#fn_name { #( #args_name_pattern_ref, )* } => { - let __pallet_base_weight = #fn_weight; - - let __pallet_weight = < - dyn #frame_support::dispatch::WeighData<( #( & #args_type, )* )> - >::weigh_data(&__pallet_base_weight, ( #( #args_name, )* )); - - let __pallet_class = < - dyn #frame_support::dispatch::ClassifyDispatch< - ( #( & #args_type, )* ) - > - >::classify_dispatch(&__pallet_base_weight, ( #( #args_name, )* )); - - let __pallet_pays_fee = < - dyn #frame_support::dispatch::PaysFee<( #( & #args_type, )* )> - >::pays_fee(&__pallet_base_weight, ( #( #args_name, )* )); - - #frame_support::dispatch::DispatchInfo { - weight: __pallet_weight, - class: __pallet_class, - pays_fee: __pallet_pays_fee, - } - }, - )* - Self::__Ignore(_, _) => unreachable!("__Ignore cannot be used"), - } - } - } - - impl<#type_impl_gen> #frame_support::dispatch::CheckIfFeeless for #call_ident<#type_use_gen> - #where_clause - { - type Origin = #frame_system::pallet_prelude::OriginFor; - #[allow(unused_variables)] - fn is_feeless(&self, origin: &Self::Origin) -> bool { - match *self { - #( - #cfg_attrs - Self::#fn_name { #( #args_name_pattern_ref, )* } => { - #feeless_check_result - }, - )* - Self::__Ignore(_, _) => unreachable!("__Ignore cannot be used"), - } - } - } - - impl<#type_impl_gen> #frame_support::traits::GetCallName for #call_ident<#type_use_gen> - #where_clause - { - fn get_call_name(&self) -> &'static str { - match *self { - #( #cfg_attrs Self::#fn_name { .. } => stringify!(#fn_name), )* - Self::__Ignore(_, _) => unreachable!("__PhantomItem cannot be used."), - } - } - - fn get_call_names() -> &'static [&'static str] { - &[ #( #cfg_attrs stringify!(#fn_name), )* ] - } - } - - impl<#type_impl_gen> #frame_support::traits::GetCallIndex for #call_ident<#type_use_gen> - #where_clause - { - fn get_call_index(&self) -> u8 { - match *self { - #( #cfg_attrs Self::#fn_name { .. } => #call_index, )* - Self::__Ignore(_, _) => unreachable!("__PhantomItem cannot be used."), - } - } - - fn get_call_indices() -> &'static [u8] { - &[ #( #cfg_attrs #call_index, )* ] - } - } - - impl<#type_impl_gen> #frame_support::traits::UnfilteredDispatchable - for #call_ident<#type_use_gen> - #where_clause - { - type RuntimeOrigin = #frame_system::pallet_prelude::OriginFor; - fn dispatch_bypass_filter( - self, - origin: Self::RuntimeOrigin - ) -> #frame_support::dispatch::DispatchResultWithPostInfo { - #frame_support::dispatch_context::run_in_context(|| { - match self { - #( - #cfg_attrs - Self::#fn_name { #( #args_name_pattern, )* } => { - #frame_support::__private::sp_tracing::enter_span!( - #frame_support::__private::sp_tracing::trace_span!(stringify!(#fn_name)) - ); - #maybe_allow_attrs - <#pallet_ident<#type_use_gen>>::#fn_name(origin, #( #args_name, )* ) - .map(Into::into).map_err(Into::into) - }, - )* - Self::__Ignore(_, _) => { - let _ = origin; // Use origin for empty Call enum - unreachable!("__PhantomItem cannot be used."); - }, - } - }) - } - } - - impl<#type_impl_gen> #frame_support::dispatch::Callable for #pallet_ident<#type_use_gen> - #where_clause - { - type RuntimeCall = #call_ident<#type_use_gen>; - } - - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #where_clause { - #[allow(dead_code)] - #[doc(hidden)] - pub fn call_functions() -> #frame_support::__private::metadata_ir::PalletCallMetadataIR { - #frame_support::__private::scale_info::meta_type::<#call_ident<#type_use_gen>>().into() - } - } - ) + call_index_warnings.push(warning); + } + + let mut fn_weight = Vec::::new(); + let mut weight_warnings = Vec::new(); + for method in &methods { + match &method.weight { + CallWeightDef::DevModeDefault => fn_weight.push(syn::parse_quote!(0)), + CallWeightDef::Immediate(e) => { + weight_constant_warning(e, def.dev_mode, &mut weight_warnings); + weight_witness_warning(method, def.dev_mode, &mut weight_warnings); + + fn_weight.push(e.into_token_stream()); + } + CallWeightDef::Inherited => { + let pallet_weight = def + .call + .as_ref() + .expect("we have methods; we have calls; qed") + .inherited_call_weight + .as_ref() + .expect("the parser prevents this"); + + // Expand `<::WeightInfo>::call_name()`. + let t = &pallet_weight.typename; + let n = &method.name; + fn_weight.push(quote!({ < #t > :: #n () })); + } + } + } + debug_assert_eq!(fn_weight.len(), methods.len()); + + let fn_doc = methods + .iter() + .map(|method| &method.docs) + .collect::>(); + + let args_name = methods + .iter() + .map(|method| { + method + .args + .iter() + .map(|(_, name, _)| name.clone()) + .collect::>() + }) + .collect::>(); + + let args_name_stripped = methods + .iter() + .map(|method| { + method + .args + .iter() + .map(|(_, name, _)| { + syn::Ident::new(name.to_string().trim_start_matches('_'), name.span()) + }) + .collect::>() + }) + .collect::>(); + + let make_args_name_pattern = |ref_tok| { + args_name + .iter() + .zip(args_name_stripped.iter()) + .map(|(args_name, args_name_stripped)| { + args_name + .iter() + .zip(args_name_stripped) + .map(|(args_name, args_name_stripped)| { + if args_name == args_name_stripped { + quote::quote!( #ref_tok #args_name ) + } else { + quote::quote!( #args_name_stripped: #ref_tok #args_name ) + } + }) + .collect::>() + }) + .collect::>() + }; + + let args_name_pattern = make_args_name_pattern(None); + let args_name_pattern_ref = make_args_name_pattern(Some(quote::quote!(ref))); + + let args_type = methods + .iter() + .map(|method| { + method + .args + .iter() + .map(|(_, _, type_)| type_.clone()) + .collect::>() + }) + .collect::>(); + + let args_compact_attr = methods.iter().map(|method| { + method + .args + .iter() + .map(|(is_compact, _, type_)| { + if *is_compact { + quote::quote_spanned!(type_.span() => #[codec(compact)] ) + } else { + quote::quote!() + } + }) + .collect::>() + }); + + let default_docs = [syn::parse_quote!( + r"Contains a variant per dispatchable extrinsic that this pallet has." + )]; + let docs = if docs.is_empty() { + &default_docs[..] + } else { + &docs[..] + }; + + let maybe_compile_error = if def.call.is_none() { + quote::quote! { + compile_error!(concat!( + "`", + stringify!($pallet_name), + "` does not have #[pallet::call] defined, perhaps you should remove `Call` from \ + construct_runtime?", + )); + } + } else { + proc_macro2::TokenStream::new() + }; + + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + let macro_ident = syn::Ident::new(&format!("__is_call_part_defined_{}", count), span); + + let capture_docs = if cfg!(feature = "no-metadata-docs") { + "never" + } else { + "always" + }; + + // Wrap all calls inside of storage layers + if let Some(syn::Item::Impl(item_impl)) = def + .call + .as_ref() + .map(|c| &mut def.item.content.as_mut().expect("Checked by def parser").1[c.index]) + { + item_impl.items.iter_mut().for_each(|i| { + if let syn::ImplItem::Fn(method) = i { + let block = &method.block; + method.block = syn::parse_quote! {{ + // We execute all dispatchable in a new storage layer, allowing them + // to return an error at any point, and undoing any storage changes. + #frame_support::storage::with_storage_layer(|| #block) + }}; + } + }); + } + + // Extracts #[allow] attributes, necessary so that we don't run into compiler warnings + let maybe_allow_attrs = methods + .iter() + .map(|method| { + method + .attrs + .iter() + .find(|attr| attr.path().is_ident("allow")) + .map_or(proc_macro2::TokenStream::new(), |attr| { + attr.to_token_stream() + }) + }) + .collect::>(); + + let cfg_attrs = methods + .iter() + .map(|method| { + let attrs = method + .cfg_attrs + .iter() + .map(|attr| attr.to_token_stream()) + .collect::>(); + quote::quote!( #( #attrs )* ) + }) + .collect::>(); + + let feeless_check = methods + .iter() + .map(|method| &method.feeless_check) + .collect::>(); + let feeless_check_result = + feeless_check + .iter() + .zip(args_name.iter()) + .map(|(feeless_check, arg_name)| { + if let Some(feeless_check) = feeless_check { + quote::quote!(#feeless_check(origin, #( #arg_name, )*)) + } else { + quote::quote!(false) + } + }); + + quote::quote_spanned!(span => + #[doc(hidden)] + mod warnings { + #( + #call_index_warnings + )* + #( + #weight_warnings + )* + } + + #[allow(unused_imports)] + #[doc(hidden)] + pub mod __substrate_call_check { + #[macro_export] + #[doc(hidden)] + macro_rules! #macro_ident { + ($pallet_name:ident) => { + #maybe_compile_error + }; + } + + #[doc(hidden)] + pub use #macro_ident as is_call_part_defined; + } + + #( #[doc = #docs] )* + #[derive( + #frame_support::RuntimeDebugNoBound, + #frame_support::CloneNoBound, + #frame_support::EqNoBound, + #frame_support::PartialEqNoBound, + #frame_support::__private::codec::Encode, + #frame_support::__private::codec::Decode, + #frame_support::__private::scale_info::TypeInfo, + )] + #[codec(encode_bound())] + #[codec(decode_bound())] + #[scale_info(skip_type_params(#type_use_gen), capture_docs = #capture_docs)] + #[allow(non_camel_case_types)] + pub enum #call_ident<#type_decl_bounded_gen> #where_clause { + #[doc(hidden)] + #[codec(skip)] + __Ignore( + ::core::marker::PhantomData<(#type_use_gen,)>, + #frame_support::Never, + ), + #( + #cfg_attrs + #( #[doc = #fn_doc] )* + #[codec(index = #call_index)] + #fn_name { + #( + #[allow(missing_docs)] + #args_compact_attr #args_name_stripped: #args_type + ),* + }, + )* + } + + impl<#type_impl_gen> #call_ident<#type_use_gen> #where_clause { + #( + #cfg_attrs + #[doc = #new_call_variant_doc] + pub fn #new_call_variant_fn_name( + #( #args_name_stripped: #args_type ),* + ) -> Self { + Self::#fn_name { + #( #args_name_stripped ),* + } + } + )* + } + + impl<#type_impl_gen> #frame_support::dispatch::GetDispatchInfo + for #call_ident<#type_use_gen> + #where_clause + { + fn get_dispatch_info(&self) -> #frame_support::dispatch::DispatchInfo { + match *self { + #( + #cfg_attrs + Self::#fn_name { #( #args_name_pattern_ref, )* } => { + let __pallet_base_weight = #fn_weight; + + let __pallet_weight = < + dyn #frame_support::dispatch::WeighData<( #( & #args_type, )* )> + >::weigh_data(&__pallet_base_weight, ( #( #args_name, )* )); + + let __pallet_class = < + dyn #frame_support::dispatch::ClassifyDispatch< + ( #( & #args_type, )* ) + > + >::classify_dispatch(&__pallet_base_weight, ( #( #args_name, )* )); + + let __pallet_pays_fee = < + dyn #frame_support::dispatch::PaysFee<( #( & #args_type, )* )> + >::pays_fee(&__pallet_base_weight, ( #( #args_name, )* )); + + #frame_support::dispatch::DispatchInfo { + weight: __pallet_weight, + class: __pallet_class, + pays_fee: __pallet_pays_fee, + } + }, + )* + Self::__Ignore(_, _) => unreachable!("__Ignore cannot be used"), + } + } + } + + impl<#type_impl_gen> #frame_support::dispatch::CheckIfFeeless for #call_ident<#type_use_gen> + #where_clause + { + type Origin = #frame_system::pallet_prelude::OriginFor; + #[allow(unused_variables)] + fn is_feeless(&self, origin: &Self::Origin) -> bool { + match *self { + #( + #cfg_attrs + Self::#fn_name { #( #args_name_pattern_ref, )* } => { + #feeless_check_result + }, + )* + Self::__Ignore(_, _) => unreachable!("__Ignore cannot be used"), + } + } + } + + impl<#type_impl_gen> #frame_support::traits::GetCallName for #call_ident<#type_use_gen> + #where_clause + { + fn get_call_name(&self) -> &'static str { + match *self { + #( #cfg_attrs Self::#fn_name { .. } => stringify!(#fn_name), )* + Self::__Ignore(_, _) => unreachable!("__PhantomItem cannot be used."), + } + } + + fn get_call_names() -> &'static [&'static str] { + &[ #( #cfg_attrs stringify!(#fn_name), )* ] + } + } + + impl<#type_impl_gen> #frame_support::traits::GetCallIndex for #call_ident<#type_use_gen> + #where_clause + { + fn get_call_index(&self) -> u8 { + match *self { + #( #cfg_attrs Self::#fn_name { .. } => #call_index, )* + Self::__Ignore(_, _) => unreachable!("__PhantomItem cannot be used."), + } + } + + fn get_call_indices() -> &'static [u8] { + &[ #( #cfg_attrs #call_index, )* ] + } + } + + impl<#type_impl_gen> #frame_support::traits::UnfilteredDispatchable + for #call_ident<#type_use_gen> + #where_clause + { + type RuntimeOrigin = #frame_system::pallet_prelude::OriginFor; + fn dispatch_bypass_filter( + self, + origin: Self::RuntimeOrigin + ) -> #frame_support::dispatch::DispatchResultWithPostInfo { + #frame_support::dispatch_context::run_in_context(|| { + match self { + #( + #cfg_attrs + Self::#fn_name { #( #args_name_pattern, )* } => { + #frame_support::__private::sp_tracing::enter_span!( + #frame_support::__private::sp_tracing::trace_span!(stringify!(#fn_name)) + ); + #maybe_allow_attrs + <#pallet_ident<#type_use_gen>>::#fn_name(origin, #( #args_name, )* ) + .map(Into::into).map_err(Into::into) + }, + )* + Self::__Ignore(_, _) => { + let _ = origin; // Use origin for empty Call enum + unreachable!("__PhantomItem cannot be used."); + }, + } + }) + } + } + + impl<#type_impl_gen> #frame_support::dispatch::Callable for #pallet_ident<#type_use_gen> + #where_clause + { + type RuntimeCall = #call_ident<#type_use_gen>; + } + + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #where_clause { + #[allow(dead_code)] + #[doc(hidden)] + pub fn call_functions() -> #frame_support::__private::metadata_ir::PalletCallMetadataIR { + #frame_support::__private::scale_info::meta_type::<#call_ident<#type_use_gen>>().into() + } + } + ) } diff --git a/support/procedural-fork/src/pallet/expand/composite.rs b/support/procedural-fork/src/pallet/expand/composite.rs index d449afe8f..49c0ad675 100644 --- a/support/procedural-fork/src/pallet/expand/composite.rs +++ b/support/procedural-fork/src/pallet/expand/composite.rs @@ -20,21 +20,21 @@ use proc_macro2::TokenStream; /// Expands `composite_enum` and adds the `VariantCount` implementation for it. pub fn expand_composites(def: &mut Def) -> TokenStream { - let mut expand = quote::quote!(); - let frame_support = &def.frame_support; + let mut expand = quote::quote!(); + let frame_support = &def.frame_support; - for composite in &def.composites { - let name = &composite.ident; - let (impl_generics, ty_generics, where_clause) = composite.generics.split_for_impl(); - let variants_count = composite.variant_count; + for composite in &def.composites { + let name = &composite.ident; + let (impl_generics, ty_generics, where_clause) = composite.generics.split_for_impl(); + let variants_count = composite.variant_count; - // add `VariantCount` implementation for `composite_enum` - expand.extend(quote::quote_spanned!(composite.attr_span => + // add `VariantCount` implementation for `composite_enum` + expand.extend(quote::quote_spanned!(composite.attr_span => impl #impl_generics #frame_support::traits::VariantCount for #name #ty_generics #where_clause { const VARIANT_COUNT: u32 = #variants_count; } )); - } + } - expand + expand } diff --git a/support/procedural-fork/src/pallet/expand/config.rs b/support/procedural-fork/src/pallet/expand/config.rs index 5cf4035a8..836c74ae7 100644 --- a/support/procedural-fork/src/pallet/expand/config.rs +++ b/support/procedural-fork/src/pallet/expand/config.rs @@ -23,20 +23,20 @@ use syn::{parse_quote, Item}; /// /// * Generate default rust doc pub fn expand_config(def: &mut Def) -> TokenStream { - let config = &def.config; - let config_item = { - let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[config.index]; - if let Item::Trait(item) = item { - item - } else { - unreachable!("Checked by config parser") - } - }; + let config = &def.config; + let config_item = { + let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[config.index]; + if let Item::Trait(item) = item { + item + } else { + unreachable!("Checked by config parser") + } + }; - config_item.attrs.insert( - 0, - parse_quote!( - #[doc = r" + config_item.attrs.insert( + 0, + parse_quote!( + #[doc = r" Configuration trait of this pallet. The main purpose of this trait is to act as an interface between this pallet and the runtime in @@ -44,54 +44,54 @@ which it is embedded in. A type, function, or constant in this trait is essentia configured by the runtime that includes this pallet. Consequently, a runtime that wants to include this pallet must implement this trait." - ] - ), - ); + ] + ), + ); - // we only emit `DefaultConfig` if there are trait items, so an empty `DefaultConfig` is - // impossible consequently. - match &config.default_sub_trait { - Some(default_sub_trait) if default_sub_trait.items.len() > 0 => { - let trait_items = &default_sub_trait - .items - .iter() - .map(|item| { - if item.1 { - if let syn::TraitItem::Type(item) = item.0.clone() { - let mut item = item.clone(); - item.bounds.clear(); - syn::TraitItem::Type(item) - } else { - item.0.clone() - } - } else { - item.0.clone() - } - }) - .collect::>(); + // we only emit `DefaultConfig` if there are trait items, so an empty `DefaultConfig` is + // impossible consequently. + match &config.default_sub_trait { + Some(default_sub_trait) if default_sub_trait.items.len() > 0 => { + let trait_items = &default_sub_trait + .items + .iter() + .map(|item| { + if item.1 { + if let syn::TraitItem::Type(item) = item.0.clone() { + let mut item = item.clone(); + item.bounds.clear(); + syn::TraitItem::Type(item) + } else { + item.0.clone() + } + } else { + item.0.clone() + } + }) + .collect::>(); - let type_param_bounds = if default_sub_trait.has_system { - let system = &def.frame_system; - quote::quote!(: #system::DefaultConfig) - } else { - quote::quote!() - }; + let type_param_bounds = if default_sub_trait.has_system { + let system = &def.frame_system; + quote::quote!(: #system::DefaultConfig) + } else { + quote::quote!() + }; - quote!( - /// Based on [`Config`]. Auto-generated by - /// [`#[pallet::config(with_default)]`](`frame_support::pallet_macros::config`). - /// Can be used in tandem with - /// [`#[register_default_config]`](`frame_support::register_default_config`) and - /// [`#[derive_impl]`](`frame_support::derive_impl`) to derive test config traits - /// based on existing pallet config traits in a safe and developer-friendly way. - /// - /// See [here](`frame_support::pallet_macros::config`) for more information and caveats about - /// the auto-generated `DefaultConfig` trait and how it is generated. - pub trait DefaultConfig #type_param_bounds { - #(#trait_items)* - } - ) - }, - _ => Default::default(), - } + quote!( + /// Based on [`Config`]. Auto-generated by + /// [`#[pallet::config(with_default)]`](`frame_support::pallet_macros::config`). + /// Can be used in tandem with + /// [`#[register_default_config]`](`frame_support::register_default_config`) and + /// [`#[derive_impl]`](`frame_support::derive_impl`) to derive test config traits + /// based on existing pallet config traits in a safe and developer-friendly way. + /// + /// See [here](`frame_support::pallet_macros::config`) for more information and caveats about + /// the auto-generated `DefaultConfig` trait and how it is generated. + pub trait DefaultConfig #type_param_bounds { + #(#trait_items)* + } + ) + } + _ => Default::default(), + } } diff --git a/support/procedural-fork/src/pallet/expand/constants.rs b/support/procedural-fork/src/pallet/expand/constants.rs index 57fa8b7f3..5153ccf49 100644 --- a/support/procedural-fork/src/pallet/expand/constants.rs +++ b/support/procedural-fork/src/pallet/expand/constants.rs @@ -18,91 +18,99 @@ use crate::pallet::Def; struct ConstDef { - /// Name of the associated type. - pub ident: syn::Ident, - /// The type in Get, e.g. `u32` in `type Foo: Get;`, but `Self` is replaced by `T` - pub type_: syn::Type, - /// The doc associated - pub doc: Vec, - /// default_byte implementation - pub default_byte_impl: proc_macro2::TokenStream, - /// Constant name for Metadata (optional) - pub metadata_name: Option, + /// Name of the associated type. + pub ident: syn::Ident, + /// The type in Get, e.g. `u32` in `type Foo: Get;`, but `Self` is replaced by `T` + pub type_: syn::Type, + /// The doc associated + pub doc: Vec, + /// default_byte implementation + pub default_byte_impl: proc_macro2::TokenStream, + /// Constant name for Metadata (optional) + pub metadata_name: Option, } /// /// * Impl fn module_constant_metadata for pallet. pub fn expand_constants(def: &mut Def) -> proc_macro2::TokenStream { - let frame_support = &def.frame_support; - let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site()); - let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site()); - let pallet_ident = &def.pallet_struct.pallet; - let trait_use_gen = &def.trait_use_generics(proc_macro2::Span::call_site()); - - let mut where_clauses = vec![&def.config.where_clause]; - where_clauses.extend(def.extra_constants.iter().map(|d| &d.where_clause)); - let completed_where_clause = super::merge_where_clauses(&where_clauses); - - let config_consts = def.config.consts_metadata.iter().map(|const_| { - let ident = &const_.ident; - let const_type = &const_.type_; - - ConstDef { - ident: const_.ident.clone(), - type_: const_.type_.clone(), - doc: const_.doc.clone(), - default_byte_impl: quote::quote!( - let value = <::#ident as - #frame_support::traits::Get<#const_type>>::get(); - #frame_support::__private::codec::Encode::encode(&value) - ), - metadata_name: None, - } - }); - - let extra_consts = def.extra_constants.iter().flat_map(|d| &d.extra_constants).map(|const_| { - let ident = &const_.ident; - - ConstDef { - ident: const_.ident.clone(), - type_: const_.type_.clone(), - doc: const_.doc.clone(), - default_byte_impl: quote::quote!( - let value = >::#ident(); - #frame_support::__private::codec::Encode::encode(&value) - ), - metadata_name: const_.metadata_name.clone(), - } - }); - - let consts = config_consts.chain(extra_consts).map(|const_| { - let const_type = &const_.type_; - let ident_str = format!("{}", const_.metadata_name.unwrap_or(const_.ident)); - - let no_docs = vec![]; - let doc = if cfg!(feature = "no-metadata-docs") { &no_docs } else { &const_.doc }; - - let default_byte_impl = &const_.default_byte_impl; - - quote::quote!({ - #frame_support::__private::metadata_ir::PalletConstantMetadataIR { - name: #ident_str, - ty: #frame_support::__private::scale_info::meta_type::<#const_type>(), - value: { #default_byte_impl }, - docs: #frame_support::__private::sp_std::vec![ #( #doc ),* ], - } - }) - }); - - quote::quote!( - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause{ - - #[doc(hidden)] - pub fn pallet_constants_metadata() - -> #frame_support::__private::sp_std::vec::Vec<#frame_support::__private::metadata_ir::PalletConstantMetadataIR> - { - #frame_support::__private::sp_std::vec![ #( #consts ),* ] - } - } - ) + let frame_support = &def.frame_support; + let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site()); + let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site()); + let pallet_ident = &def.pallet_struct.pallet; + let trait_use_gen = &def.trait_use_generics(proc_macro2::Span::call_site()); + + let mut where_clauses = vec![&def.config.where_clause]; + where_clauses.extend(def.extra_constants.iter().map(|d| &d.where_clause)); + let completed_where_clause = super::merge_where_clauses(&where_clauses); + + let config_consts = def.config.consts_metadata.iter().map(|const_| { + let ident = &const_.ident; + let const_type = &const_.type_; + + ConstDef { + ident: const_.ident.clone(), + type_: const_.type_.clone(), + doc: const_.doc.clone(), + default_byte_impl: quote::quote!( + let value = <::#ident as + #frame_support::traits::Get<#const_type>>::get(); + #frame_support::__private::codec::Encode::encode(&value) + ), + metadata_name: None, + } + }); + + let extra_consts = def + .extra_constants + .iter() + .flat_map(|d| &d.extra_constants) + .map(|const_| { + let ident = &const_.ident; + + ConstDef { + ident: const_.ident.clone(), + type_: const_.type_.clone(), + doc: const_.doc.clone(), + default_byte_impl: quote::quote!( + let value = >::#ident(); + #frame_support::__private::codec::Encode::encode(&value) + ), + metadata_name: const_.metadata_name.clone(), + } + }); + + let consts = config_consts.chain(extra_consts).map(|const_| { + let const_type = &const_.type_; + let ident_str = format!("{}", const_.metadata_name.unwrap_or(const_.ident)); + + let no_docs = vec![]; + let doc = if cfg!(feature = "no-metadata-docs") { + &no_docs + } else { + &const_.doc + }; + + let default_byte_impl = &const_.default_byte_impl; + + quote::quote!({ + #frame_support::__private::metadata_ir::PalletConstantMetadataIR { + name: #ident_str, + ty: #frame_support::__private::scale_info::meta_type::<#const_type>(), + value: { #default_byte_impl }, + docs: #frame_support::__private::sp_std::vec![ #( #doc ),* ], + } + }) + }); + + quote::quote!( + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause{ + + #[doc(hidden)] + pub fn pallet_constants_metadata() + -> #frame_support::__private::sp_std::vec::Vec<#frame_support::__private::metadata_ir::PalletConstantMetadataIR> + { + #frame_support::__private::sp_std::vec![ #( #consts ),* ] + } + } + ) } diff --git a/support/procedural-fork/src/pallet/expand/doc_only.rs b/support/procedural-fork/src/pallet/expand/doc_only.rs index 621a051ac..3e60e9a9b 100644 --- a/support/procedural-fork/src/pallet/expand/doc_only.rs +++ b/support/procedural-fork/src/pallet/expand/doc_only.rs @@ -20,84 +20,84 @@ use proc_macro2::Span; use crate::pallet::Def; pub fn expand_doc_only(def: &mut Def) -> proc_macro2::TokenStream { - let dispatchables = if let Some(call_def) = &def.call { - let type_impl_generics = def.type_impl_generics(Span::call_site()); - call_def - .methods - .iter() - .map(|method| { - let name = &method.name; - let args = &method - .args - .iter() - .map(|(_, arg_name, arg_type)| quote::quote!( #arg_name: #arg_type, )) - .collect::(); - let docs = &method.docs; + let dispatchables = if let Some(call_def) = &def.call { + let type_impl_generics = def.type_impl_generics(Span::call_site()); + call_def + .methods + .iter() + .map(|method| { + let name = &method.name; + let args = &method + .args + .iter() + .map(|(_, arg_name, arg_type)| quote::quote!( #arg_name: #arg_type, )) + .collect::(); + let docs = &method.docs; - let real = format!(" [`Pallet::{}`].", name); - quote::quote!( - #( #[doc = #docs] )* - /// - /// # Warning: Doc-Only - /// - /// This function is an automatically generated, and is doc-only, uncallable - /// stub. See the real version in - #[ doc = #real ] - pub fn #name<#type_impl_generics>(#args) { unreachable!(); } - ) - }) - .collect::() - } else { - quote::quote!() - }; + let real = format!(" [`Pallet::{}`].", name); + quote::quote!( + #( #[doc = #docs] )* + /// + /// # Warning: Doc-Only + /// + /// This function is an automatically generated, and is doc-only, uncallable + /// stub. See the real version in + #[ doc = #real ] + pub fn #name<#type_impl_generics>(#args) { unreachable!(); } + ) + }) + .collect::() + } else { + quote::quote!() + }; - let storage_types = def - .storages - .iter() - .map(|storage| { - let storage_name = &storage.ident; - let storage_type_docs = &storage.docs; - let real = format!("[`pallet::{}`].", storage_name); - quote::quote!( - #( #[doc = #storage_type_docs] )* - /// - /// # Warning: Doc-Only - /// - /// This type is automatically generated, and is doc-only. See the real version in - #[ doc = #real ] - pub struct #storage_name(); - ) - }) - .collect::(); + let storage_types = def + .storages + .iter() + .map(|storage| { + let storage_name = &storage.ident; + let storage_type_docs = &storage.docs; + let real = format!("[`pallet::{}`].", storage_name); + quote::quote!( + #( #[doc = #storage_type_docs] )* + /// + /// # Warning: Doc-Only + /// + /// This type is automatically generated, and is doc-only. See the real version in + #[ doc = #real ] + pub struct #storage_name(); + ) + }) + .collect::(); - quote::quote!( - /// Auto-generated docs-only module listing all (public and private) defined storage types - /// for this pallet. - /// - /// # Warning: Doc-Only - /// - /// Members of this module cannot be used directly and are only provided for documentation - /// purposes. - /// - /// To see the actual storage type, find a struct with the same name at the root of the - /// pallet, in the list of [*Type Definitions*](../index.html#types). - #[cfg(doc)] - pub mod storage_types { - use super::*; - #storage_types - } + quote::quote!( + /// Auto-generated docs-only module listing all (public and private) defined storage types + /// for this pallet. + /// + /// # Warning: Doc-Only + /// + /// Members of this module cannot be used directly and are only provided for documentation + /// purposes. + /// + /// To see the actual storage type, find a struct with the same name at the root of the + /// pallet, in the list of [*Type Definitions*](../index.html#types). + #[cfg(doc)] + pub mod storage_types { + use super::*; + #storage_types + } - /// Auto-generated docs-only module listing all defined dispatchables for this pallet. - /// - /// # Warning: Doc-Only - /// - /// Members of this module cannot be used directly and are only provided for documentation - /// purposes. To see the real version of each dispatchable, look for them in [`Pallet`] or - /// [`Call`]. - #[cfg(doc)] - pub mod dispatchables { - use super::*; - #dispatchables - } - ) + /// Auto-generated docs-only module listing all defined dispatchables for this pallet. + /// + /// # Warning: Doc-Only + /// + /// Members of this module cannot be used directly and are only provided for documentation + /// purposes. To see the real version of each dispatchable, look for them in [`Pallet`] or + /// [`Call`]. + #[cfg(doc)] + pub mod dispatchables { + use super::*; + #dispatchables + } + ) } diff --git a/support/procedural-fork/src/pallet/expand/documentation.rs b/support/procedural-fork/src/pallet/expand/documentation.rs index ec19f889a..adc4f7ce9 100644 --- a/support/procedural-fork/src/pallet/expand/documentation.rs +++ b/support/procedural-fork/src/pallet/expand/documentation.rs @@ -28,12 +28,12 @@ const PALLET_DOC: &'static str = "pallet_doc"; /// Supported format: /// `#[pallet_doc(PATH)]`: The path of the file from which the documentation is loaded fn parse_pallet_doc_value(attr: &Attribute) -> syn::Result { - let lit: syn::LitStr = attr.parse_args().map_err(|_| { + let lit: syn::LitStr = attr.parse_args().map_err(|_| { let msg = "The `pallet_doc` received an unsupported argument. Supported format: `pallet_doc(\"PATH\")`"; syn::Error::new(attr.span(), msg) })?; - Ok(DocMetaValue::Path(lit)) + Ok(DocMetaValue::Path(lit)) } /// Get the value from the `doc` comment attribute: @@ -42,46 +42,49 @@ fn parse_pallet_doc_value(attr: &Attribute) -> syn::Result { /// - `#[doc = "A doc string"]`: Documentation as a string literal /// - `#[doc = include_str!(PATH)]`: Documentation obtained from a path fn parse_doc_value(attr: &Attribute) -> syn::Result> { - if !attr.path().is_ident(DOC) { - return Ok(None) - } - - let meta = attr.meta.require_name_value()?; - - match &meta.value { - syn::Expr::Lit(lit) => Ok(Some(DocMetaValue::Lit(lit.lit.clone()))), - syn::Expr::Macro(mac) if mac.mac.path.is_ident("include_str") => - Ok(Some(DocMetaValue::Path(mac.mac.parse_body()?))), - _ => - Err(syn::Error::new(attr.span(), "Expected `= \"docs\"` or `= include_str!(\"PATH\")`")), - } + if !attr.path().is_ident(DOC) { + return Ok(None); + } + + let meta = attr.meta.require_name_value()?; + + match &meta.value { + syn::Expr::Lit(lit) => Ok(Some(DocMetaValue::Lit(lit.lit.clone()))), + syn::Expr::Macro(mac) if mac.mac.path.is_ident("include_str") => { + Ok(Some(DocMetaValue::Path(mac.mac.parse_body()?))) + } + _ => Err(syn::Error::new( + attr.span(), + "Expected `= \"docs\"` or `= include_str!(\"PATH\")`", + )), + } } /// Supported documentation tokens. #[derive(Debug)] enum DocMetaValue { - /// Documentation with string literals. - /// - /// `#[doc = "Lit"]` - Lit(Lit), - /// Documentation with `include_str!` macro. - /// - /// The string literal represents the file `PATH`. - /// - /// `#[doc = include_str!(PATH)]` - Path(LitStr), + /// Documentation with string literals. + /// + /// `#[doc = "Lit"]` + Lit(Lit), + /// Documentation with `include_str!` macro. + /// + /// The string literal represents the file `PATH`. + /// + /// `#[doc = include_str!(PATH)]` + Path(LitStr), } impl ToTokens for DocMetaValue { - fn to_tokens(&self, tokens: &mut TokenStream) { - match self { - DocMetaValue::Lit(lit) => lit.to_tokens(tokens), - DocMetaValue::Path(path_lit) => { - let decl = quote::quote!(include_str!(#path_lit)); - tokens.extend(decl) - }, - } - } + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + DocMetaValue::Lit(lit) => lit.to_tokens(tokens), + DocMetaValue::Path(path_lit) => { + let decl = quote::quote!(include_str!(#path_lit)); + tokens.extend(decl) + } + } + } } /// Extract the documentation from the given pallet definition @@ -110,63 +113,63 @@ impl ToTokens for DocMetaValue { /// Unlike the `doc` attribute, the documentation provided to the `proc_macro` attribute is /// not added to the pallet. pub fn expand_documentation(def: &mut Def) -> proc_macro2::TokenStream { - let frame_support = &def.frame_support; - let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site()); - let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site()); - let pallet_ident = &def.pallet_struct.pallet; - let where_clauses = &def.config.where_clause; - - // TODO: Use [drain_filter](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.drain_filter) when it is stable. - - // The `pallet_doc` attributes are excluded from the generation of the pallet, - // but they are included in the runtime metadata. - let mut pallet_docs = Vec::with_capacity(def.item.attrs.len()); - let mut index = 0; - while index < def.item.attrs.len() { - let attr = &def.item.attrs[index]; - if attr.path().get_ident().map_or(false, |i| *i == PALLET_DOC) { - pallet_docs.push(def.item.attrs.remove(index)); - // Do not increment the index, we have just removed the - // element from the attributes. - continue - } - - index += 1; - } - - // Capture the `#[doc = include_str!("../README.md")]` and `#[doc = "Documentation"]`. - let docs = match def - .item - .attrs - .iter() - .filter_map(|v| parse_doc_value(v).transpose()) - .collect::>>() - { - Ok(r) => r, - Err(err) => return err.into_compile_error(), - }; - - // Capture the `#[pallet_doc("../README.md")]`. - let pallet_docs = match pallet_docs - .into_iter() - .map(|attr| parse_pallet_doc_value(&attr)) - .collect::>>() - { - Ok(docs) => docs, - Err(err) => return err.into_compile_error(), - }; - - let docs = docs.iter().chain(pallet_docs.iter()); - - quote::quote!( - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #where_clauses{ - - #[doc(hidden)] - pub fn pallet_documentation_metadata() - -> #frame_support::__private::sp_std::vec::Vec<&'static str> - { - #frame_support::__private::sp_std::vec![ #( #docs ),* ] - } - } - ) + let frame_support = &def.frame_support; + let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site()); + let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site()); + let pallet_ident = &def.pallet_struct.pallet; + let where_clauses = &def.config.where_clause; + + // TODO: Use [drain_filter](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.drain_filter) when it is stable. + + // The `pallet_doc` attributes are excluded from the generation of the pallet, + // but they are included in the runtime metadata. + let mut pallet_docs = Vec::with_capacity(def.item.attrs.len()); + let mut index = 0; + while index < def.item.attrs.len() { + let attr = &def.item.attrs[index]; + if attr.path().get_ident().map_or(false, |i| *i == PALLET_DOC) { + pallet_docs.push(def.item.attrs.remove(index)); + // Do not increment the index, we have just removed the + // element from the attributes. + continue; + } + + index += 1; + } + + // Capture the `#[doc = include_str!("../README.md")]` and `#[doc = "Documentation"]`. + let docs = match def + .item + .attrs + .iter() + .filter_map(|v| parse_doc_value(v).transpose()) + .collect::>>() + { + Ok(r) => r, + Err(err) => return err.into_compile_error(), + }; + + // Capture the `#[pallet_doc("../README.md")]`. + let pallet_docs = match pallet_docs + .into_iter() + .map(|attr| parse_pallet_doc_value(&attr)) + .collect::>>() + { + Ok(docs) => docs, + Err(err) => return err.into_compile_error(), + }; + + let docs = docs.iter().chain(pallet_docs.iter()); + + quote::quote!( + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #where_clauses{ + + #[doc(hidden)] + pub fn pallet_documentation_metadata() + -> #frame_support::__private::sp_std::vec::Vec<&'static str> + { + #frame_support::__private::sp_std::vec![ #( #docs ),* ] + } + } + ) } diff --git a/support/procedural-fork/src/pallet/expand/error.rs b/support/procedural-fork/src/pallet/expand/error.rs index 72fb6e923..e2c3f680c 100644 --- a/support/procedural-fork/src/pallet/expand/error.rs +++ b/support/procedural-fork/src/pallet/expand/error.rs @@ -16,11 +16,11 @@ // limitations under the License. use crate::{ - pallet::{ - parse::error::{VariantDef, VariantField}, - Def, - }, - COUNTER, + pallet::{ + parse::error::{VariantDef, VariantField}, + Def, + }, + COUNTER, }; use frame_support_procedural_tools::get_doc_literals; use quote::ToTokens; @@ -29,49 +29,49 @@ use syn::spanned::Spanned; /// /// * impl various trait on Error pub fn expand_error(def: &mut Def) -> proc_macro2::TokenStream { - let count = COUNTER.with(|counter| counter.borrow_mut().inc()); - let error_token_unique_id = - syn::Ident::new(&format!("__tt_error_token_{}", count), def.item.span()); - - let frame_support = &def.frame_support; - let frame_system = &def.frame_system; - let config_where_clause = &def.config.where_clause; - - let error = if let Some(error) = &def.error { - error - } else { - return quote::quote! { - #[macro_export] - #[doc(hidden)] - macro_rules! #error_token_unique_id { - { - $caller:tt - your_tt_return = [{ $my_tt_return:path }] - } => { - $my_tt_return! { - $caller - } - }; - } - - pub use #error_token_unique_id as tt_error_token; - } - }; - - let error_ident = &error.error; - let type_impl_gen = &def.type_impl_generics(error.attr_span); - let type_use_gen = &def.type_use_generics(error.attr_span); - - let phantom_variant: syn::Variant = syn::parse_quote!( - #[doc(hidden)] - #[codec(skip)] - __Ignore( - #frame_support::__private::sp_std::marker::PhantomData<(#type_use_gen)>, - #frame_support::Never, - ) - ); - - let as_str_matches = error.variants.iter().map( + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + let error_token_unique_id = + syn::Ident::new(&format!("__tt_error_token_{}", count), def.item.span()); + + let frame_support = &def.frame_support; + let frame_system = &def.frame_system; + let config_where_clause = &def.config.where_clause; + + let error = if let Some(error) = &def.error { + error + } else { + return quote::quote! { + #[macro_export] + #[doc(hidden)] + macro_rules! #error_token_unique_id { + { + $caller:tt + your_tt_return = [{ $my_tt_return:path }] + } => { + $my_tt_return! { + $caller + } + }; + } + + pub use #error_token_unique_id as tt_error_token; + }; + }; + + let error_ident = &error.error; + let type_impl_gen = &def.type_impl_generics(error.attr_span); + let type_use_gen = &def.type_use_generics(error.attr_span); + + let phantom_variant: syn::Variant = syn::parse_quote!( + #[doc(hidden)] + #[codec(skip)] + __Ignore( + #frame_support::__private::sp_std::marker::PhantomData<(#type_use_gen)>, + #frame_support::Never, + ) + ); + + let as_str_matches = error.variants.iter().map( |VariantDef { ident: variant, field: field_ty, docs: _, cfg_attrs }| { let variant_str = variant.to_string(); let cfg_attrs = cfg_attrs.iter().map(|attr| attr.to_token_stream()); @@ -89,103 +89,107 @@ pub fn expand_error(def: &mut Def) -> proc_macro2::TokenStream { }, ); - let error_item = { - let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[error.index]; - if let syn::Item::Enum(item) = item { - item - } else { - unreachable!("Checked by error parser") - } - }; - - error_item.variants.insert(0, phantom_variant); - - let capture_docs = if cfg!(feature = "no-metadata-docs") { "never" } else { "always" }; - - // derive TypeInfo for error metadata - error_item.attrs.push(syn::parse_quote! { - #[derive( - #frame_support::__private::codec::Encode, - #frame_support::__private::codec::Decode, - #frame_support::__private::scale_info::TypeInfo, - #frame_support::PalletError, - )] - }); - error_item.attrs.push(syn::parse_quote!( - #[scale_info(skip_type_params(#type_use_gen), capture_docs = #capture_docs)] - )); - - if get_doc_literals(&error_item.attrs).is_empty() { - error_item.attrs.push(syn::parse_quote!( - #[doc = "The `Error` enum of this pallet."] - )); - } - - quote::quote_spanned!(error.attr_span => - impl<#type_impl_gen> #frame_support::__private::sp_std::fmt::Debug for #error_ident<#type_use_gen> - #config_where_clause - { - fn fmt(&self, f: &mut #frame_support::__private::sp_std::fmt::Formatter<'_>) - -> #frame_support::__private::sp_std::fmt::Result - { - f.write_str(self.as_str()) - } - } - - impl<#type_impl_gen> #error_ident<#type_use_gen> #config_where_clause { - #[doc(hidden)] - pub fn as_str(&self) -> &'static str { - match &self { - Self::__Ignore(_, _) => unreachable!("`__Ignore` can never be constructed"), - #( #as_str_matches )* - } - } - } - - impl<#type_impl_gen> From<#error_ident<#type_use_gen>> for &'static str - #config_where_clause - { - fn from(err: #error_ident<#type_use_gen>) -> &'static str { - err.as_str() - } - } - - impl<#type_impl_gen> From<#error_ident<#type_use_gen>> - for #frame_support::sp_runtime::DispatchError - #config_where_clause - { - fn from(err: #error_ident<#type_use_gen>) -> Self { - use #frame_support::__private::codec::Encode; - let index = < - ::PalletInfo - as #frame_support::traits::PalletInfo - >::index::>() - .expect("Every active module has an index in the runtime; qed") as u8; - let mut encoded = err.encode(); - encoded.resize(#frame_support::MAX_MODULE_ERROR_ENCODED_SIZE, 0); - - #frame_support::sp_runtime::DispatchError::Module(#frame_support::sp_runtime::ModuleError { - index, - error: TryInto::try_into(encoded).expect("encoded error is resized to be equal to the maximum encoded error size; qed"), - message: Some(err.as_str()), - }) - } - } - - #[macro_export] - #[doc(hidden)] - macro_rules! #error_token_unique_id { - { - $caller:tt - your_tt_return = [{ $my_tt_return:path }] - } => { - $my_tt_return! { - $caller - error = [{ #error_ident }] - } - }; - } - - pub use #error_token_unique_id as tt_error_token; - ) + let error_item = { + let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[error.index]; + if let syn::Item::Enum(item) = item { + item + } else { + unreachable!("Checked by error parser") + } + }; + + error_item.variants.insert(0, phantom_variant); + + let capture_docs = if cfg!(feature = "no-metadata-docs") { + "never" + } else { + "always" + }; + + // derive TypeInfo for error metadata + error_item.attrs.push(syn::parse_quote! { + #[derive( + #frame_support::__private::codec::Encode, + #frame_support::__private::codec::Decode, + #frame_support::__private::scale_info::TypeInfo, + #frame_support::PalletError, + )] + }); + error_item.attrs.push(syn::parse_quote!( + #[scale_info(skip_type_params(#type_use_gen), capture_docs = #capture_docs)] + )); + + if get_doc_literals(&error_item.attrs).is_empty() { + error_item.attrs.push(syn::parse_quote!( + #[doc = "The `Error` enum of this pallet."] + )); + } + + quote::quote_spanned!(error.attr_span => + impl<#type_impl_gen> #frame_support::__private::sp_std::fmt::Debug for #error_ident<#type_use_gen> + #config_where_clause + { + fn fmt(&self, f: &mut #frame_support::__private::sp_std::fmt::Formatter<'_>) + -> #frame_support::__private::sp_std::fmt::Result + { + f.write_str(self.as_str()) + } + } + + impl<#type_impl_gen> #error_ident<#type_use_gen> #config_where_clause { + #[doc(hidden)] + pub fn as_str(&self) -> &'static str { + match &self { + Self::__Ignore(_, _) => unreachable!("`__Ignore` can never be constructed"), + #( #as_str_matches )* + } + } + } + + impl<#type_impl_gen> From<#error_ident<#type_use_gen>> for &'static str + #config_where_clause + { + fn from(err: #error_ident<#type_use_gen>) -> &'static str { + err.as_str() + } + } + + impl<#type_impl_gen> From<#error_ident<#type_use_gen>> + for #frame_support::sp_runtime::DispatchError + #config_where_clause + { + fn from(err: #error_ident<#type_use_gen>) -> Self { + use #frame_support::__private::codec::Encode; + let index = < + ::PalletInfo + as #frame_support::traits::PalletInfo + >::index::>() + .expect("Every active module has an index in the runtime; qed") as u8; + let mut encoded = err.encode(); + encoded.resize(#frame_support::MAX_MODULE_ERROR_ENCODED_SIZE, 0); + + #frame_support::sp_runtime::DispatchError::Module(#frame_support::sp_runtime::ModuleError { + index, + error: TryInto::try_into(encoded).expect("encoded error is resized to be equal to the maximum encoded error size; qed"), + message: Some(err.as_str()), + }) + } + } + + #[macro_export] + #[doc(hidden)] + macro_rules! #error_token_unique_id { + { + $caller:tt + your_tt_return = [{ $my_tt_return:path }] + } => { + $my_tt_return! { + $caller + error = [{ #error_ident }] + } + }; + } + + pub use #error_token_unique_id as tt_error_token; + ) } diff --git a/support/procedural-fork/src/pallet/expand/event.rs b/support/procedural-fork/src/pallet/expand/event.rs index 655fc5507..931dcd95a 100644 --- a/support/procedural-fork/src/pallet/expand/event.rs +++ b/support/procedural-fork/src/pallet/expand/event.rs @@ -16,8 +16,8 @@ // limitations under the License. use crate::{ - pallet::{parse::event::PalletEventDepositAttr, Def}, - COUNTER, + pallet::{parse::event::PalletEventDepositAttr, Def}, + COUNTER, }; use frame_support_procedural_tools::get_doc_literals; use syn::{spanned::Spanned, Ident}; @@ -27,148 +27,159 @@ use syn::{spanned::Spanned, Ident}; /// * Impl various trait on Event including metadata /// * if deposit_event is defined, implement deposit_event on module. pub fn expand_event(def: &mut Def) -> proc_macro2::TokenStream { - let count = COUNTER.with(|counter| counter.borrow_mut().inc()); - - let (event, macro_ident) = if let Some(event) = &def.event { - let ident = Ident::new(&format!("__is_event_part_defined_{}", count), event.attr_span); - (event, ident) - } else { - let macro_ident = - Ident::new(&format!("__is_event_part_defined_{}", count), def.item.span()); - - return quote::quote! { - #[doc(hidden)] - pub mod __substrate_event_check { - #[macro_export] - #[doc(hidden)] - macro_rules! #macro_ident { - ($pallet_name:ident) => { - compile_error!(concat!( - "`", - stringify!($pallet_name), - "` does not have #[pallet::event] defined, perhaps you should \ - remove `Event` from construct_runtime?", - )); - } - } - - #[doc(hidden)] - pub use #macro_ident as is_event_part_defined; - } - } - }; - - let event_where_clause = &event.where_clause; - - // NOTE: actually event where clause must be a subset of config where clause because of - // `type RuntimeEvent: From>`. But we merge either way for potential better error - // message - let completed_where_clause = - super::merge_where_clauses(&[&event.where_clause, &def.config.where_clause]); - - let event_ident = &event.event; - let frame_system = &def.frame_system; - let frame_support = &def.frame_support; - let event_use_gen = &event.gen_kind.type_use_gen(event.attr_span); - let event_impl_gen = &event.gen_kind.type_impl_gen(event.attr_span); - - let event_item = { - let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[event.index]; - if let syn::Item::Enum(item) = item { - item - } else { - unreachable!("Checked by event parser") - } - }; - - // Phantom data is added for generic event. - if event.gen_kind.is_generic() { - let variant = syn::parse_quote!( - #[doc(hidden)] - #[codec(skip)] - __Ignore( - ::core::marker::PhantomData<(#event_use_gen)>, - #frame_support::Never, - ) - ); - - // Push ignore variant at the end. - event_item.variants.push(variant); - } - - if get_doc_literals(&event_item.attrs).is_empty() { - event_item - .attrs - .push(syn::parse_quote!(#[doc = "The `Event` enum of this pallet"])); - } - - // derive some traits because system event require Clone, FullCodec, Eq, PartialEq and Debug - event_item.attrs.push(syn::parse_quote!( - #[derive( - #frame_support::CloneNoBound, - #frame_support::EqNoBound, - #frame_support::PartialEqNoBound, - #frame_support::RuntimeDebugNoBound, - #frame_support::__private::codec::Encode, - #frame_support::__private::codec::Decode, - #frame_support::__private::scale_info::TypeInfo, - )] - )); - - let capture_docs = if cfg!(feature = "no-metadata-docs") { "never" } else { "always" }; - - // skip requirement for type params to implement `TypeInfo`, and set docs capture - event_item.attrs.push(syn::parse_quote!( - #[scale_info(skip_type_params(#event_use_gen), capture_docs = #capture_docs)] - )); - - let deposit_event = if let Some(deposit_event) = &event.deposit_event { - let event_use_gen = &event.gen_kind.type_use_gen(event.attr_span); - let trait_use_gen = &def.trait_use_generics(event.attr_span); - let type_impl_gen = &def.type_impl_generics(event.attr_span); - let type_use_gen = &def.type_use_generics(event.attr_span); - let pallet_ident = &def.pallet_struct.pallet; - - let PalletEventDepositAttr { fn_vis, fn_span, .. } = deposit_event; - - quote::quote_spanned!(*fn_span => - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { - #fn_vis fn deposit_event(event: Event<#event_use_gen>) { - let event = < - ::RuntimeEvent as - From> - >::from(event); - - let event = < - ::RuntimeEvent as - Into<::RuntimeEvent> - >::into(event); - - <#frame_system::Pallet>::deposit_event(event) - } - } - ) - } else { - Default::default() - }; - - quote::quote_spanned!(event.attr_span => - #[doc(hidden)] - pub mod __substrate_event_check { - #[macro_export] - #[doc(hidden)] - macro_rules! #macro_ident { - ($pallet_name:ident) => {}; - } - - #[doc(hidden)] - pub use #macro_ident as is_event_part_defined; - } - - #deposit_event - - impl<#event_impl_gen> From<#event_ident<#event_use_gen>> for () #event_where_clause { - fn from(_: #event_ident<#event_use_gen>) {} - } - ) + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + + let (event, macro_ident) = if let Some(event) = &def.event { + let ident = Ident::new( + &format!("__is_event_part_defined_{}", count), + event.attr_span, + ); + (event, ident) + } else { + let macro_ident = Ident::new( + &format!("__is_event_part_defined_{}", count), + def.item.span(), + ); + + return quote::quote! { + #[doc(hidden)] + pub mod __substrate_event_check { + #[macro_export] + #[doc(hidden)] + macro_rules! #macro_ident { + ($pallet_name:ident) => { + compile_error!(concat!( + "`", + stringify!($pallet_name), + "` does not have #[pallet::event] defined, perhaps you should \ + remove `Event` from construct_runtime?", + )); + } + } + + #[doc(hidden)] + pub use #macro_ident as is_event_part_defined; + } + }; + }; + + let event_where_clause = &event.where_clause; + + // NOTE: actually event where clause must be a subset of config where clause because of + // `type RuntimeEvent: From>`. But we merge either way for potential better error + // message + let completed_where_clause = + super::merge_where_clauses(&[&event.where_clause, &def.config.where_clause]); + + let event_ident = &event.event; + let frame_system = &def.frame_system; + let frame_support = &def.frame_support; + let event_use_gen = &event.gen_kind.type_use_gen(event.attr_span); + let event_impl_gen = &event.gen_kind.type_impl_gen(event.attr_span); + + let event_item = { + let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[event.index]; + if let syn::Item::Enum(item) = item { + item + } else { + unreachable!("Checked by event parser") + } + }; + + // Phantom data is added for generic event. + if event.gen_kind.is_generic() { + let variant = syn::parse_quote!( + #[doc(hidden)] + #[codec(skip)] + __Ignore( + ::core::marker::PhantomData<(#event_use_gen)>, + #frame_support::Never, + ) + ); + + // Push ignore variant at the end. + event_item.variants.push(variant); + } + + if get_doc_literals(&event_item.attrs).is_empty() { + event_item + .attrs + .push(syn::parse_quote!(#[doc = "The `Event` enum of this pallet"])); + } + + // derive some traits because system event require Clone, FullCodec, Eq, PartialEq and Debug + event_item.attrs.push(syn::parse_quote!( + #[derive( + #frame_support::CloneNoBound, + #frame_support::EqNoBound, + #frame_support::PartialEqNoBound, + #frame_support::RuntimeDebugNoBound, + #frame_support::__private::codec::Encode, + #frame_support::__private::codec::Decode, + #frame_support::__private::scale_info::TypeInfo, + )] + )); + + let capture_docs = if cfg!(feature = "no-metadata-docs") { + "never" + } else { + "always" + }; + + // skip requirement for type params to implement `TypeInfo`, and set docs capture + event_item.attrs.push(syn::parse_quote!( + #[scale_info(skip_type_params(#event_use_gen), capture_docs = #capture_docs)] + )); + + let deposit_event = if let Some(deposit_event) = &event.deposit_event { + let event_use_gen = &event.gen_kind.type_use_gen(event.attr_span); + let trait_use_gen = &def.trait_use_generics(event.attr_span); + let type_impl_gen = &def.type_impl_generics(event.attr_span); + let type_use_gen = &def.type_use_generics(event.attr_span); + let pallet_ident = &def.pallet_struct.pallet; + + let PalletEventDepositAttr { + fn_vis, fn_span, .. + } = deposit_event; + + quote::quote_spanned!(*fn_span => + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { + #fn_vis fn deposit_event(event: Event<#event_use_gen>) { + let event = < + ::RuntimeEvent as + From> + >::from(event); + + let event = < + ::RuntimeEvent as + Into<::RuntimeEvent> + >::into(event); + + <#frame_system::Pallet>::deposit_event(event) + } + } + ) + } else { + Default::default() + }; + + quote::quote_spanned!(event.attr_span => + #[doc(hidden)] + pub mod __substrate_event_check { + #[macro_export] + #[doc(hidden)] + macro_rules! #macro_ident { + ($pallet_name:ident) => {}; + } + + #[doc(hidden)] + pub use #macro_ident as is_event_part_defined; + } + + #deposit_event + + impl<#event_impl_gen> From<#event_ident<#event_use_gen>> for () #event_where_clause { + fn from(_: #event_ident<#event_use_gen>) {} + } + ) } diff --git a/support/procedural-fork/src/pallet/expand/genesis_build.rs b/support/procedural-fork/src/pallet/expand/genesis_build.rs index 248e83469..c6089550d 100644 --- a/support/procedural-fork/src/pallet/expand/genesis_build.rs +++ b/support/procedural-fork/src/pallet/expand/genesis_build.rs @@ -20,30 +20,34 @@ use crate::pallet::Def; /// /// * implement the trait `sp_runtime::BuildStorage` pub fn expand_genesis_build(def: &mut Def) -> proc_macro2::TokenStream { - let genesis_config = if let Some(genesis_config) = &def.genesis_config { - genesis_config - } else { - return Default::default() - }; - let genesis_build = def.genesis_build.as_ref().expect("Checked by def parser"); + let genesis_config = if let Some(genesis_config) = &def.genesis_config { + genesis_config + } else { + return Default::default(); + }; + let genesis_build = def.genesis_build.as_ref().expect("Checked by def parser"); - let frame_support = &def.frame_support; - let type_impl_gen = &genesis_config.gen_kind.type_impl_gen(genesis_build.attr_span); - let gen_cfg_ident = &genesis_config.genesis_config; - let gen_cfg_use_gen = &genesis_config.gen_kind.type_use_gen(genesis_build.attr_span); + let frame_support = &def.frame_support; + let type_impl_gen = &genesis_config + .gen_kind + .type_impl_gen(genesis_build.attr_span); + let gen_cfg_ident = &genesis_config.genesis_config; + let gen_cfg_use_gen = &genesis_config + .gen_kind + .type_use_gen(genesis_build.attr_span); - let where_clause = &genesis_build.where_clause; + let where_clause = &genesis_build.where_clause; - quote::quote_spanned!(genesis_build.attr_span => - #[cfg(feature = "std")] - impl<#type_impl_gen> #frame_support::sp_runtime::BuildStorage for #gen_cfg_ident<#gen_cfg_use_gen> #where_clause - { - fn assimilate_storage(&self, storage: &mut #frame_support::sp_runtime::Storage) -> std::result::Result<(), std::string::String> { - #frame_support::__private::BasicExternalities::execute_with_storage(storage, || { - self.build(); - Ok(()) - }) - } - } - ) + quote::quote_spanned!(genesis_build.attr_span => + #[cfg(feature = "std")] + impl<#type_impl_gen> #frame_support::sp_runtime::BuildStorage for #gen_cfg_ident<#gen_cfg_use_gen> #where_clause + { + fn assimilate_storage(&self, storage: &mut #frame_support::sp_runtime::Storage) -> std::result::Result<(), std::string::String> { + #frame_support::__private::BasicExternalities::execute_with_storage(storage, || { + self.build(); + Ok(()) + }) + } + } + ) } diff --git a/support/procedural-fork/src/pallet/expand/genesis_config.rs b/support/procedural-fork/src/pallet/expand/genesis_config.rs index 31d519ef2..e171e2468 100644 --- a/support/procedural-fork/src/pallet/expand/genesis_config.rs +++ b/support/procedural-fork/src/pallet/expand/genesis_config.rs @@ -23,125 +23,130 @@ use syn::{spanned::Spanned, Ident}; /// /// * add various derive trait on GenesisConfig struct. pub fn expand_genesis_config(def: &mut Def) -> proc_macro2::TokenStream { - let count = COUNTER.with(|counter| counter.borrow_mut().inc()); - - let (genesis_config, def_macro_ident, std_macro_ident) = - if let Some(genesis_config) = &def.genesis_config { - let def_macro_ident = Ident::new( - &format!("__is_genesis_config_defined_{}", count), - genesis_config.genesis_config.span(), - ); - - let std_macro_ident = Ident::new( - &format!("__is_std_macro_defined_for_genesis_{}", count), - genesis_config.genesis_config.span(), - ); - - (genesis_config, def_macro_ident, std_macro_ident) - } else { - let def_macro_ident = - Ident::new(&format!("__is_genesis_config_defined_{}", count), def.item.span()); - - let std_macro_ident = - Ident::new(&format!("__is_std_enabled_for_genesis_{}", count), def.item.span()); - - return quote::quote! { - #[doc(hidden)] - pub mod __substrate_genesis_config_check { - #[macro_export] - #[doc(hidden)] - macro_rules! #def_macro_ident { - ($pallet_name:ident) => { - compile_error!(concat!( - "`", - stringify!($pallet_name), - "` does not have #[pallet::genesis_config] defined, perhaps you should \ - remove `Config` from construct_runtime?", - )); - } - } - - #[macro_export] - #[doc(hidden)] - macro_rules! #std_macro_ident { - ($pallet_name:ident, $pallet_path:expr) => {}; - } - - #[doc(hidden)] - pub use #def_macro_ident as is_genesis_config_defined; - #[doc(hidden)] - pub use #std_macro_ident as is_std_enabled_for_genesis; - } - } - }; - - let frame_support = &def.frame_support; - - let genesis_config_item = - &mut def.item.content.as_mut().expect("Checked by def parser").1[genesis_config.index]; - - let serde_crate = format!("{}::__private::serde", frame_support.to_token_stream()); - - match genesis_config_item { - syn::Item::Enum(syn::ItemEnum { attrs, .. }) | - syn::Item::Struct(syn::ItemStruct { attrs, .. }) | - syn::Item::Type(syn::ItemType { attrs, .. }) => { - if get_doc_literals(attrs).is_empty() { - attrs.push(syn::parse_quote!( - #[doc = r" + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + + let (genesis_config, def_macro_ident, std_macro_ident) = if let Some(genesis_config) = + &def.genesis_config + { + let def_macro_ident = Ident::new( + &format!("__is_genesis_config_defined_{}", count), + genesis_config.genesis_config.span(), + ); + + let std_macro_ident = Ident::new( + &format!("__is_std_macro_defined_for_genesis_{}", count), + genesis_config.genesis_config.span(), + ); + + (genesis_config, def_macro_ident, std_macro_ident) + } else { + let def_macro_ident = Ident::new( + &format!("__is_genesis_config_defined_{}", count), + def.item.span(), + ); + + let std_macro_ident = Ident::new( + &format!("__is_std_enabled_for_genesis_{}", count), + def.item.span(), + ); + + return quote::quote! { + #[doc(hidden)] + pub mod __substrate_genesis_config_check { + #[macro_export] + #[doc(hidden)] + macro_rules! #def_macro_ident { + ($pallet_name:ident) => { + compile_error!(concat!( + "`", + stringify!($pallet_name), + "` does not have #[pallet::genesis_config] defined, perhaps you should \ + remove `Config` from construct_runtime?", + )); + } + } + + #[macro_export] + #[doc(hidden)] + macro_rules! #std_macro_ident { + ($pallet_name:ident, $pallet_path:expr) => {}; + } + + #[doc(hidden)] + pub use #def_macro_ident as is_genesis_config_defined; + #[doc(hidden)] + pub use #std_macro_ident as is_std_enabled_for_genesis; + } + }; + }; + + let frame_support = &def.frame_support; + + let genesis_config_item = + &mut def.item.content.as_mut().expect("Checked by def parser").1[genesis_config.index]; + + let serde_crate = format!("{}::__private::serde", frame_support.to_token_stream()); + + match genesis_config_item { + syn::Item::Enum(syn::ItemEnum { attrs, .. }) + | syn::Item::Struct(syn::ItemStruct { attrs, .. }) + | syn::Item::Type(syn::ItemType { attrs, .. }) => { + if get_doc_literals(attrs).is_empty() { + attrs.push(syn::parse_quote!( + #[doc = r" Can be used to configure the [genesis state](https://docs.substrate.io/build/genesis-configuration/) of this pallet. "] - )); - } - attrs.push(syn::parse_quote!( - #[derive(#frame_support::Serialize, #frame_support::Deserialize)] - )); - attrs.push(syn::parse_quote!( #[serde(rename_all = "camelCase")] )); - attrs.push(syn::parse_quote!( #[serde(deny_unknown_fields)] )); - attrs.push(syn::parse_quote!( #[serde(bound(serialize = ""))] )); - attrs.push(syn::parse_quote!( #[serde(bound(deserialize = ""))] )); - attrs.push(syn::parse_quote!( #[serde(crate = #serde_crate)] )); - }, - _ => unreachable!("Checked by genesis_config parser"), - } - - quote::quote! { - #[doc(hidden)] - pub mod __substrate_genesis_config_check { - #[macro_export] - #[doc(hidden)] - macro_rules! #def_macro_ident { - ($pallet_name:ident) => {}; - } - - #[cfg(not(feature = "std"))] - #[macro_export] - #[doc(hidden)] - macro_rules! #std_macro_ident { - ($pallet_name:ident, $pallet_path:expr) => { - compile_error!(concat!( - "`", - stringify!($pallet_name), - "` does not have the std feature enabled, this will cause the `", - $pallet_path, - "::GenesisConfig` type to not implement serde traits." - )); - }; - } - - #[cfg(feature = "std")] - #[macro_export] - #[doc(hidden)] - macro_rules! #std_macro_ident { - ($pallet_name:ident, $pallet_path:expr) => {}; - } - - #[doc(hidden)] - pub use #def_macro_ident as is_genesis_config_defined; - #[doc(hidden)] - pub use #std_macro_ident as is_std_enabled_for_genesis; - } - } + )); + } + attrs.push(syn::parse_quote!( + #[derive(#frame_support::Serialize, #frame_support::Deserialize)] + )); + attrs.push(syn::parse_quote!( #[serde(rename_all = "camelCase")] )); + attrs.push(syn::parse_quote!( #[serde(deny_unknown_fields)] )); + attrs.push(syn::parse_quote!( #[serde(bound(serialize = ""))] )); + attrs.push(syn::parse_quote!( #[serde(bound(deserialize = ""))] )); + attrs.push(syn::parse_quote!( #[serde(crate = #serde_crate)] )); + } + _ => unreachable!("Checked by genesis_config parser"), + } + + quote::quote! { + #[doc(hidden)] + pub mod __substrate_genesis_config_check { + #[macro_export] + #[doc(hidden)] + macro_rules! #def_macro_ident { + ($pallet_name:ident) => {}; + } + + #[cfg(not(feature = "std"))] + #[macro_export] + #[doc(hidden)] + macro_rules! #std_macro_ident { + ($pallet_name:ident, $pallet_path:expr) => { + compile_error!(concat!( + "`", + stringify!($pallet_name), + "` does not have the std feature enabled, this will cause the `", + $pallet_path, + "::GenesisConfig` type to not implement serde traits." + )); + }; + } + + #[cfg(feature = "std")] + #[macro_export] + #[doc(hidden)] + macro_rules! #std_macro_ident { + ($pallet_name:ident, $pallet_path:expr) => {}; + } + + #[doc(hidden)] + pub use #def_macro_ident as is_genesis_config_defined; + #[doc(hidden)] + pub use #std_macro_ident as is_std_enabled_for_genesis; + } + } } diff --git a/support/procedural-fork/src/pallet/expand/hooks.rs b/support/procedural-fork/src/pallet/expand/hooks.rs index 3623b5952..6967f4c08 100644 --- a/support/procedural-fork/src/pallet/expand/hooks.rs +++ b/support/procedural-fork/src/pallet/expand/hooks.rs @@ -19,322 +19,326 @@ use crate::pallet::Def; /// * implement the individual traits using the Hooks trait pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream { - let (where_clause, span, has_runtime_upgrade) = match def.hooks.as_ref() { - Some(hooks) => { - let where_clause = hooks.where_clause.clone(); - let span = hooks.attr_span; - let has_runtime_upgrade = hooks.has_runtime_upgrade; - (where_clause, span, has_runtime_upgrade) - }, - None => (def.config.where_clause.clone(), def.pallet_struct.attr_span, false), - }; + let (where_clause, span, has_runtime_upgrade) = match def.hooks.as_ref() { + Some(hooks) => { + let where_clause = hooks.where_clause.clone(); + let span = hooks.attr_span; + let has_runtime_upgrade = hooks.has_runtime_upgrade; + (where_clause, span, has_runtime_upgrade) + } + None => ( + def.config.where_clause.clone(), + def.pallet_struct.attr_span, + false, + ), + }; - let frame_support = &def.frame_support; - let type_impl_gen = &def.type_impl_generics(span); - let type_use_gen = &def.type_use_generics(span); - let pallet_ident = &def.pallet_struct.pallet; - let frame_system = &def.frame_system; - let pallet_name = quote::quote! { - < - ::PalletInfo - as - #frame_support::traits::PalletInfo - >::name::().unwrap_or("") - }; + let frame_support = &def.frame_support; + let type_impl_gen = &def.type_impl_generics(span); + let type_use_gen = &def.type_use_generics(span); + let pallet_ident = &def.pallet_struct.pallet; + let frame_system = &def.frame_system; + let pallet_name = quote::quote! { + < + ::PalletInfo + as + #frame_support::traits::PalletInfo + >::name::().unwrap_or("") + }; - let initialize_on_chain_storage_version = if let Some(in_code_version) = - &def.pallet_struct.storage_version - { - quote::quote! { - #frame_support::__private::log::info!( - target: #frame_support::LOG_TARGET, - "🐥 New pallet {:?} detected in the runtime. Initializing the on-chain storage version to match the storage version defined in the pallet: {:?}", - #pallet_name, - #in_code_version - ); - #in_code_version.put::(); - } - } else { - quote::quote! { - let default_version = #frame_support::traits::StorageVersion::new(0); - #frame_support::__private::log::info!( - target: #frame_support::LOG_TARGET, - "🐥 New pallet {:?} detected in the runtime. The pallet has no defined storage version, so the on-chain version is being initialized to {:?}.", - #pallet_name, - default_version - ); - default_version.put::(); - } - }; + let initialize_on_chain_storage_version = if let Some(in_code_version) = + &def.pallet_struct.storage_version + { + quote::quote! { + #frame_support::__private::log::info!( + target: #frame_support::LOG_TARGET, + "🐥 New pallet {:?} detected in the runtime. Initializing the on-chain storage version to match the storage version defined in the pallet: {:?}", + #pallet_name, + #in_code_version + ); + #in_code_version.put::(); + } + } else { + quote::quote! { + let default_version = #frame_support::traits::StorageVersion::new(0); + #frame_support::__private::log::info!( + target: #frame_support::LOG_TARGET, + "🐥 New pallet {:?} detected in the runtime. The pallet has no defined storage version, so the on-chain version is being initialized to {:?}.", + #pallet_name, + default_version + ); + default_version.put::(); + } + }; - let log_runtime_upgrade = if has_runtime_upgrade { - // a migration is defined here. - quote::quote! { - #frame_support::__private::log::info!( - target: #frame_support::LOG_TARGET, - "⚠️ {} declares internal migrations (which *might* execute). \ - On-chain `{:?}` vs in-code storage version `{:?}`", - #pallet_name, - ::on_chain_storage_version(), - ::in_code_storage_version(), - ); - } - } else { - // default. - quote::quote! { - #frame_support::__private::log::debug!( - target: #frame_support::LOG_TARGET, - "✅ no migration for {}", - #pallet_name, - ); - } - }; + let log_runtime_upgrade = if has_runtime_upgrade { + // a migration is defined here. + quote::quote! { + #frame_support::__private::log::info!( + target: #frame_support::LOG_TARGET, + "⚠️ {} declares internal migrations (which *might* execute). \ + On-chain `{:?}` vs in-code storage version `{:?}`", + #pallet_name, + ::on_chain_storage_version(), + ::in_code_storage_version(), + ); + } + } else { + // default. + quote::quote! { + #frame_support::__private::log::debug!( + target: #frame_support::LOG_TARGET, + "✅ no migration for {}", + #pallet_name, + ); + } + }; - let hooks_impl = if def.hooks.is_none() { - let frame_system = &def.frame_system; - quote::quote! { - impl<#type_impl_gen> - #frame_support::traits::Hooks<#frame_system::pallet_prelude::BlockNumberFor::> - for #pallet_ident<#type_use_gen> #where_clause {} - } - } else { - proc_macro2::TokenStream::new() - }; + let hooks_impl = if def.hooks.is_none() { + let frame_system = &def.frame_system; + quote::quote! { + impl<#type_impl_gen> + #frame_support::traits::Hooks<#frame_system::pallet_prelude::BlockNumberFor::> + for #pallet_ident<#type_use_gen> #where_clause {} + } + } else { + proc_macro2::TokenStream::new() + }; - // If a storage version is set, we should ensure that the storage version on chain matches the - // in-code storage version. This assumes that `Executive` is running custom migrations before - // the pallets are called. - let post_storage_version_check = if def.pallet_struct.storage_version.is_some() { - quote::quote! { - let on_chain_version = ::on_chain_storage_version(); - let in_code_version = ::in_code_storage_version(); + // If a storage version is set, we should ensure that the storage version on chain matches the + // in-code storage version. This assumes that `Executive` is running custom migrations before + // the pallets are called. + let post_storage_version_check = if def.pallet_struct.storage_version.is_some() { + quote::quote! { + let on_chain_version = ::on_chain_storage_version(); + let in_code_version = ::in_code_storage_version(); - if on_chain_version != in_code_version { - #frame_support::__private::log::error!( - target: #frame_support::LOG_TARGET, - "{}: On chain storage version {:?} doesn't match in-code storage version {:?}.", - #pallet_name, - on_chain_version, - in_code_version, - ); + if on_chain_version != in_code_version { + #frame_support::__private::log::error!( + target: #frame_support::LOG_TARGET, + "{}: On chain storage version {:?} doesn't match in-code storage version {:?}.", + #pallet_name, + on_chain_version, + in_code_version, + ); - return Err("On chain and in-code storage version do not match. Missing runtime upgrade?".into()); - } - } - } else { - quote::quote! { - let on_chain_version = ::on_chain_storage_version(); + return Err("On chain and in-code storage version do not match. Missing runtime upgrade?".into()); + } + } + } else { + quote::quote! { + let on_chain_version = ::on_chain_storage_version(); - if on_chain_version != #frame_support::traits::StorageVersion::new(0) { - #frame_support::__private::log::error!( - target: #frame_support::LOG_TARGET, - "{}: On chain storage version {:?} is set to non zero, \ - while the pallet is missing the `#[pallet::storage_version(VERSION)]` attribute.", - #pallet_name, - on_chain_version, - ); + if on_chain_version != #frame_support::traits::StorageVersion::new(0) { + #frame_support::__private::log::error!( + target: #frame_support::LOG_TARGET, + "{}: On chain storage version {:?} is set to non zero, \ + while the pallet is missing the `#[pallet::storage_version(VERSION)]` attribute.", + #pallet_name, + on_chain_version, + ); - return Err("On chain storage version set, while the pallet doesn't \ - have the `#[pallet::storage_version(VERSION)]` attribute.".into()); - } - } - }; + return Err("On chain storage version set, while the pallet doesn't \ + have the `#[pallet::storage_version(VERSION)]` attribute.".into()); + } + } + }; - quote::quote_spanned!(span => - #hooks_impl + quote::quote_spanned!(span => + #hooks_impl - impl<#type_impl_gen> - #frame_support::traits::OnFinalize<#frame_system::pallet_prelude::BlockNumberFor::> - for #pallet_ident<#type_use_gen> #where_clause - { - fn on_finalize(n: #frame_system::pallet_prelude::BlockNumberFor::) { - #frame_support::__private::sp_tracing::enter_span!( - #frame_support::__private::sp_tracing::trace_span!("on_finalize") - ); - < - Self as #frame_support::traits::Hooks< - #frame_system::pallet_prelude::BlockNumberFor:: - > - >::on_finalize(n) - } - } + impl<#type_impl_gen> + #frame_support::traits::OnFinalize<#frame_system::pallet_prelude::BlockNumberFor::> + for #pallet_ident<#type_use_gen> #where_clause + { + fn on_finalize(n: #frame_system::pallet_prelude::BlockNumberFor::) { + #frame_support::__private::sp_tracing::enter_span!( + #frame_support::__private::sp_tracing::trace_span!("on_finalize") + ); + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::on_finalize(n) + } + } - impl<#type_impl_gen> - #frame_support::traits::OnIdle<#frame_system::pallet_prelude::BlockNumberFor::> - for #pallet_ident<#type_use_gen> #where_clause - { - fn on_idle( - n: #frame_system::pallet_prelude::BlockNumberFor::, - remaining_weight: #frame_support::weights::Weight - ) -> #frame_support::weights::Weight { - < - Self as #frame_support::traits::Hooks< - #frame_system::pallet_prelude::BlockNumberFor:: - > - >::on_idle(n, remaining_weight) - } - } + impl<#type_impl_gen> + #frame_support::traits::OnIdle<#frame_system::pallet_prelude::BlockNumberFor::> + for #pallet_ident<#type_use_gen> #where_clause + { + fn on_idle( + n: #frame_system::pallet_prelude::BlockNumberFor::, + remaining_weight: #frame_support::weights::Weight + ) -> #frame_support::weights::Weight { + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::on_idle(n, remaining_weight) + } + } - impl<#type_impl_gen> - #frame_support::traits::OnPoll<#frame_system::pallet_prelude::BlockNumberFor::> - for #pallet_ident<#type_use_gen> #where_clause - { - fn on_poll( - n: #frame_system::pallet_prelude::BlockNumberFor::, - weight: &mut #frame_support::weights::WeightMeter - ) { - < - Self as #frame_support::traits::Hooks< - #frame_system::pallet_prelude::BlockNumberFor:: - > - >::on_poll(n, weight); - } - } + impl<#type_impl_gen> + #frame_support::traits::OnPoll<#frame_system::pallet_prelude::BlockNumberFor::> + for #pallet_ident<#type_use_gen> #where_clause + { + fn on_poll( + n: #frame_system::pallet_prelude::BlockNumberFor::, + weight: &mut #frame_support::weights::WeightMeter + ) { + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::on_poll(n, weight); + } + } - impl<#type_impl_gen> - #frame_support::traits::OnInitialize<#frame_system::pallet_prelude::BlockNumberFor::> - for #pallet_ident<#type_use_gen> #where_clause - { - fn on_initialize( - n: #frame_system::pallet_prelude::BlockNumberFor:: - ) -> #frame_support::weights::Weight { - #frame_support::__private::sp_tracing::enter_span!( - #frame_support::__private::sp_tracing::trace_span!("on_initialize") - ); - < - Self as #frame_support::traits::Hooks< - #frame_system::pallet_prelude::BlockNumberFor:: - > - >::on_initialize(n) - } - } + impl<#type_impl_gen> + #frame_support::traits::OnInitialize<#frame_system::pallet_prelude::BlockNumberFor::> + for #pallet_ident<#type_use_gen> #where_clause + { + fn on_initialize( + n: #frame_system::pallet_prelude::BlockNumberFor:: + ) -> #frame_support::weights::Weight { + #frame_support::__private::sp_tracing::enter_span!( + #frame_support::__private::sp_tracing::trace_span!("on_initialize") + ); + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::on_initialize(n) + } + } - impl<#type_impl_gen> - #frame_support::traits::BeforeAllRuntimeMigrations - for #pallet_ident<#type_use_gen> #where_clause - { - fn before_all_runtime_migrations() -> #frame_support::weights::Weight { - use #frame_support::traits::{Get, PalletInfoAccess}; - use #frame_support::__private::hashing::twox_128; - use #frame_support::storage::unhashed::contains_prefixed_key; - #frame_support::__private::sp_tracing::enter_span!( - #frame_support::__private::sp_tracing::trace_span!("before_all") - ); + impl<#type_impl_gen> + #frame_support::traits::BeforeAllRuntimeMigrations + for #pallet_ident<#type_use_gen> #where_clause + { + fn before_all_runtime_migrations() -> #frame_support::weights::Weight { + use #frame_support::traits::{Get, PalletInfoAccess}; + use #frame_support::__private::hashing::twox_128; + use #frame_support::storage::unhashed::contains_prefixed_key; + #frame_support::__private::sp_tracing::enter_span!( + #frame_support::__private::sp_tracing::trace_span!("before_all") + ); - // Check if the pallet has any keys set, including the storage version. If there are - // no keys set, the pallet was just added to the runtime and needs to have its - // version initialized. - let pallet_hashed_prefix = ::name_hash(); - let exists = contains_prefixed_key(&pallet_hashed_prefix); - if !exists { - #initialize_on_chain_storage_version - ::DbWeight::get().reads_writes(1, 1) - } else { - ::DbWeight::get().reads(1) - } - } - } + // Check if the pallet has any keys set, including the storage version. If there are + // no keys set, the pallet was just added to the runtime and needs to have its + // version initialized. + let pallet_hashed_prefix = ::name_hash(); + let exists = contains_prefixed_key(&pallet_hashed_prefix); + if !exists { + #initialize_on_chain_storage_version + ::DbWeight::get().reads_writes(1, 1) + } else { + ::DbWeight::get().reads(1) + } + } + } - impl<#type_impl_gen> - #frame_support::traits::OnRuntimeUpgrade - for #pallet_ident<#type_use_gen> #where_clause - { - fn on_runtime_upgrade() -> #frame_support::weights::Weight { - #frame_support::__private::sp_tracing::enter_span!( - #frame_support::__private::sp_tracing::trace_span!("on_runtime_update") - ); + impl<#type_impl_gen> + #frame_support::traits::OnRuntimeUpgrade + for #pallet_ident<#type_use_gen> #where_clause + { + fn on_runtime_upgrade() -> #frame_support::weights::Weight { + #frame_support::__private::sp_tracing::enter_span!( + #frame_support::__private::sp_tracing::trace_span!("on_runtime_update") + ); - // log info about the upgrade. - #log_runtime_upgrade + // log info about the upgrade. + #log_runtime_upgrade - < - Self as #frame_support::traits::Hooks< - #frame_system::pallet_prelude::BlockNumberFor:: - > - >::on_runtime_upgrade() - } + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::on_runtime_upgrade() + } - #[cfg(feature = "try-runtime")] - fn pre_upgrade() -> Result<#frame_support::__private::sp_std::vec::Vec, #frame_support::sp_runtime::TryRuntimeError> { - < - Self - as - #frame_support::traits::Hooks<#frame_system::pallet_prelude::BlockNumberFor::> - >::pre_upgrade() - } + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result<#frame_support::__private::sp_std::vec::Vec, #frame_support::sp_runtime::TryRuntimeError> { + < + Self + as + #frame_support::traits::Hooks<#frame_system::pallet_prelude::BlockNumberFor::> + >::pre_upgrade() + } - #[cfg(feature = "try-runtime")] - fn post_upgrade(state: #frame_support::__private::sp_std::vec::Vec) -> Result<(), #frame_support::sp_runtime::TryRuntimeError> { - #post_storage_version_check + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: #frame_support::__private::sp_std::vec::Vec) -> Result<(), #frame_support::sp_runtime::TryRuntimeError> { + #post_storage_version_check - < - Self - as - #frame_support::traits::Hooks<#frame_system::pallet_prelude::BlockNumberFor::> - >::post_upgrade(state) - } - } + < + Self + as + #frame_support::traits::Hooks<#frame_system::pallet_prelude::BlockNumberFor::> + >::post_upgrade(state) + } + } - impl<#type_impl_gen> - #frame_support::traits::OffchainWorker<#frame_system::pallet_prelude::BlockNumberFor::> - for #pallet_ident<#type_use_gen> #where_clause - { - fn offchain_worker(n: #frame_system::pallet_prelude::BlockNumberFor::) { - < - Self as #frame_support::traits::Hooks< - #frame_system::pallet_prelude::BlockNumberFor:: - > - >::offchain_worker(n) - } - } + impl<#type_impl_gen> + #frame_support::traits::OffchainWorker<#frame_system::pallet_prelude::BlockNumberFor::> + for #pallet_ident<#type_use_gen> #where_clause + { + fn offchain_worker(n: #frame_system::pallet_prelude::BlockNumberFor::) { + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::offchain_worker(n) + } + } - // Integrity tests are only required for when `std` is enabled. - #frame_support::std_enabled! { - impl<#type_impl_gen> - #frame_support::traits::IntegrityTest - for #pallet_ident<#type_use_gen> #where_clause - { - fn integrity_test() { - #frame_support::__private::sp_io::TestExternalities::default().execute_with(|| { - < - Self as #frame_support::traits::Hooks< - #frame_system::pallet_prelude::BlockNumberFor:: - > - >::integrity_test() - }); - } - } - } + // Integrity tests are only required for when `std` is enabled. + #frame_support::std_enabled! { + impl<#type_impl_gen> + #frame_support::traits::IntegrityTest + for #pallet_ident<#type_use_gen> #where_clause + { + fn integrity_test() { + #frame_support::__private::sp_io::TestExternalities::default().execute_with(|| { + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::integrity_test() + }); + } + } + } - #[cfg(feature = "try-runtime")] - impl<#type_impl_gen> - #frame_support::traits::TryState<#frame_system::pallet_prelude::BlockNumberFor::> - for #pallet_ident<#type_use_gen> #where_clause - { - fn try_state( - n: #frame_system::pallet_prelude::BlockNumberFor::, - _s: #frame_support::traits::TryStateSelect - ) -> Result<(), #frame_support::sp_runtime::TryRuntimeError> { - #frame_support::__private::log::info!( - target: #frame_support::LOG_TARGET, - "🩺 Running {:?} try-state checks", - #pallet_name, - ); - < - Self as #frame_support::traits::Hooks< - #frame_system::pallet_prelude::BlockNumberFor:: - > - >::try_state(n).map_err(|err| { - #frame_support::__private::log::error!( - target: #frame_support::LOG_TARGET, - "❌ {:?} try_state checks failed: {:?}", - #pallet_name, - err - ); + #[cfg(feature = "try-runtime")] + impl<#type_impl_gen> + #frame_support::traits::TryState<#frame_system::pallet_prelude::BlockNumberFor::> + for #pallet_ident<#type_use_gen> #where_clause + { + fn try_state( + n: #frame_system::pallet_prelude::BlockNumberFor::, + _s: #frame_support::traits::TryStateSelect + ) -> Result<(), #frame_support::sp_runtime::TryRuntimeError> { + #frame_support::__private::log::info!( + target: #frame_support::LOG_TARGET, + "🩺 Running {:?} try-state checks", + #pallet_name, + ); + < + Self as #frame_support::traits::Hooks< + #frame_system::pallet_prelude::BlockNumberFor:: + > + >::try_state(n).map_err(|err| { + #frame_support::__private::log::error!( + target: #frame_support::LOG_TARGET, + "❌ {:?} try_state checks failed: {:?}", + #pallet_name, + err + ); - err - }) - } - } - ) + err + }) + } + } + ) } diff --git a/support/procedural-fork/src/pallet/expand/inherent.rs b/support/procedural-fork/src/pallet/expand/inherent.rs index 182d79f5b..0a80d672a 100644 --- a/support/procedural-fork/src/pallet/expand/inherent.rs +++ b/support/procedural-fork/src/pallet/expand/inherent.rs @@ -21,35 +21,38 @@ use quote::quote; use syn::{spanned::Spanned, Ident}; pub fn expand_inherents(def: &mut Def) -> TokenStream { - let count = COUNTER.with(|counter| counter.borrow_mut().inc()); - let macro_ident = Ident::new(&format!("__is_inherent_part_defined_{}", count), def.item.span()); + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + let macro_ident = Ident::new( + &format!("__is_inherent_part_defined_{}", count), + def.item.span(), + ); - let maybe_compile_error = if def.inherent.is_none() { - quote! { - compile_error!(concat!( - "`", - stringify!($pallet_name), - "` does not have #[pallet::inherent] defined, perhaps you should \ - remove `Inherent` from construct_runtime?", - )); - } - } else { - TokenStream::new() - }; + let maybe_compile_error = if def.inherent.is_none() { + quote! { + compile_error!(concat!( + "`", + stringify!($pallet_name), + "` does not have #[pallet::inherent] defined, perhaps you should \ + remove `Inherent` from construct_runtime?", + )); + } + } else { + TokenStream::new() + }; - quote! { - #[doc(hidden)] - pub mod __substrate_inherent_check { - #[macro_export] - #[doc(hidden)] - macro_rules! #macro_ident { - ($pallet_name:ident) => { - #maybe_compile_error - } - } + quote! { + #[doc(hidden)] + pub mod __substrate_inherent_check { + #[macro_export] + #[doc(hidden)] + macro_rules! #macro_ident { + ($pallet_name:ident) => { + #maybe_compile_error + } + } - #[doc(hidden)] - pub use #macro_ident as is_inherent_part_defined; - } - } + #[doc(hidden)] + pub use #macro_ident as is_inherent_part_defined; + } + } } diff --git a/support/procedural-fork/src/pallet/expand/instances.rs b/support/procedural-fork/src/pallet/expand/instances.rs index b6dfa7e6d..12423409c 100644 --- a/support/procedural-fork/src/pallet/expand/instances.rs +++ b/support/procedural-fork/src/pallet/expand/instances.rs @@ -22,22 +22,22 @@ use proc_macro2::Span; /// * Provide inherent instance to be used by construct_runtime /// * Provide Instance1 ..= Instance16 for instantiable pallet pub fn expand_instances(def: &mut Def) -> proc_macro2::TokenStream { - let frame_support = &def.frame_support; - let inherent_ident = syn::Ident::new(crate::INHERENT_INSTANCE_NAME, Span::call_site()); - let instances = if def.config.has_instance { - (1..=NUMBER_OF_INSTANCE) - .map(|i| syn::Ident::new(&format!("Instance{}", i), Span::call_site())) - .collect() - } else { - vec![] - }; + let frame_support = &def.frame_support; + let inherent_ident = syn::Ident::new(crate::INHERENT_INSTANCE_NAME, Span::call_site()); + let instances = if def.config.has_instance { + (1..=NUMBER_OF_INSTANCE) + .map(|i| syn::Ident::new(&format!("Instance{}", i), Span::call_site())) + .collect() + } else { + vec![] + }; - quote::quote!( - /// Hidden instance generated to be internally used when module is used without - /// instance. - #[doc(hidden)] - pub type #inherent_ident = (); + quote::quote!( + /// Hidden instance generated to be internally used when module is used without + /// instance. + #[doc(hidden)] + pub type #inherent_ident = (); - #( pub use #frame_support::instances::#instances; )* - ) + #( pub use #frame_support::instances::#instances; )* + ) } diff --git a/support/procedural-fork/src/pallet/expand/mod.rs b/support/procedural-fork/src/pallet/expand/mod.rs index 067839c28..ff4423f85 100644 --- a/support/procedural-fork/src/pallet/expand/mod.rs +++ b/support/procedural-fork/src/pallet/expand/mod.rs @@ -42,12 +42,12 @@ use quote::ToTokens; /// Merge where clause together, `where` token span is taken from the first not none one. pub fn merge_where_clauses(clauses: &[&Option]) -> Option { - let mut clauses = clauses.iter().filter_map(|f| f.as_ref()); - let mut res = clauses.next()?.clone(); - for other in clauses { - res.predicates.extend(other.predicates.iter().cloned()) - } - Some(res) + let mut clauses = clauses.iter().filter_map(|f| f.as_ref()); + let mut res = clauses.next()?.clone(); + for other in clauses { + res.predicates.extend(other.predicates.iter().cloned()) + } + Some(res) } /// Expand definition, in particular: @@ -55,32 +55,32 @@ pub fn merge_where_clauses(clauses: &[&Option]) -> Option proc_macro2::TokenStream { - // Remove the `pallet_doc` attribute first. - let metadata_docs = documentation::expand_documentation(&mut def); - let constants = constants::expand_constants(&mut def); - let pallet_struct = pallet_struct::expand_pallet_struct(&mut def); - let config = config::expand_config(&mut def); - let call = call::expand_call(&mut def); - let tasks = tasks::expand_tasks(&mut def); - let error = error::expand_error(&mut def); - let event = event::expand_event(&mut def); - let storages = storage::expand_storages(&mut def); - let inherents = inherent::expand_inherents(&mut def); - let instances = instances::expand_instances(&mut def); - let hooks = hooks::expand_hooks(&mut def); - let genesis_build = genesis_build::expand_genesis_build(&mut def); - let genesis_config = genesis_config::expand_genesis_config(&mut def); - let type_values = type_value::expand_type_values(&mut def); - let origins = origin::expand_origins(&mut def); - let validate_unsigned = validate_unsigned::expand_validate_unsigned(&mut def); - let tt_default_parts = tt_default_parts::expand_tt_default_parts(&mut def); - let doc_only = doc_only::expand_doc_only(&mut def); - let composites = composite::expand_composites(&mut def); + // Remove the `pallet_doc` attribute first. + let metadata_docs = documentation::expand_documentation(&mut def); + let constants = constants::expand_constants(&mut def); + let pallet_struct = pallet_struct::expand_pallet_struct(&mut def); + let config = config::expand_config(&mut def); + let call = call::expand_call(&mut def); + let tasks = tasks::expand_tasks(&mut def); + let error = error::expand_error(&mut def); + let event = event::expand_event(&mut def); + let storages = storage::expand_storages(&mut def); + let inherents = inherent::expand_inherents(&mut def); + let instances = instances::expand_instances(&mut def); + let hooks = hooks::expand_hooks(&mut def); + let genesis_build = genesis_build::expand_genesis_build(&mut def); + let genesis_config = genesis_config::expand_genesis_config(&mut def); + let type_values = type_value::expand_type_values(&mut def); + let origins = origin::expand_origins(&mut def); + let validate_unsigned = validate_unsigned::expand_validate_unsigned(&mut def); + let tt_default_parts = tt_default_parts::expand_tt_default_parts(&mut def); + let doc_only = doc_only::expand_doc_only(&mut def); + let composites = composite::expand_composites(&mut def); - def.item.attrs.insert( - 0, - syn::parse_quote!( - #[doc = r"The `pallet` module in each FRAME pallet hosts the most important items needed + def.item.attrs.insert( + 0, + syn::parse_quote!( + #[doc = r"The `pallet` module in each FRAME pallet hosts the most important items needed to construct this pallet. The main components of this pallet are: @@ -93,38 +93,38 @@ storage item. Otherwise, all storage items are listed among [*Type Definitions*] - [`Config`], which contains the configuration trait of this pallet. - [`Event`] and [`Error`], which are listed among the [*Enums*](#enums). "] - ), - ); + ), + ); - let new_items = quote::quote!( - #metadata_docs - #constants - #pallet_struct - #config - #call - #tasks - #error - #event - #storages - #inherents - #instances - #hooks - #genesis_build - #genesis_config - #type_values - #origins - #validate_unsigned - #tt_default_parts - #doc_only - #composites - ); + let new_items = quote::quote!( + #metadata_docs + #constants + #pallet_struct + #config + #call + #tasks + #error + #event + #storages + #inherents + #instances + #hooks + #genesis_build + #genesis_config + #type_values + #origins + #validate_unsigned + #tt_default_parts + #doc_only + #composites + ); - def.item - .content - .as_mut() - .expect("This is checked by parsing") - .1 - .push(syn::Item::Verbatim(new_items)); + def.item + .content + .as_mut() + .expect("This is checked by parsing") + .1 + .push(syn::Item::Verbatim(new_items)); - def.item.into_token_stream() + def.item.into_token_stream() } diff --git a/support/procedural-fork/src/pallet/expand/origin.rs b/support/procedural-fork/src/pallet/expand/origin.rs index 55865b424..167445ad6 100644 --- a/support/procedural-fork/src/pallet/expand/origin.rs +++ b/support/procedural-fork/src/pallet/expand/origin.rs @@ -21,35 +21,38 @@ use quote::quote; use syn::{spanned::Spanned, Ident}; pub fn expand_origins(def: &mut Def) -> TokenStream { - let count = COUNTER.with(|counter| counter.borrow_mut().inc()); - let macro_ident = Ident::new(&format!("__is_origin_part_defined_{}", count), def.item.span()); + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + let macro_ident = Ident::new( + &format!("__is_origin_part_defined_{}", count), + def.item.span(), + ); - let maybe_compile_error = if def.origin.is_none() { - quote! { - compile_error!(concat!( - "`", - stringify!($pallet_name), - "` does not have #[pallet::origin] defined, perhaps you should \ - remove `Origin` from construct_runtime?", - )); - } - } else { - TokenStream::new() - }; + let maybe_compile_error = if def.origin.is_none() { + quote! { + compile_error!(concat!( + "`", + stringify!($pallet_name), + "` does not have #[pallet::origin] defined, perhaps you should \ + remove `Origin` from construct_runtime?", + )); + } + } else { + TokenStream::new() + }; - quote! { - #[doc(hidden)] - pub mod __substrate_origin_check { - #[macro_export] - #[doc(hidden)] - macro_rules! #macro_ident { - ($pallet_name:ident) => { - #maybe_compile_error - } - } + quote! { + #[doc(hidden)] + pub mod __substrate_origin_check { + #[macro_export] + #[doc(hidden)] + macro_rules! #macro_ident { + ($pallet_name:ident) => { + #maybe_compile_error + } + } - #[doc(hidden)] - pub use #macro_ident as is_origin_part_defined; - } - } + #[doc(hidden)] + pub use #macro_ident as is_origin_part_defined; + } + } } diff --git a/support/procedural-fork/src/pallet/expand/pallet_struct.rs b/support/procedural-fork/src/pallet/expand/pallet_struct.rs index 7cdf6bde9..c5def65ed 100644 --- a/support/procedural-fork/src/pallet/expand/pallet_struct.rs +++ b/support/procedural-fork/src/pallet/expand/pallet_struct.rs @@ -28,263 +28,275 @@ use frame_support_procedural_tools::get_doc_literals; /// * implementation of `PalletInfoAccess` information /// * implementation of `StorageInfoTrait` on Pallet pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream { - let frame_support = &def.frame_support; - let frame_system = &def.frame_system; - let type_impl_gen = &def.type_impl_generics(def.pallet_struct.attr_span); - let type_use_gen = &def.type_use_generics(def.pallet_struct.attr_span); - let type_decl_gen = &def.type_decl_generics(def.pallet_struct.attr_span); - let pallet_ident = &def.pallet_struct.pallet; - let config_where_clause = &def.config.where_clause; - - let mut storages_where_clauses = vec![&def.config.where_clause]; - storages_where_clauses.extend(def.storages.iter().map(|storage| &storage.where_clause)); - let storages_where_clauses = merge_where_clauses(&storages_where_clauses); - - let pallet_item = { - let pallet_module_items = &mut def.item.content.as_mut().expect("Checked by def").1; - let item = &mut pallet_module_items[def.pallet_struct.index]; - if let syn::Item::Struct(item) = item { - item - } else { - unreachable!("Checked by pallet struct parser") - } - }; - - // If the first field type is `_` then we replace with `PhantomData` - if let Some(field) = pallet_item.fields.iter_mut().next() { - if field.ty == syn::parse_quote!(_) { - field.ty = syn::parse_quote!( - #frame_support::__private::sp_std::marker::PhantomData<(#type_use_gen)> - ); - } - } - - if get_doc_literals(&pallet_item.attrs).is_empty() { - pallet_item.attrs.push(syn::parse_quote!( - #[doc = r" + let frame_support = &def.frame_support; + let frame_system = &def.frame_system; + let type_impl_gen = &def.type_impl_generics(def.pallet_struct.attr_span); + let type_use_gen = &def.type_use_generics(def.pallet_struct.attr_span); + let type_decl_gen = &def.type_decl_generics(def.pallet_struct.attr_span); + let pallet_ident = &def.pallet_struct.pallet; + let config_where_clause = &def.config.where_clause; + + let mut storages_where_clauses = vec![&def.config.where_clause]; + storages_where_clauses.extend(def.storages.iter().map(|storage| &storage.where_clause)); + let storages_where_clauses = merge_where_clauses(&storages_where_clauses); + + let pallet_item = { + let pallet_module_items = &mut def.item.content.as_mut().expect("Checked by def").1; + let item = &mut pallet_module_items[def.pallet_struct.index]; + if let syn::Item::Struct(item) = item { + item + } else { + unreachable!("Checked by pallet struct parser") + } + }; + + // If the first field type is `_` then we replace with `PhantomData` + if let Some(field) = pallet_item.fields.iter_mut().next() { + if field.ty == syn::parse_quote!(_) { + field.ty = syn::parse_quote!( + #frame_support::__private::sp_std::marker::PhantomData<(#type_use_gen)> + ); + } + } + + if get_doc_literals(&pallet_item.attrs).is_empty() { + pallet_item.attrs.push(syn::parse_quote!( + #[doc = r" The `Pallet` struct, the main type that implements traits and standalone functions within the pallet. "] - )); - } - - pallet_item.attrs.push(syn::parse_quote!( - #[derive( - #frame_support::CloneNoBound, - #frame_support::EqNoBound, - #frame_support::PartialEqNoBound, - #frame_support::RuntimeDebugNoBound, - )] - )); - - let pallet_error_metadata = if let Some(error_def) = &def.error { - let error_ident = &error_def.error; - quote::quote_spanned!(def.pallet_struct.attr_span => - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #config_where_clause { - #[doc(hidden)] - pub fn error_metadata() -> Option<#frame_support::__private::metadata_ir::PalletErrorMetadataIR> { - Some(#frame_support::__private::metadata_ir::PalletErrorMetadataIR { - ty: #frame_support::__private::scale_info::meta_type::<#error_ident<#type_use_gen>>() - }) - } - } - ) - } else { - quote::quote_spanned!(def.pallet_struct.attr_span => - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #config_where_clause { - #[doc(hidden)] - pub fn error_metadata() -> Option<#frame_support::__private::metadata_ir::PalletErrorMetadataIR> { - None - } - } - ) - }; - - let storage_info_span = - def.pallet_struct.without_storage_info.unwrap_or(def.pallet_struct.attr_span); - - let storage_names = &def.storages.iter().map(|storage| &storage.ident).collect::>(); - let storage_cfg_attrs = - &def.storages.iter().map(|storage| &storage.cfg_attrs).collect::>(); - - // Depending on the flag `without_storage_info` and the storage attribute `unbounded`, we use - // partial or full storage info from storage. - let storage_info_traits = &def - .storages - .iter() - .map(|storage| { - if storage.unbounded || def.pallet_struct.without_storage_info.is_some() { - quote::quote_spanned!(storage_info_span => PartialStorageInfoTrait) - } else { - quote::quote_spanned!(storage_info_span => StorageInfoTrait) - } - }) - .collect::>(); - - let storage_info_methods = &def - .storages - .iter() - .map(|storage| { - if storage.unbounded || def.pallet_struct.without_storage_info.is_some() { - quote::quote_spanned!(storage_info_span => partial_storage_info) - } else { - quote::quote_spanned!(storage_info_span => storage_info) - } - }) - .collect::>(); - - let storage_info = quote::quote_spanned!(storage_info_span => - impl<#type_impl_gen> #frame_support::traits::StorageInfoTrait - for #pallet_ident<#type_use_gen> - #storages_where_clauses - { - fn storage_info() - -> #frame_support::__private::sp_std::vec::Vec<#frame_support::traits::StorageInfo> - { - #[allow(unused_mut)] - let mut res = #frame_support::__private::sp_std::vec![]; - - #( - #(#storage_cfg_attrs)* - { - let mut storage_info = < - #storage_names<#type_use_gen> - as #frame_support::traits::#storage_info_traits - >::#storage_info_methods(); - res.append(&mut storage_info); - } - )* - - res - } - } - ); - - let (storage_version, in_code_storage_version_ty) = - if let Some(v) = def.pallet_struct.storage_version.as_ref() { - (quote::quote! { #v }, quote::quote! { #frame_support::traits::StorageVersion }) - } else { - ( - quote::quote! { core::default::Default::default() }, - quote::quote! { #frame_support::traits::NoStorageVersionSet }, - ) - }; - - let whitelisted_storage_idents: Vec = def - .storages - .iter() - .filter_map(|s| s.whitelisted.then_some(s.ident.clone())) - .collect(); - - let whitelisted_storage_keys_impl = quote::quote![ - use #frame_support::traits::{StorageInfoTrait, TrackedStorageKey, WhitelistedStorageKeys}; - impl<#type_impl_gen> WhitelistedStorageKeys for #pallet_ident<#type_use_gen> #storages_where_clauses { - fn whitelisted_storage_keys() -> #frame_support::__private::sp_std::vec::Vec { - use #frame_support::__private::sp_std::vec; - vec![#( - TrackedStorageKey::new(#whitelisted_storage_idents::<#type_use_gen>::hashed_key().to_vec()) - ),*] - } - } - ]; - - quote::quote_spanned!(def.pallet_struct.attr_span => - #pallet_error_metadata - - /// Type alias to `Pallet`, to be used by `construct_runtime`. - /// - /// Generated by `pallet` attribute macro. - #[deprecated(note = "use `Pallet` instead")] - #[allow(dead_code)] - pub type Module<#type_decl_gen> = #pallet_ident<#type_use_gen>; - - // Implement `GetStorageVersion` for `Pallet` - impl<#type_impl_gen> #frame_support::traits::GetStorageVersion - for #pallet_ident<#type_use_gen> - #config_where_clause - { - type InCodeStorageVersion = #in_code_storage_version_ty; - - fn in_code_storage_version() -> Self::InCodeStorageVersion { - #storage_version - } - - fn on_chain_storage_version() -> #frame_support::traits::StorageVersion { - #frame_support::traits::StorageVersion::get::() - } - } - - // Implement `OnGenesis` for `Pallet` - impl<#type_impl_gen> #frame_support::traits::OnGenesis - for #pallet_ident<#type_use_gen> - #config_where_clause - { - fn on_genesis() { - let storage_version: #frame_support::traits::StorageVersion = #storage_version; - storage_version.put::(); - } - } - - // Implement `PalletInfoAccess` for `Pallet` - impl<#type_impl_gen> #frame_support::traits::PalletInfoAccess - for #pallet_ident<#type_use_gen> - #config_where_clause - { - fn index() -> usize { - < - ::PalletInfo as #frame_support::traits::PalletInfo - >::index::() - .expect("Pallet is part of the runtime because pallet `Config` trait is \ - implemented by the runtime") - } - - fn name() -> &'static str { - < - ::PalletInfo as #frame_support::traits::PalletInfo - >::name::() - .expect("Pallet is part of the runtime because pallet `Config` trait is \ - implemented by the runtime") - } - - fn name_hash() -> [u8; 16] { - < - ::PalletInfo as #frame_support::traits::PalletInfo - >::name_hash::() - .expect("Pallet is part of the runtime because pallet `Config` trait is \ - implemented by the runtime") - } - - fn module_name() -> &'static str { - < - ::PalletInfo as #frame_support::traits::PalletInfo - >::module_name::() - .expect("Pallet is part of the runtime because pallet `Config` trait is \ - implemented by the runtime") - } - - fn crate_version() -> #frame_support::traits::CrateVersion { - #frame_support::crate_to_crate_version!() - } - } - - impl<#type_impl_gen> #frame_support::traits::PalletsInfoAccess - for #pallet_ident<#type_use_gen> - #config_where_clause - { - fn count() -> usize { 1 } - fn infos() -> #frame_support::__private::sp_std::vec::Vec<#frame_support::traits::PalletInfoData> { - use #frame_support::traits::PalletInfoAccess; - let item = #frame_support::traits::PalletInfoData { - index: Self::index(), - name: Self::name(), - module_name: Self::module_name(), - crate_version: Self::crate_version(), - }; - #frame_support::__private::sp_std::vec![item] - } - } - - #storage_info - #whitelisted_storage_keys_impl - ) + )); + } + + pallet_item.attrs.push(syn::parse_quote!( + #[derive( + #frame_support::CloneNoBound, + #frame_support::EqNoBound, + #frame_support::PartialEqNoBound, + #frame_support::RuntimeDebugNoBound, + )] + )); + + let pallet_error_metadata = if let Some(error_def) = &def.error { + let error_ident = &error_def.error; + quote::quote_spanned!(def.pallet_struct.attr_span => + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #config_where_clause { + #[doc(hidden)] + pub fn error_metadata() -> Option<#frame_support::__private::metadata_ir::PalletErrorMetadataIR> { + Some(#frame_support::__private::metadata_ir::PalletErrorMetadataIR { + ty: #frame_support::__private::scale_info::meta_type::<#error_ident<#type_use_gen>>() + }) + } + } + ) + } else { + quote::quote_spanned!(def.pallet_struct.attr_span => + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #config_where_clause { + #[doc(hidden)] + pub fn error_metadata() -> Option<#frame_support::__private::metadata_ir::PalletErrorMetadataIR> { + None + } + } + ) + }; + + let storage_info_span = def + .pallet_struct + .without_storage_info + .unwrap_or(def.pallet_struct.attr_span); + + let storage_names = &def + .storages + .iter() + .map(|storage| &storage.ident) + .collect::>(); + let storage_cfg_attrs = &def + .storages + .iter() + .map(|storage| &storage.cfg_attrs) + .collect::>(); + + // Depending on the flag `without_storage_info` and the storage attribute `unbounded`, we use + // partial or full storage info from storage. + let storage_info_traits = &def + .storages + .iter() + .map(|storage| { + if storage.unbounded || def.pallet_struct.without_storage_info.is_some() { + quote::quote_spanned!(storage_info_span => PartialStorageInfoTrait) + } else { + quote::quote_spanned!(storage_info_span => StorageInfoTrait) + } + }) + .collect::>(); + + let storage_info_methods = &def + .storages + .iter() + .map(|storage| { + if storage.unbounded || def.pallet_struct.without_storage_info.is_some() { + quote::quote_spanned!(storage_info_span => partial_storage_info) + } else { + quote::quote_spanned!(storage_info_span => storage_info) + } + }) + .collect::>(); + + let storage_info = quote::quote_spanned!(storage_info_span => + impl<#type_impl_gen> #frame_support::traits::StorageInfoTrait + for #pallet_ident<#type_use_gen> + #storages_where_clauses + { + fn storage_info() + -> #frame_support::__private::sp_std::vec::Vec<#frame_support::traits::StorageInfo> + { + #[allow(unused_mut)] + let mut res = #frame_support::__private::sp_std::vec![]; + + #( + #(#storage_cfg_attrs)* + { + let mut storage_info = < + #storage_names<#type_use_gen> + as #frame_support::traits::#storage_info_traits + >::#storage_info_methods(); + res.append(&mut storage_info); + } + )* + + res + } + } + ); + + let (storage_version, in_code_storage_version_ty) = + if let Some(v) = def.pallet_struct.storage_version.as_ref() { + ( + quote::quote! { #v }, + quote::quote! { #frame_support::traits::StorageVersion }, + ) + } else { + ( + quote::quote! { core::default::Default::default() }, + quote::quote! { #frame_support::traits::NoStorageVersionSet }, + ) + }; + + let whitelisted_storage_idents: Vec = def + .storages + .iter() + .filter_map(|s| s.whitelisted.then_some(s.ident.clone())) + .collect(); + + let whitelisted_storage_keys_impl = quote::quote![ + use #frame_support::traits::{StorageInfoTrait, TrackedStorageKey, WhitelistedStorageKeys}; + impl<#type_impl_gen> WhitelistedStorageKeys for #pallet_ident<#type_use_gen> #storages_where_clauses { + fn whitelisted_storage_keys() -> #frame_support::__private::sp_std::vec::Vec { + use #frame_support::__private::sp_std::vec; + vec![#( + TrackedStorageKey::new(#whitelisted_storage_idents::<#type_use_gen>::hashed_key().to_vec()) + ),*] + } + } + ]; + + quote::quote_spanned!(def.pallet_struct.attr_span => + #pallet_error_metadata + + /// Type alias to `Pallet`, to be used by `construct_runtime`. + /// + /// Generated by `pallet` attribute macro. + #[deprecated(note = "use `Pallet` instead")] + #[allow(dead_code)] + pub type Module<#type_decl_gen> = #pallet_ident<#type_use_gen>; + + // Implement `GetStorageVersion` for `Pallet` + impl<#type_impl_gen> #frame_support::traits::GetStorageVersion + for #pallet_ident<#type_use_gen> + #config_where_clause + { + type InCodeStorageVersion = #in_code_storage_version_ty; + + fn in_code_storage_version() -> Self::InCodeStorageVersion { + #storage_version + } + + fn on_chain_storage_version() -> #frame_support::traits::StorageVersion { + #frame_support::traits::StorageVersion::get::() + } + } + + // Implement `OnGenesis` for `Pallet` + impl<#type_impl_gen> #frame_support::traits::OnGenesis + for #pallet_ident<#type_use_gen> + #config_where_clause + { + fn on_genesis() { + let storage_version: #frame_support::traits::StorageVersion = #storage_version; + storage_version.put::(); + } + } + + // Implement `PalletInfoAccess` for `Pallet` + impl<#type_impl_gen> #frame_support::traits::PalletInfoAccess + for #pallet_ident<#type_use_gen> + #config_where_clause + { + fn index() -> usize { + < + ::PalletInfo as #frame_support::traits::PalletInfo + >::index::() + .expect("Pallet is part of the runtime because pallet `Config` trait is \ + implemented by the runtime") + } + + fn name() -> &'static str { + < + ::PalletInfo as #frame_support::traits::PalletInfo + >::name::() + .expect("Pallet is part of the runtime because pallet `Config` trait is \ + implemented by the runtime") + } + + fn name_hash() -> [u8; 16] { + < + ::PalletInfo as #frame_support::traits::PalletInfo + >::name_hash::() + .expect("Pallet is part of the runtime because pallet `Config` trait is \ + implemented by the runtime") + } + + fn module_name() -> &'static str { + < + ::PalletInfo as #frame_support::traits::PalletInfo + >::module_name::() + .expect("Pallet is part of the runtime because pallet `Config` trait is \ + implemented by the runtime") + } + + fn crate_version() -> #frame_support::traits::CrateVersion { + #frame_support::crate_to_crate_version!() + } + } + + impl<#type_impl_gen> #frame_support::traits::PalletsInfoAccess + for #pallet_ident<#type_use_gen> + #config_where_clause + { + fn count() -> usize { 1 } + fn infos() -> #frame_support::__private::sp_std::vec::Vec<#frame_support::traits::PalletInfoData> { + use #frame_support::traits::PalletInfoAccess; + let item = #frame_support::traits::PalletInfoData { + index: Self::index(), + name: Self::name(), + module_name: Self::module_name(), + crate_version: Self::crate_version(), + }; + #frame_support::__private::sp_std::vec![item] + } + } + + #storage_info + #whitelisted_storage_keys_impl + ) } diff --git a/support/procedural-fork/src/pallet/expand/storage.rs b/support/procedural-fork/src/pallet/expand/storage.rs index 937b068cf..b77e9846b 100644 --- a/support/procedural-fork/src/pallet/expand/storage.rs +++ b/support/procedural-fork/src/pallet/expand/storage.rs @@ -16,14 +16,14 @@ // limitations under the License. use crate::{ - counter_prefix, - pallet::{ - parse::{ - helper::two128_str, - storage::{Metadata, QueryKind, StorageDef, StorageGenerics}, - }, - Def, - }, + counter_prefix, + pallet::{ + parse::{ + helper::two128_str, + storage::{Metadata, QueryKind, StorageDef, StorageGenerics}, + }, + Def, + }, }; use quote::ToTokens; use std::{collections::HashMap, ops::IndexMut}; @@ -32,73 +32,76 @@ use syn::spanned::Spanned; /// Generate the prefix_ident related to the storage. /// prefix_ident is used for the prefix struct to be given to storage as first generic param. fn prefix_ident(storage: &StorageDef) -> syn::Ident { - let storage_ident = &storage.ident; - syn::Ident::new(&format!("_GeneratedPrefixForStorage{}", storage_ident), storage_ident.span()) + let storage_ident = &storage.ident; + syn::Ident::new( + &format!("_GeneratedPrefixForStorage{}", storage_ident), + storage_ident.span(), + ) } /// Generate the counter_prefix_ident related to the storage. /// counter_prefix_ident is used for the prefix struct to be given to counted storage map. fn counter_prefix_ident(storage_ident: &syn::Ident) -> syn::Ident { - syn::Ident::new( - &format!("_GeneratedCounterPrefixForStorage{}", storage_ident), - storage_ident.span(), - ) + syn::Ident::new( + &format!("_GeneratedCounterPrefixForStorage{}", storage_ident), + storage_ident.span(), + ) } /// Check for duplicated storage prefixes. This step is necessary since users can specify an /// alternative storage prefix using the #[pallet::storage_prefix] syntax, and we need to ensure /// that the prefix specified by the user is not a duplicate of an existing one. fn check_prefix_duplicates( - storage_def: &StorageDef, - // A hashmap of all already used prefix and their associated error if duplication - used_prefixes: &mut HashMap, + storage_def: &StorageDef, + // A hashmap of all already used prefix and their associated error if duplication + used_prefixes: &mut HashMap, ) -> syn::Result<()> { - let prefix = storage_def.prefix(); - let dup_err = syn::Error::new( - storage_def.prefix_span(), - format!("Duplicate storage prefixes found for `{}`", prefix), - ); - - if let Some(other_dup_err) = used_prefixes.insert(prefix.clone(), dup_err.clone()) { - let mut err = dup_err; - err.combine(other_dup_err); - return Err(err) - } - - if let Metadata::CountedMap { .. } = storage_def.metadata { - let counter_prefix = counter_prefix(&prefix); - let counter_dup_err = syn::Error::new( - storage_def.prefix_span(), - format!( - "Duplicate storage prefixes found for `{}`, used for counter associated to \ + let prefix = storage_def.prefix(); + let dup_err = syn::Error::new( + storage_def.prefix_span(), + format!("Duplicate storage prefixes found for `{}`", prefix), + ); + + if let Some(other_dup_err) = used_prefixes.insert(prefix.clone(), dup_err.clone()) { + let mut err = dup_err; + err.combine(other_dup_err); + return Err(err); + } + + if let Metadata::CountedMap { .. } = storage_def.metadata { + let counter_prefix = counter_prefix(&prefix); + let counter_dup_err = syn::Error::new( + storage_def.prefix_span(), + format!( + "Duplicate storage prefixes found for `{}`, used for counter associated to \ counted storage map", - counter_prefix, - ), - ); - - if let Some(other_dup_err) = used_prefixes.insert(counter_prefix, counter_dup_err.clone()) { - let mut err = counter_dup_err; - err.combine(other_dup_err); - return Err(err) - } - } - - Ok(()) + counter_prefix, + ), + ); + + if let Some(other_dup_err) = used_prefixes.insert(counter_prefix, counter_dup_err.clone()) { + let mut err = counter_dup_err; + err.combine(other_dup_err); + return Err(err); + } + } + + Ok(()) } pub struct ResultOnEmptyStructMetadata { - /// The Rust ident that is going to be used as the name of the OnEmpty struct. - pub name: syn::Ident, - /// The path to the error type being returned by the ResultQuery. - pub error_path: syn::Path, - /// The visibility of the OnEmpty struct. - pub visibility: syn::Visibility, - /// The type of the storage item. - pub value_ty: syn::Type, - /// The name of the pallet error enum variant that is going to be returned. - pub variant_name: syn::Ident, - /// The span used to report compilation errors about the OnEmpty struct. - pub span: proc_macro2::Span, + /// The Rust ident that is going to be used as the name of the OnEmpty struct. + pub name: syn::Ident, + /// The path to the error type being returned by the ResultQuery. + pub error_path: syn::Path, + /// The visibility of the OnEmpty struct. + pub visibility: syn::Visibility, + /// The type of the storage item. + pub value_ty: syn::Type, + /// The name of the pallet error enum variant that is going to be returned. + pub variant_name: syn::Ident, + /// The span used to report compilation errors about the OnEmpty struct. + pub span: proc_macro2::Span, } /// @@ -106,277 +109,305 @@ pub struct ResultOnEmptyStructMetadata { /// * if generics are named: reorder the generic, remove their name, and add the missing ones. /// * Add `#[allow(type_alias_bounds)]` pub fn process_generics(def: &mut Def) -> syn::Result> { - let frame_support = &def.frame_support; - let mut on_empty_struct_metadata = Vec::new(); - - for storage_def in def.storages.iter_mut() { - let item = &mut def.item.content.as_mut().expect("Checked by def").1[storage_def.index]; - - let typ_item = match item { - syn::Item::Type(t) => t, - _ => unreachable!("Checked by def"), - }; - - typ_item.attrs.push(syn::parse_quote!(#[allow(type_alias_bounds)])); - - let typ_path = match &mut *typ_item.ty { - syn::Type::Path(p) => p, - _ => unreachable!("Checked by def"), - }; - - let args = match &mut typ_path.path.segments[0].arguments { - syn::PathArguments::AngleBracketed(args) => args, - _ => unreachable!("Checked by def"), - }; - - let prefix_ident = prefix_ident(storage_def); - let type_use_gen = if def.config.has_instance { - quote::quote_spanned!(storage_def.attr_span => T, I) - } else { - quote::quote_spanned!(storage_def.attr_span => T) - }; - - let default_query_kind: syn::Type = - syn::parse_quote!(#frame_support::storage::types::OptionQuery); - let mut default_on_empty = |value_ty: syn::Type| -> syn::Type { - if let Some(QueryKind::ResultQuery(error_path, variant_name)) = - storage_def.query_kind.as_ref() - { - let on_empty_ident = - quote::format_ident!("__Frame_Internal_Get{}Result", storage_def.ident); - on_empty_struct_metadata.push(ResultOnEmptyStructMetadata { - name: on_empty_ident.clone(), - visibility: storage_def.vis.clone(), - value_ty, - error_path: error_path.clone(), - variant_name: variant_name.clone(), - span: storage_def.attr_span, - }); - return syn::parse_quote!(#on_empty_ident) - } - syn::parse_quote!(#frame_support::traits::GetDefault) - }; - let default_max_values: syn::Type = syn::parse_quote!(#frame_support::traits::GetDefault); - - let set_result_query_type_parameter = |query_type: &mut syn::Type| -> syn::Result<()> { - if let Some(QueryKind::ResultQuery(error_path, _)) = storage_def.query_kind.as_ref() { - if let syn::Type::Path(syn::TypePath { path: syn::Path { segments, .. }, .. }) = - query_type - { - if let Some(seg) = segments.last_mut() { - if let syn::PathArguments::AngleBracketed( - syn::AngleBracketedGenericArguments { args, .. }, - ) = &mut seg.arguments - { - args.clear(); - args.push(syn::GenericArgument::Type(syn::parse_quote!(#error_path))); - } - } - } else { - let msg = format!( - "Invalid pallet::storage, unexpected type for query, expected ResultQuery \ + let frame_support = &def.frame_support; + let mut on_empty_struct_metadata = Vec::new(); + + for storage_def in def.storages.iter_mut() { + let item = &mut def.item.content.as_mut().expect("Checked by def").1[storage_def.index]; + + let typ_item = match item { + syn::Item::Type(t) => t, + _ => unreachable!("Checked by def"), + }; + + typ_item + .attrs + .push(syn::parse_quote!(#[allow(type_alias_bounds)])); + + let typ_path = match &mut *typ_item.ty { + syn::Type::Path(p) => p, + _ => unreachable!("Checked by def"), + }; + + let args = match &mut typ_path.path.segments[0].arguments { + syn::PathArguments::AngleBracketed(args) => args, + _ => unreachable!("Checked by def"), + }; + + let prefix_ident = prefix_ident(storage_def); + let type_use_gen = if def.config.has_instance { + quote::quote_spanned!(storage_def.attr_span => T, I) + } else { + quote::quote_spanned!(storage_def.attr_span => T) + }; + + let default_query_kind: syn::Type = + syn::parse_quote!(#frame_support::storage::types::OptionQuery); + let mut default_on_empty = |value_ty: syn::Type| -> syn::Type { + if let Some(QueryKind::ResultQuery(error_path, variant_name)) = + storage_def.query_kind.as_ref() + { + let on_empty_ident = + quote::format_ident!("__Frame_Internal_Get{}Result", storage_def.ident); + on_empty_struct_metadata.push(ResultOnEmptyStructMetadata { + name: on_empty_ident.clone(), + visibility: storage_def.vis.clone(), + value_ty, + error_path: error_path.clone(), + variant_name: variant_name.clone(), + span: storage_def.attr_span, + }); + return syn::parse_quote!(#on_empty_ident); + } + syn::parse_quote!(#frame_support::traits::GetDefault) + }; + let default_max_values: syn::Type = syn::parse_quote!(#frame_support::traits::GetDefault); + + let set_result_query_type_parameter = |query_type: &mut syn::Type| -> syn::Result<()> { + if let Some(QueryKind::ResultQuery(error_path, _)) = storage_def.query_kind.as_ref() { + if let syn::Type::Path(syn::TypePath { + path: syn::Path { segments, .. }, + .. + }) = query_type + { + if let Some(seg) = segments.last_mut() { + if let syn::PathArguments::AngleBracketed( + syn::AngleBracketedGenericArguments { args, .. }, + ) = &mut seg.arguments + { + args.clear(); + args.push(syn::GenericArgument::Type(syn::parse_quote!(#error_path))); + } + } + } else { + let msg = format!( + "Invalid pallet::storage, unexpected type for query, expected ResultQuery \ with 1 type parameter, found `{}`", - query_type.to_token_stream().to_string() - ); - return Err(syn::Error::new(query_type.span(), msg)) - } - } - Ok(()) - }; - - if let Some(named_generics) = storage_def.named_generics.clone() { - args.args.clear(); - args.args.push(syn::parse_quote!( #prefix_ident<#type_use_gen> )); - match named_generics { - StorageGenerics::Value { value, query_kind, on_empty } => { - args.args.push(syn::GenericArgument::Type(value.clone())); - let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone()); - set_result_query_type_parameter(&mut query_kind)?; - args.args.push(syn::GenericArgument::Type(query_kind)); - let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value)); - args.args.push(syn::GenericArgument::Type(on_empty)); - }, - StorageGenerics::Map { hasher, key, value, query_kind, on_empty, max_values } | - StorageGenerics::CountedMap { - hasher, - key, - value, - query_kind, - on_empty, - max_values, - } => { - args.args.push(syn::GenericArgument::Type(hasher)); - args.args.push(syn::GenericArgument::Type(key)); - args.args.push(syn::GenericArgument::Type(value.clone())); - let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone()); - set_result_query_type_parameter(&mut query_kind)?; - args.args.push(syn::GenericArgument::Type(query_kind)); - let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value)); - args.args.push(syn::GenericArgument::Type(on_empty)); - let max_values = max_values.unwrap_or_else(|| default_max_values.clone()); - args.args.push(syn::GenericArgument::Type(max_values)); - }, - StorageGenerics::DoubleMap { - hasher1, - key1, - hasher2, - key2, - value, - query_kind, - on_empty, - max_values, - } => { - args.args.push(syn::GenericArgument::Type(hasher1)); - args.args.push(syn::GenericArgument::Type(key1)); - args.args.push(syn::GenericArgument::Type(hasher2)); - args.args.push(syn::GenericArgument::Type(key2)); - args.args.push(syn::GenericArgument::Type(value.clone())); - let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone()); - set_result_query_type_parameter(&mut query_kind)?; - args.args.push(syn::GenericArgument::Type(query_kind)); - let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value)); - args.args.push(syn::GenericArgument::Type(on_empty)); - let max_values = max_values.unwrap_or_else(|| default_max_values.clone()); - args.args.push(syn::GenericArgument::Type(max_values)); - }, - StorageGenerics::NMap { keygen, value, query_kind, on_empty, max_values } | - StorageGenerics::CountedNMap { - keygen, - value, - query_kind, - on_empty, - max_values, - } => { - args.args.push(syn::GenericArgument::Type(keygen)); - args.args.push(syn::GenericArgument::Type(value.clone())); - let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone()); - set_result_query_type_parameter(&mut query_kind)?; - args.args.push(syn::GenericArgument::Type(query_kind)); - let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value)); - args.args.push(syn::GenericArgument::Type(on_empty)); - let max_values = max_values.unwrap_or_else(|| default_max_values.clone()); - args.args.push(syn::GenericArgument::Type(max_values)); - }, - } - } else { - args.args[0] = syn::parse_quote!( #prefix_ident<#type_use_gen> ); - - let (value_idx, query_idx, on_empty_idx) = match storage_def.metadata { - Metadata::Value { .. } => (1, 2, 3), - Metadata::NMap { .. } | Metadata::CountedNMap { .. } => (2, 3, 4), - Metadata::Map { .. } | Metadata::CountedMap { .. } => (3, 4, 5), - Metadata::DoubleMap { .. } => (5, 6, 7), - }; - - if storage_def.use_default_hasher { - let hasher_indices: Vec = match storage_def.metadata { - Metadata::Map { .. } | Metadata::CountedMap { .. } => vec![1], - Metadata::DoubleMap { .. } => vec![1, 3], - _ => vec![], - }; - for hasher_idx in hasher_indices { - args.args[hasher_idx] = syn::GenericArgument::Type( - syn::parse_quote!(#frame_support::Blake2_128Concat), - ); - } - } - - if query_idx < args.args.len() { - if let syn::GenericArgument::Type(query_kind) = args.args.index_mut(query_idx) { - set_result_query_type_parameter(query_kind)?; - } - } else if let Some(QueryKind::ResultQuery(error_path, _)) = - storage_def.query_kind.as_ref() - { - args.args.push(syn::GenericArgument::Type(syn::parse_quote!(#error_path))) - } - - // Here, we only need to check if OnEmpty is *not* specified, and if so, then we have to - // generate a default OnEmpty struct for it. - if on_empty_idx >= args.args.len() && - matches!(storage_def.query_kind.as_ref(), Some(QueryKind::ResultQuery(_, _))) - { - let value_ty = match args.args[value_idx].clone() { - syn::GenericArgument::Type(ty) => ty, - _ => unreachable!(), - }; - let on_empty = default_on_empty(value_ty); - args.args.push(syn::GenericArgument::Type(on_empty)); - } - } - } - - Ok(on_empty_struct_metadata) + query_type.to_token_stream().to_string() + ); + return Err(syn::Error::new(query_type.span(), msg)); + } + } + Ok(()) + }; + + if let Some(named_generics) = storage_def.named_generics.clone() { + args.args.clear(); + args.args + .push(syn::parse_quote!( #prefix_ident<#type_use_gen> )); + match named_generics { + StorageGenerics::Value { + value, + query_kind, + on_empty, + } => { + args.args.push(syn::GenericArgument::Type(value.clone())); + let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone()); + set_result_query_type_parameter(&mut query_kind)?; + args.args.push(syn::GenericArgument::Type(query_kind)); + let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value)); + args.args.push(syn::GenericArgument::Type(on_empty)); + } + StorageGenerics::Map { + hasher, + key, + value, + query_kind, + on_empty, + max_values, + } + | StorageGenerics::CountedMap { + hasher, + key, + value, + query_kind, + on_empty, + max_values, + } => { + args.args.push(syn::GenericArgument::Type(hasher)); + args.args.push(syn::GenericArgument::Type(key)); + args.args.push(syn::GenericArgument::Type(value.clone())); + let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone()); + set_result_query_type_parameter(&mut query_kind)?; + args.args.push(syn::GenericArgument::Type(query_kind)); + let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value)); + args.args.push(syn::GenericArgument::Type(on_empty)); + let max_values = max_values.unwrap_or_else(|| default_max_values.clone()); + args.args.push(syn::GenericArgument::Type(max_values)); + } + StorageGenerics::DoubleMap { + hasher1, + key1, + hasher2, + key2, + value, + query_kind, + on_empty, + max_values, + } => { + args.args.push(syn::GenericArgument::Type(hasher1)); + args.args.push(syn::GenericArgument::Type(key1)); + args.args.push(syn::GenericArgument::Type(hasher2)); + args.args.push(syn::GenericArgument::Type(key2)); + args.args.push(syn::GenericArgument::Type(value.clone())); + let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone()); + set_result_query_type_parameter(&mut query_kind)?; + args.args.push(syn::GenericArgument::Type(query_kind)); + let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value)); + args.args.push(syn::GenericArgument::Type(on_empty)); + let max_values = max_values.unwrap_or_else(|| default_max_values.clone()); + args.args.push(syn::GenericArgument::Type(max_values)); + } + StorageGenerics::NMap { + keygen, + value, + query_kind, + on_empty, + max_values, + } + | StorageGenerics::CountedNMap { + keygen, + value, + query_kind, + on_empty, + max_values, + } => { + args.args.push(syn::GenericArgument::Type(keygen)); + args.args.push(syn::GenericArgument::Type(value.clone())); + let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone()); + set_result_query_type_parameter(&mut query_kind)?; + args.args.push(syn::GenericArgument::Type(query_kind)); + let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value)); + args.args.push(syn::GenericArgument::Type(on_empty)); + let max_values = max_values.unwrap_or_else(|| default_max_values.clone()); + args.args.push(syn::GenericArgument::Type(max_values)); + } + } + } else { + args.args[0] = syn::parse_quote!( #prefix_ident<#type_use_gen> ); + + let (value_idx, query_idx, on_empty_idx) = match storage_def.metadata { + Metadata::Value { .. } => (1, 2, 3), + Metadata::NMap { .. } | Metadata::CountedNMap { .. } => (2, 3, 4), + Metadata::Map { .. } | Metadata::CountedMap { .. } => (3, 4, 5), + Metadata::DoubleMap { .. } => (5, 6, 7), + }; + + if storage_def.use_default_hasher { + let hasher_indices: Vec = match storage_def.metadata { + Metadata::Map { .. } | Metadata::CountedMap { .. } => vec![1], + Metadata::DoubleMap { .. } => vec![1, 3], + _ => vec![], + }; + for hasher_idx in hasher_indices { + args.args[hasher_idx] = syn::GenericArgument::Type( + syn::parse_quote!(#frame_support::Blake2_128Concat), + ); + } + } + + if query_idx < args.args.len() { + if let syn::GenericArgument::Type(query_kind) = args.args.index_mut(query_idx) { + set_result_query_type_parameter(query_kind)?; + } + } else if let Some(QueryKind::ResultQuery(error_path, _)) = + storage_def.query_kind.as_ref() + { + args.args + .push(syn::GenericArgument::Type(syn::parse_quote!(#error_path))) + } + + // Here, we only need to check if OnEmpty is *not* specified, and if so, then we have to + // generate a default OnEmpty struct for it. + if on_empty_idx >= args.args.len() + && matches!( + storage_def.query_kind.as_ref(), + Some(QueryKind::ResultQuery(_, _)) + ) + { + let value_ty = match args.args[value_idx].clone() { + syn::GenericArgument::Type(ty) => ty, + _ => unreachable!(), + }; + let on_empty = default_on_empty(value_ty); + args.args.push(syn::GenericArgument::Type(on_empty)); + } + } + } + + Ok(on_empty_struct_metadata) } fn augment_final_docs(def: &mut Def) { - // expand the docs with a new line showing the storage type (value, map, double map, etc), and - // the key/value type(s). - let mut push_string_literal = |doc_line: &str, storage: &mut StorageDef| { - let item = &mut def.item.content.as_mut().expect("Checked by def").1[storage.index]; - let typ_item = match item { - syn::Item::Type(t) => t, - _ => unreachable!("Checked by def"), - }; - typ_item.attrs.push(syn::parse_quote!(#[doc = ""])); - typ_item.attrs.push(syn::parse_quote!(#[doc = #doc_line])); - }; - def.storages.iter_mut().for_each(|storage| match &storage.metadata { - Metadata::Value { value } => { - let doc_line = format!( - "Storage type is [`StorageValue`] with value type `{}`.", - value.to_token_stream() - ); - push_string_literal(&doc_line, storage); - }, - Metadata::Map { key, value } => { - let doc_line = format!( - "Storage type is [`StorageMap`] with key type `{}` and value type `{}`.", - key.to_token_stream(), - value.to_token_stream() - ); - push_string_literal(&doc_line, storage); - }, - Metadata::DoubleMap { key1, key2, value } => { - let doc_line = format!( + // expand the docs with a new line showing the storage type (value, map, double map, etc), and + // the key/value type(s). + let mut push_string_literal = |doc_line: &str, storage: &mut StorageDef| { + let item = &mut def.item.content.as_mut().expect("Checked by def").1[storage.index]; + let typ_item = match item { + syn::Item::Type(t) => t, + _ => unreachable!("Checked by def"), + }; + typ_item.attrs.push(syn::parse_quote!(#[doc = ""])); + typ_item.attrs.push(syn::parse_quote!(#[doc = #doc_line])); + }; + def.storages + .iter_mut() + .for_each(|storage| match &storage.metadata { + Metadata::Value { value } => { + let doc_line = format!( + "Storage type is [`StorageValue`] with value type `{}`.", + value.to_token_stream() + ); + push_string_literal(&doc_line, storage); + } + Metadata::Map { key, value } => { + let doc_line = format!( + "Storage type is [`StorageMap`] with key type `{}` and value type `{}`.", + key.to_token_stream(), + value.to_token_stream() + ); + push_string_literal(&doc_line, storage); + } + Metadata::DoubleMap { key1, key2, value } => { + let doc_line = format!( "Storage type is [`StorageDoubleMap`] with key1 type {}, key2 type {} and value type {}.", key1.to_token_stream(), key2.to_token_stream(), value.to_token_stream() ); - push_string_literal(&doc_line, storage); - }, - Metadata::NMap { keys, value, .. } => { - let doc_line = format!( - "Storage type is [`StorageNMap`] with keys type ({}) and value type {}.", - keys.iter() - .map(|k| k.to_token_stream().to_string()) - .collect::>() - .join(", "), - value.to_token_stream() - ); - push_string_literal(&doc_line, storage); - }, - Metadata::CountedNMap { keys, value, .. } => { - let doc_line = format!( - "Storage type is [`CountedStorageNMap`] with keys type ({}) and value type {}.", - keys.iter() - .map(|k| k.to_token_stream().to_string()) - .collect::>() - .join(", "), - value.to_token_stream() - ); - push_string_literal(&doc_line, storage); - }, - Metadata::CountedMap { key, value } => { - let doc_line = format!( - "Storage type is [`CountedStorageMap`] with key type {} and value type {}.", - key.to_token_stream(), - value.to_token_stream() - ); - push_string_literal(&doc_line, storage); - }, - }); + push_string_literal(&doc_line, storage); + } + Metadata::NMap { keys, value, .. } => { + let doc_line = format!( + "Storage type is [`StorageNMap`] with keys type ({}) and value type {}.", + keys.iter() + .map(|k| k.to_token_stream().to_string()) + .collect::>() + .join(", "), + value.to_token_stream() + ); + push_string_literal(&doc_line, storage); + } + Metadata::CountedNMap { keys, value, .. } => { + let doc_line = format!( + "Storage type is [`CountedStorageNMap`] with keys type ({}) and value type {}.", + keys.iter() + .map(|k| k.to_token_stream().to_string()) + .collect::>() + .join(", "), + value.to_token_stream() + ); + push_string_literal(&doc_line, storage); + } + Metadata::CountedMap { key, value } => { + let doc_line = format!( + "Storage type is [`CountedStorageMap`] with key type {} and value type {}.", + key.to_token_stream(), + value.to_token_stream() + ); + push_string_literal(&doc_line, storage); + } + }); } /// @@ -387,29 +418,29 @@ fn augment_final_docs(def: &mut Def) { /// * Add `#[allow(type_alias_bounds)]` on storages type alias /// * generate metadatas pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { - let on_empty_struct_metadata = match process_generics(def) { - Ok(idents) => idents, - Err(e) => return e.into_compile_error(), - }; - - augment_final_docs(def); - - // Check for duplicate prefixes - let mut prefix_set = HashMap::new(); - let mut errors = def - .storages - .iter() - .filter_map(|storage_def| check_prefix_duplicates(storage_def, &mut prefix_set).err()); - if let Some(mut final_error) = errors.next() { - errors.for_each(|error| final_error.combine(error)); - return final_error.into_compile_error() - } - - let frame_support = &def.frame_support; - let frame_system = &def.frame_system; - let pallet_ident = &def.pallet_struct.pallet; - - let entries_builder = def.storages.iter().map(|storage| { + let on_empty_struct_metadata = match process_generics(def) { + Ok(idents) => idents, + Err(e) => return e.into_compile_error(), + }; + + augment_final_docs(def); + + // Check for duplicate prefixes + let mut prefix_set = HashMap::new(); + let mut errors = def + .storages + .iter() + .filter_map(|storage_def| check_prefix_duplicates(storage_def, &mut prefix_set).err()); + if let Some(mut final_error) = errors.next() { + errors.for_each(|error| final_error.combine(error)); + return final_error.into_compile_error(); + } + + let frame_support = &def.frame_support; + let frame_system = &def.frame_system; + let pallet_ident = &def.pallet_struct.pallet; + + let entries_builder = def.storages.iter().map(|storage| { let no_docs = vec![]; let docs = if cfg!(feature = "no-metadata-docs") { &no_docs } else { &storage.docs }; @@ -432,202 +463,202 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { ) }); - let getters = def.storages.iter().map(|storage| { - if let Some(getter) = &storage.getter { - let completed_where_clause = - super::merge_where_clauses(&[&storage.where_clause, &def.config.where_clause]); - - let ident = &storage.ident; - let gen = &def.type_use_generics(storage.attr_span); - let type_impl_gen = &def.type_impl_generics(storage.attr_span); - let type_use_gen = &def.type_use_generics(storage.attr_span); - let full_ident = quote::quote_spanned!(storage.attr_span => #ident<#gen> ); - - let cfg_attrs = &storage.cfg_attrs; - - // If the storage item is public, link it and otherwise just mention it. - // - // We can not just copy the docs from a non-public type as it may links to internal - // types which makes the compiler very unhappy :( - let getter_doc_line = if matches!(storage.vis, syn::Visibility::Public(_)) { - format!("An auto-generated getter for [`{}`].", storage.ident) - } else { - format!("An auto-generated getter for `{}`.", storage.ident) - }; - - match &storage.metadata { - Metadata::Value { value } => { - let query = match storage.query_kind.as_ref().expect("Checked by def") { - QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => - Option<#value> - ), - QueryKind::ResultQuery(error_path, _) => { - quote::quote_spanned!(storage.attr_span => - Result<#value, #error_path> - ) - }, - QueryKind::ValueQuery => quote::quote!(#value), - }; - quote::quote_spanned!(storage.attr_span => - #(#cfg_attrs)* - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { - #[doc = #getter_doc_line] - pub fn #getter() -> #query { - < - #full_ident as #frame_support::storage::StorageValue<#value> - >::get() - } - } - ) - }, - Metadata::Map { key, value } => { - let query = match storage.query_kind.as_ref().expect("Checked by def") { - QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => - Option<#value> - ), - QueryKind::ResultQuery(error_path, _) => { - quote::quote_spanned!(storage.attr_span => - Result<#value, #error_path> - ) - }, - QueryKind::ValueQuery => quote::quote!(#value), - }; - quote::quote_spanned!(storage.attr_span => - #(#cfg_attrs)* - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { - #[doc = #getter_doc_line] - pub fn #getter(k: KArg) -> #query where - KArg: #frame_support::__private::codec::EncodeLike<#key>, - { - < - #full_ident as #frame_support::storage::StorageMap<#key, #value> - >::get(k) - } - } - ) - }, - Metadata::CountedMap { key, value } => { - let query = match storage.query_kind.as_ref().expect("Checked by def") { - QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => - Option<#value> - ), - QueryKind::ResultQuery(error_path, _) => { - quote::quote_spanned!(storage.attr_span => - Result<#value, #error_path> - ) - }, - QueryKind::ValueQuery => quote::quote!(#value), - }; - quote::quote_spanned!(storage.attr_span => - #(#cfg_attrs)* - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { - #[doc = #getter_doc_line] - pub fn #getter(k: KArg) -> #query where - KArg: #frame_support::__private::codec::EncodeLike<#key>, - { - // NOTE: we can't use any trait here because CountedStorageMap - // doesn't implement any. - <#full_ident>::get(k) - } - } - ) - }, - Metadata::DoubleMap { key1, key2, value } => { - let query = match storage.query_kind.as_ref().expect("Checked by def") { - QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => - Option<#value> - ), - QueryKind::ResultQuery(error_path, _) => { - quote::quote_spanned!(storage.attr_span => - Result<#value, #error_path> - ) - }, - QueryKind::ValueQuery => quote::quote!(#value), - }; - quote::quote_spanned!(storage.attr_span => - #(#cfg_attrs)* - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { - #[doc = #getter_doc_line] - pub fn #getter(k1: KArg1, k2: KArg2) -> #query where - KArg1: #frame_support::__private::codec::EncodeLike<#key1>, - KArg2: #frame_support::__private::codec::EncodeLike<#key2>, - { - < - #full_ident as - #frame_support::storage::StorageDoubleMap<#key1, #key2, #value> - >::get(k1, k2) - } - } - ) - }, - Metadata::NMap { keygen, value, .. } => { - let query = match storage.query_kind.as_ref().expect("Checked by def") { - QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => - Option<#value> - ), - QueryKind::ResultQuery(error_path, _) => { - quote::quote_spanned!(storage.attr_span => - Result<#value, #error_path> - ) - }, - QueryKind::ValueQuery => quote::quote!(#value), - }; - quote::quote_spanned!(storage.attr_span => - #(#cfg_attrs)* - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { - #[doc = #getter_doc_line] - pub fn #getter(key: KArg) -> #query - where - KArg: #frame_support::storage::types::EncodeLikeTuple< - <#keygen as #frame_support::storage::types::KeyGenerator>::KArg - > - + #frame_support::storage::types::TupleToEncodedIter, - { - < - #full_ident as - #frame_support::storage::StorageNMap<#keygen, #value> - >::get(key) - } - } - ) - }, - Metadata::CountedNMap { keygen, value, .. } => { - let query = match storage.query_kind.as_ref().expect("Checked by def") { - QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => - Option<#value> - ), - QueryKind::ResultQuery(error_path, _) => { - quote::quote_spanned!(storage.attr_span => - Result<#value, #error_path> - ) - }, - QueryKind::ValueQuery => quote::quote!(#value), - }; - quote::quote_spanned!(storage.attr_span => - #(#cfg_attrs)* - impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { - #[doc = #getter_doc_line] - pub fn #getter(key: KArg) -> #query - where - KArg: #frame_support::storage::types::EncodeLikeTuple< - <#keygen as #frame_support::storage::types::KeyGenerator>::KArg - > - + #frame_support::storage::types::TupleToEncodedIter, - { - // NOTE: we can't use any trait here because CountedStorageNMap - // doesn't implement any. - <#full_ident>::get(key) - } - } - ) - }, - } - } else { - Default::default() - } - }); - - let prefix_structs = def.storages.iter().map(|storage_def| { + let getters = def.storages.iter().map(|storage| { + if let Some(getter) = &storage.getter { + let completed_where_clause = + super::merge_where_clauses(&[&storage.where_clause, &def.config.where_clause]); + + let ident = &storage.ident; + let gen = &def.type_use_generics(storage.attr_span); + let type_impl_gen = &def.type_impl_generics(storage.attr_span); + let type_use_gen = &def.type_use_generics(storage.attr_span); + let full_ident = quote::quote_spanned!(storage.attr_span => #ident<#gen> ); + + let cfg_attrs = &storage.cfg_attrs; + + // If the storage item is public, link it and otherwise just mention it. + // + // We can not just copy the docs from a non-public type as it may links to internal + // types which makes the compiler very unhappy :( + let getter_doc_line = if matches!(storage.vis, syn::Visibility::Public(_)) { + format!("An auto-generated getter for [`{}`].", storage.ident) + } else { + format!("An auto-generated getter for `{}`.", storage.ident) + }; + + match &storage.metadata { + Metadata::Value { value } => { + let query = match storage.query_kind.as_ref().expect("Checked by def") { + QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => + Option<#value> + ), + QueryKind::ResultQuery(error_path, _) => { + quote::quote_spanned!(storage.attr_span => + Result<#value, #error_path> + ) + } + QueryKind::ValueQuery => quote::quote!(#value), + }; + quote::quote_spanned!(storage.attr_span => + #(#cfg_attrs)* + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { + #[doc = #getter_doc_line] + pub fn #getter() -> #query { + < + #full_ident as #frame_support::storage::StorageValue<#value> + >::get() + } + } + ) + } + Metadata::Map { key, value } => { + let query = match storage.query_kind.as_ref().expect("Checked by def") { + QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => + Option<#value> + ), + QueryKind::ResultQuery(error_path, _) => { + quote::quote_spanned!(storage.attr_span => + Result<#value, #error_path> + ) + } + QueryKind::ValueQuery => quote::quote!(#value), + }; + quote::quote_spanned!(storage.attr_span => + #(#cfg_attrs)* + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { + #[doc = #getter_doc_line] + pub fn #getter(k: KArg) -> #query where + KArg: #frame_support::__private::codec::EncodeLike<#key>, + { + < + #full_ident as #frame_support::storage::StorageMap<#key, #value> + >::get(k) + } + } + ) + } + Metadata::CountedMap { key, value } => { + let query = match storage.query_kind.as_ref().expect("Checked by def") { + QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => + Option<#value> + ), + QueryKind::ResultQuery(error_path, _) => { + quote::quote_spanned!(storage.attr_span => + Result<#value, #error_path> + ) + } + QueryKind::ValueQuery => quote::quote!(#value), + }; + quote::quote_spanned!(storage.attr_span => + #(#cfg_attrs)* + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { + #[doc = #getter_doc_line] + pub fn #getter(k: KArg) -> #query where + KArg: #frame_support::__private::codec::EncodeLike<#key>, + { + // NOTE: we can't use any trait here because CountedStorageMap + // doesn't implement any. + <#full_ident>::get(k) + } + } + ) + } + Metadata::DoubleMap { key1, key2, value } => { + let query = match storage.query_kind.as_ref().expect("Checked by def") { + QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => + Option<#value> + ), + QueryKind::ResultQuery(error_path, _) => { + quote::quote_spanned!(storage.attr_span => + Result<#value, #error_path> + ) + } + QueryKind::ValueQuery => quote::quote!(#value), + }; + quote::quote_spanned!(storage.attr_span => + #(#cfg_attrs)* + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { + #[doc = #getter_doc_line] + pub fn #getter(k1: KArg1, k2: KArg2) -> #query where + KArg1: #frame_support::__private::codec::EncodeLike<#key1>, + KArg2: #frame_support::__private::codec::EncodeLike<#key2>, + { + < + #full_ident as + #frame_support::storage::StorageDoubleMap<#key1, #key2, #value> + >::get(k1, k2) + } + } + ) + } + Metadata::NMap { keygen, value, .. } => { + let query = match storage.query_kind.as_ref().expect("Checked by def") { + QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => + Option<#value> + ), + QueryKind::ResultQuery(error_path, _) => { + quote::quote_spanned!(storage.attr_span => + Result<#value, #error_path> + ) + } + QueryKind::ValueQuery => quote::quote!(#value), + }; + quote::quote_spanned!(storage.attr_span => + #(#cfg_attrs)* + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { + #[doc = #getter_doc_line] + pub fn #getter(key: KArg) -> #query + where + KArg: #frame_support::storage::types::EncodeLikeTuple< + <#keygen as #frame_support::storage::types::KeyGenerator>::KArg + > + + #frame_support::storage::types::TupleToEncodedIter, + { + < + #full_ident as + #frame_support::storage::StorageNMap<#keygen, #value> + >::get(key) + } + } + ) + } + Metadata::CountedNMap { keygen, value, .. } => { + let query = match storage.query_kind.as_ref().expect("Checked by def") { + QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span => + Option<#value> + ), + QueryKind::ResultQuery(error_path, _) => { + quote::quote_spanned!(storage.attr_span => + Result<#value, #error_path> + ) + } + QueryKind::ValueQuery => quote::quote!(#value), + }; + quote::quote_spanned!(storage.attr_span => + #(#cfg_attrs)* + impl<#type_impl_gen> #pallet_ident<#type_use_gen> #completed_where_clause { + #[doc = #getter_doc_line] + pub fn #getter(key: KArg) -> #query + where + KArg: #frame_support::storage::types::EncodeLikeTuple< + <#keygen as #frame_support::storage::types::KeyGenerator>::KArg + > + + #frame_support::storage::types::TupleToEncodedIter, + { + // NOTE: we can't use any trait here because CountedStorageNMap + // doesn't implement any. + <#full_ident>::get(key) + } + } + ) + } + } + } else { + Default::default() + } + }); + + let prefix_structs = def.storages.iter().map(|storage_def| { let type_impl_gen = &def.type_impl_generics(storage_def.attr_span); let type_use_gen = &def.type_use_generics(storage_def.attr_span); let prefix_struct_ident = prefix_ident(storage_def); @@ -767,153 +798,159 @@ pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream { ) }); - let on_empty_structs = on_empty_struct_metadata.into_iter().map(|metadata| { - use crate::pallet::parse::GenericKind; - use syn::{GenericArgument, Path, PathArguments, PathSegment, Type, TypePath}; - - let ResultOnEmptyStructMetadata { - name, - visibility, - value_ty, - error_path, - variant_name, - span, - } = metadata; - - let generic_kind = match error_path.segments.last() { - Some(PathSegment { arguments: PathArguments::AngleBracketed(args), .. }) => { - let (has_config, has_instance) = - args.args.iter().fold((false, false), |(has_config, has_instance), arg| { - match arg { - GenericArgument::Type(Type::Path(TypePath { - path: Path { segments, .. }, - .. - })) => { - let maybe_config = - segments.first().map_or(false, |seg| seg.ident == "T"); - let maybe_instance = - segments.first().map_or(false, |seg| seg.ident == "I"); - - (has_config || maybe_config, has_instance || maybe_instance) - }, - _ => (has_config, has_instance), - } - }); - GenericKind::from_gens(has_config, has_instance).unwrap_or(GenericKind::None) - }, - _ => GenericKind::None, - }; - let type_impl_gen = generic_kind.type_impl_gen(proc_macro2::Span::call_site()); - let config_where_clause = &def.config.where_clause; - - quote::quote_spanned!(span => - #[doc(hidden)] - #[allow(non_camel_case_types)] - #visibility struct #name; - - impl<#type_impl_gen> #frame_support::traits::Get> - for #name - #config_where_clause - { - fn get() -> Result<#value_ty, #error_path> { - Err(<#error_path>::#variant_name) - } - } - ) - }); - - // aggregated where clause of all storage types and the whole pallet. - let mut where_clauses = vec![&def.config.where_clause]; - where_clauses.extend(def.storages.iter().map(|storage| &storage.where_clause)); - let completed_where_clause = super::merge_where_clauses(&where_clauses); - let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site()); - let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site()); - - let try_decode_entire_state = { - let mut storage_names = def - .storages - .iter() - .filter_map(|storage| { - // A little hacky; don't generate for cfg gated storages to not get compile errors - // when building "frame-feature-testing" gated storages in the "frame-support-test" - // crate. - if storage.try_decode && storage.cfg_attrs.is_empty() { - let ident = &storage.ident; - let gen = &def.type_use_generics(storage.attr_span); - Some(quote::quote_spanned!(storage.attr_span => #ident<#gen> )) - } else { - None - } - }) - .collect::>(); - storage_names.sort_by_cached_key(|ident| ident.to_string()); - - quote::quote!( - #[cfg(feature = "try-runtime")] - impl<#type_impl_gen> #frame_support::traits::TryDecodeEntireStorage - for #pallet_ident<#type_use_gen> #completed_where_clause - { - fn try_decode_entire_state() -> Result> { - let pallet_name = <::PalletInfo as frame_support::traits::PalletInfo> - ::name::<#pallet_ident<#type_use_gen>>() - .expect("Every active pallet has a name in the runtime; qed"); - - #frame_support::__private::log::debug!(target: "runtime::try-decode-state", "trying to decode pallet: {pallet_name}"); - - // NOTE: for now, we have to exclude storage items that are feature gated. - let mut errors = #frame_support::__private::sp_std::vec::Vec::new(); - let mut decoded = 0usize; - - #( - #frame_support::__private::log::debug!(target: "runtime::try-decode-state", "trying to decode storage: \ - {pallet_name}::{}", stringify!(#storage_names)); - - match <#storage_names as #frame_support::traits::TryDecodeEntireStorage>::try_decode_entire_state() { - Ok(count) => { - decoded += count; - }, - Err(err) => { - errors.extend(err); - }, - } - )* - - if errors.is_empty() { - Ok(decoded) - } else { - Err(errors) - } - } - } - ) - }; - - quote::quote!( - impl<#type_impl_gen> #pallet_ident<#type_use_gen> - #completed_where_clause - { - #[doc(hidden)] - pub fn storage_metadata() -> #frame_support::__private::metadata_ir::PalletStorageMetadataIR { - #frame_support::__private::metadata_ir::PalletStorageMetadataIR { - prefix: < - ::PalletInfo as - #frame_support::traits::PalletInfo - >::name::<#pallet_ident<#type_use_gen>>() - .expect("No name found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`."), - entries: { - #[allow(unused_mut)] - let mut entries = #frame_support::__private::sp_std::vec![]; - #( #entries_builder )* - entries - }, - } - } - } - - #( #getters )* - #( #prefix_structs )* - #( #on_empty_structs )* - - #try_decode_entire_state - ) + let on_empty_structs = on_empty_struct_metadata.into_iter().map(|metadata| { + use crate::pallet::parse::GenericKind; + use syn::{GenericArgument, Path, PathArguments, PathSegment, Type, TypePath}; + + let ResultOnEmptyStructMetadata { + name, + visibility, + value_ty, + error_path, + variant_name, + span, + } = metadata; + + let generic_kind = match error_path.segments.last() { + Some(PathSegment { + arguments: PathArguments::AngleBracketed(args), + .. + }) => { + let (has_config, has_instance) = + args.args + .iter() + .fold( + (false, false), + |(has_config, has_instance), arg| match arg { + GenericArgument::Type(Type::Path(TypePath { + path: Path { segments, .. }, + .. + })) => { + let maybe_config = + segments.first().map_or(false, |seg| seg.ident == "T"); + let maybe_instance = + segments.first().map_or(false, |seg| seg.ident == "I"); + + (has_config || maybe_config, has_instance || maybe_instance) + } + _ => (has_config, has_instance), + }, + ); + GenericKind::from_gens(has_config, has_instance).unwrap_or(GenericKind::None) + } + _ => GenericKind::None, + }; + let type_impl_gen = generic_kind.type_impl_gen(proc_macro2::Span::call_site()); + let config_where_clause = &def.config.where_clause; + + quote::quote_spanned!(span => + #[doc(hidden)] + #[allow(non_camel_case_types)] + #visibility struct #name; + + impl<#type_impl_gen> #frame_support::traits::Get> + for #name + #config_where_clause + { + fn get() -> Result<#value_ty, #error_path> { + Err(<#error_path>::#variant_name) + } + } + ) + }); + + // aggregated where clause of all storage types and the whole pallet. + let mut where_clauses = vec![&def.config.where_clause]; + where_clauses.extend(def.storages.iter().map(|storage| &storage.where_clause)); + let completed_where_clause = super::merge_where_clauses(&where_clauses); + let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site()); + let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site()); + + let try_decode_entire_state = { + let mut storage_names = def + .storages + .iter() + .filter_map(|storage| { + // A little hacky; don't generate for cfg gated storages to not get compile errors + // when building "frame-feature-testing" gated storages in the "frame-support-test" + // crate. + if storage.try_decode && storage.cfg_attrs.is_empty() { + let ident = &storage.ident; + let gen = &def.type_use_generics(storage.attr_span); + Some(quote::quote_spanned!(storage.attr_span => #ident<#gen> )) + } else { + None + } + }) + .collect::>(); + storage_names.sort_by_cached_key(|ident| ident.to_string()); + + quote::quote!( + #[cfg(feature = "try-runtime")] + impl<#type_impl_gen> #frame_support::traits::TryDecodeEntireStorage + for #pallet_ident<#type_use_gen> #completed_where_clause + { + fn try_decode_entire_state() -> Result> { + let pallet_name = <::PalletInfo as frame_support::traits::PalletInfo> + ::name::<#pallet_ident<#type_use_gen>>() + .expect("Every active pallet has a name in the runtime; qed"); + + #frame_support::__private::log::debug!(target: "runtime::try-decode-state", "trying to decode pallet: {pallet_name}"); + + // NOTE: for now, we have to exclude storage items that are feature gated. + let mut errors = #frame_support::__private::sp_std::vec::Vec::new(); + let mut decoded = 0usize; + + #( + #frame_support::__private::log::debug!(target: "runtime::try-decode-state", "trying to decode storage: \ + {pallet_name}::{}", stringify!(#storage_names)); + + match <#storage_names as #frame_support::traits::TryDecodeEntireStorage>::try_decode_entire_state() { + Ok(count) => { + decoded += count; + }, + Err(err) => { + errors.extend(err); + }, + } + )* + + if errors.is_empty() { + Ok(decoded) + } else { + Err(errors) + } + } + } + ) + }; + + quote::quote!( + impl<#type_impl_gen> #pallet_ident<#type_use_gen> + #completed_where_clause + { + #[doc(hidden)] + pub fn storage_metadata() -> #frame_support::__private::metadata_ir::PalletStorageMetadataIR { + #frame_support::__private::metadata_ir::PalletStorageMetadataIR { + prefix: < + ::PalletInfo as + #frame_support::traits::PalletInfo + >::name::<#pallet_ident<#type_use_gen>>() + .expect("No name found for the pallet in the runtime! This usually means that the pallet wasn't added to `construct_runtime!`."), + entries: { + #[allow(unused_mut)] + let mut entries = #frame_support::__private::sp_std::vec![]; + #( #entries_builder )* + entries + }, + } + } + } + + #( #getters )* + #( #prefix_structs )* + #( #on_empty_structs )* + + #try_decode_entire_state + ) } diff --git a/support/procedural-fork/src/pallet/expand/tasks.rs b/support/procedural-fork/src/pallet/expand/tasks.rs index 6697e5c82..8c4dfb54f 100644 --- a/support/procedural-fork/src/pallet/expand/tasks.rs +++ b/support/procedural-fork/src/pallet/expand/tasks.rs @@ -27,141 +27,145 @@ use quote::{format_ident, quote, ToTokens}; use syn::{parse_quote, spanned::Spanned, ItemEnum, ItemImpl}; impl TaskEnumDef { - /// Since we optionally allow users to manually specify a `#[pallet::task_enum]`, in the - /// event they _don't_ specify one (which is actually the most common behavior) we have to - /// generate one based on the existing [`TasksDef`]. This method performs that generation. - pub fn generate( - tasks: &TasksDef, - type_decl_bounded_generics: TokenStream2, - type_use_generics: TokenStream2, - ) -> Self { - let variants = if tasks.tasks_attr.is_some() { - tasks - .tasks - .iter() - .map(|task| { - let ident = &task.item.sig.ident; - let ident = - format_ident!("{}", ident.to_string().to_class_case(), span = ident.span()); + /// Since we optionally allow users to manually specify a `#[pallet::task_enum]`, in the + /// event they _don't_ specify one (which is actually the most common behavior) we have to + /// generate one based on the existing [`TasksDef`]. This method performs that generation. + pub fn generate( + tasks: &TasksDef, + type_decl_bounded_generics: TokenStream2, + type_use_generics: TokenStream2, + ) -> Self { + let variants = if tasks.tasks_attr.is_some() { + tasks + .tasks + .iter() + .map(|task| { + let ident = &task.item.sig.ident; + let ident = + format_ident!("{}", ident.to_string().to_class_case(), span = ident.span()); - let args = task.item.sig.inputs.iter().collect::>(); + let args = task.item.sig.inputs.iter().collect::>(); - if args.is_empty() { - quote!(#ident) - } else { - quote!(#ident { - #(#args),* - }) - } - }) - .collect::>() - } else { - Vec::new() - }; - let mut task_enum_def: TaskEnumDef = parse_quote! { - /// Auto-generated enum that encapsulates all tasks defined by this pallet. - /// - /// Conceptually similar to the [`Call`] enum, but for tasks. This is only - /// generated if there are tasks present in this pallet. - #[pallet::task_enum] - pub enum Task<#type_decl_bounded_generics> { - #( - #variants, - )* - } - }; - task_enum_def.type_use_generics = type_use_generics; - task_enum_def - } + if args.is_empty() { + quote!(#ident) + } else { + quote!(#ident { + #(#args),* + }) + } + }) + .collect::>() + } else { + Vec::new() + }; + let mut task_enum_def: TaskEnumDef = parse_quote! { + /// Auto-generated enum that encapsulates all tasks defined by this pallet. + /// + /// Conceptually similar to the [`Call`] enum, but for tasks. This is only + /// generated if there are tasks present in this pallet. + #[pallet::task_enum] + pub enum Task<#type_decl_bounded_generics> { + #( + #variants, + )* + } + }; + task_enum_def.type_use_generics = type_use_generics; + task_enum_def + } } impl ToTokens for TaskEnumDef { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let item_enum = &self.item_enum; - let ident = &item_enum.ident; - let vis = &item_enum.vis; - let attrs = &item_enum.attrs; - let generics = &item_enum.generics; - let variants = &item_enum.variants; - let scrate = &self.scrate; - let type_use_generics = &self.type_use_generics; - if self.attr.is_some() { - // `item_enum` is short-hand / generated enum - tokens.extend(quote! { - #(#attrs)* - #[derive( - #scrate::CloneNoBound, - #scrate::EqNoBound, - #scrate::PartialEqNoBound, - #scrate::pallet_prelude::Encode, - #scrate::pallet_prelude::Decode, - #scrate::pallet_prelude::TypeInfo, - )] - #[codec(encode_bound())] - #[codec(decode_bound())] - #[scale_info(skip_type_params(#type_use_generics))] - #vis enum #ident #generics { - #variants - #[doc(hidden)] - #[codec(skip)] - __Ignore(core::marker::PhantomData, #scrate::Never), - } + fn to_tokens(&self, tokens: &mut TokenStream2) { + let item_enum = &self.item_enum; + let ident = &item_enum.ident; + let vis = &item_enum.vis; + let attrs = &item_enum.attrs; + let generics = &item_enum.generics; + let variants = &item_enum.variants; + let scrate = &self.scrate; + let type_use_generics = &self.type_use_generics; + if self.attr.is_some() { + // `item_enum` is short-hand / generated enum + tokens.extend(quote! { + #(#attrs)* + #[derive( + #scrate::CloneNoBound, + #scrate::EqNoBound, + #scrate::PartialEqNoBound, + #scrate::pallet_prelude::Encode, + #scrate::pallet_prelude::Decode, + #scrate::pallet_prelude::TypeInfo, + )] + #[codec(encode_bound())] + #[codec(decode_bound())] + #[scale_info(skip_type_params(#type_use_generics))] + #vis enum #ident #generics { + #variants + #[doc(hidden)] + #[codec(skip)] + __Ignore(core::marker::PhantomData, #scrate::Never), + } - impl core::fmt::Debug for #ident<#type_use_generics> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct(stringify!(#ident)).field("value", self).finish() - } - } - }); - } else { - // `item_enum` is a manually specified enum (no attribute) - tokens.extend(item_enum.to_token_stream()); - } - } + impl core::fmt::Debug for #ident<#type_use_generics> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct(stringify!(#ident)).field("value", self).finish() + } + } + }); + } else { + // `item_enum` is a manually specified enum (no attribute) + tokens.extend(item_enum.to_token_stream()); + } + } } /// Represents an already-expanded [`TasksDef`]. #[derive(Parse)] pub struct ExpandedTasksDef { - pub task_item_impl: ItemImpl, - pub task_trait_impl: ItemImpl, + pub task_item_impl: ItemImpl, + pub task_trait_impl: ItemImpl, } impl ToTokens for TasksDef { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let scrate = &self.scrate; - let enum_ident = syn::Ident::new("Task", self.enum_ident.span()); - let enum_arguments = &self.enum_arguments; - let enum_use = quote!(#enum_ident #enum_arguments); + fn to_tokens(&self, tokens: &mut TokenStream2) { + let scrate = &self.scrate; + let enum_ident = syn::Ident::new("Task", self.enum_ident.span()); + let enum_arguments = &self.enum_arguments; + let enum_use = quote!(#enum_ident #enum_arguments); - let task_fn_idents = self - .tasks - .iter() - .map(|task| { - format_ident!( - "{}", - &task.item.sig.ident.to_string().to_class_case(), - span = task.item.sig.ident.span() - ) - }) - .collect::>(); - let task_indices = self.tasks.iter().map(|task| &task.index_attr.meta.index); - let task_conditions = self.tasks.iter().map(|task| &task.condition_attr.meta.expr); - let task_weights = self.tasks.iter().map(|task| &task.weight_attr.meta.expr); - let task_iters = self.tasks.iter().map(|task| &task.list_attr.meta.expr); + let task_fn_idents = self + .tasks + .iter() + .map(|task| { + format_ident!( + "{}", + &task.item.sig.ident.to_string().to_class_case(), + span = task.item.sig.ident.span() + ) + }) + .collect::>(); + let task_indices = self.tasks.iter().map(|task| &task.index_attr.meta.index); + let task_conditions = self.tasks.iter().map(|task| &task.condition_attr.meta.expr); + let task_weights = self.tasks.iter().map(|task| &task.weight_attr.meta.expr); + let task_iters = self.tasks.iter().map(|task| &task.list_attr.meta.expr); - let task_fn_impls = self.tasks.iter().map(|task| { - let mut task_fn_impl = task.item.clone(); - task_fn_impl.attrs = vec![]; - task_fn_impl - }); + let task_fn_impls = self.tasks.iter().map(|task| { + let mut task_fn_impl = task.item.clone(); + task_fn_impl.attrs = vec![]; + task_fn_impl + }); - let task_fn_names = self.tasks.iter().map(|task| &task.item.sig.ident); - let task_arg_names = self.tasks.iter().map(|task| &task.arg_names).collect::>(); + let task_fn_names = self.tasks.iter().map(|task| &task.item.sig.ident); + let task_arg_names = self + .tasks + .iter() + .map(|task| &task.arg_names) + .collect::>(); - let sp_std = quote!(#scrate::__private::sp_std); - let impl_generics = &self.item_impl.generics; - tokens.extend(quote! { + let sp_std = quote!(#scrate::__private::sp_std); + let impl_generics = &self.item_impl.generics; + tokens.extend(quote! { impl #impl_generics #enum_use { #(#task_fn_impls)* @@ -212,56 +216,66 @@ impl ToTokens for TasksDef { } } }); - } + } } /// Expands the [`TasksDef`] in the enclosing [`Def`], if present, and returns its tokens. /// /// This modifies the underlying [`Def`] in addition to returning any tokens that were added. pub fn expand_tasks_impl(def: &mut Def) -> TokenStream2 { - let Some(tasks) = &mut def.tasks else { return quote!() }; - let ExpandedTasksDef { task_item_impl, task_trait_impl } = parse_quote!(#tasks); - quote! { - #task_item_impl - #task_trait_impl - } + let Some(tasks) = &mut def.tasks else { + return quote!(); + }; + let ExpandedTasksDef { + task_item_impl, + task_trait_impl, + } = parse_quote!(#tasks); + quote! { + #task_item_impl + #task_trait_impl + } } /// Represents a fully-expanded [`TaskEnumDef`]. #[derive(Parse)] pub struct ExpandedTaskEnum { - pub item_enum: ItemEnum, - pub debug_impl: ItemImpl, + pub item_enum: ItemEnum, + pub debug_impl: ItemImpl, } /// Modifies a [`Def`] to expand the underlying [`TaskEnumDef`] if present, and also returns /// its tokens. A blank [`TokenStream2`] is returned if no [`TaskEnumDef`] has been generated /// or defined. pub fn expand_task_enum(def: &mut Def) -> TokenStream2 { - let Some(task_enum) = &mut def.task_enum else { return quote!() }; - let ExpandedTaskEnum { item_enum, debug_impl } = parse_quote!(#task_enum); - quote! { - #item_enum - #debug_impl - } + let Some(task_enum) = &mut def.task_enum else { + return quote!(); + }; + let ExpandedTaskEnum { + item_enum, + debug_impl, + } = parse_quote!(#task_enum); + quote! { + #item_enum + #debug_impl + } } /// Modifies a [`Def`] to expand the underlying [`TasksDef`] and also generate a /// [`TaskEnumDef`] if applicable. The tokens for these items are returned if they are created. pub fn expand_tasks(def: &mut Def) -> TokenStream2 { - if let Some(tasks_def) = &def.tasks { - if def.task_enum.is_none() { - def.task_enum = Some(TaskEnumDef::generate( - &tasks_def, - def.type_decl_bounded_generics(tasks_def.item_impl.span()), - def.type_use_generics(tasks_def.item_impl.span()), - )); - } - } - let tasks_extra_output = expand_tasks_impl(def); - let task_enum_extra_output = expand_task_enum(def); - quote! { - #tasks_extra_output - #task_enum_extra_output - } + if let Some(tasks_def) = &def.tasks { + if def.task_enum.is_none() { + def.task_enum = Some(TaskEnumDef::generate( + &tasks_def, + def.type_decl_bounded_generics(tasks_def.item_impl.span()), + def.type_use_generics(tasks_def.item_impl.span()), + )); + } + } + let tasks_extra_output = expand_tasks_impl(def); + let task_enum_extra_output = expand_task_enum(def); + quote! { + #tasks_extra_output + #task_enum_extra_output + } } diff --git a/support/procedural-fork/src/pallet/expand/tt_default_parts.rs b/support/procedural-fork/src/pallet/expand/tt_default_parts.rs index 99364aaa9..57b78339a 100644 --- a/support/procedural-fork/src/pallet/expand/tt_default_parts.rs +++ b/support/procedural-fork/src/pallet/expand/tt_default_parts.rs @@ -16,201 +16,211 @@ // limitations under the License. use crate::{ - pallet::{CompositeKeyword, Def}, - COUNTER, + pallet::{CompositeKeyword, Def}, + COUNTER, }; use syn::spanned::Spanned; /// Generate the `tt_default_parts` macro. pub fn expand_tt_default_parts(def: &mut Def) -> proc_macro2::TokenStream { - let count = COUNTER.with(|counter| counter.borrow_mut().inc()); - let default_parts_unique_id = - syn::Ident::new(&format!("__tt_default_parts_{}", count), def.item.span()); - let extra_parts_unique_id = - syn::Ident::new(&format!("__tt_extra_parts_{}", count), def.item.span()); - let default_parts_unique_id_v2 = - syn::Ident::new(&format!("__tt_default_parts_v2_{}", count), def.item.span()); - - let call_part = def.call.as_ref().map(|_| quote::quote!(Call,)); - - let task_part = def.task_enum.as_ref().map(|_| quote::quote!(Task,)); - - let storage_part = (!def.storages.is_empty()).then(|| quote::quote!(Storage,)); - - let event_part = def.event.as_ref().map(|event| { - let gen = event.gen_kind.is_generic().then(|| quote::quote!( )); - quote::quote!( Event #gen , ) - }); - - let error_part = def.error.as_ref().map(|_| quote::quote!(Error,)); - - let origin_part = def.origin.as_ref().map(|origin| { - let gen = origin.is_generic.then(|| quote::quote!( )); - quote::quote!( Origin #gen , ) - }); - - let config_part = def.genesis_config.as_ref().map(|genesis_config| { - let gen = genesis_config.gen_kind.is_generic().then(|| quote::quote!( )); - quote::quote!( Config #gen , ) - }); - - let inherent_part = def.inherent.as_ref().map(|_| quote::quote!(Inherent,)); - - let validate_unsigned_part = - def.validate_unsigned.as_ref().map(|_| quote::quote!(ValidateUnsigned,)); - - let freeze_reason_part = def - .composites - .iter() - .any(|c| matches!(c.composite_keyword, CompositeKeyword::FreezeReason(_))) - .then_some(quote::quote!(FreezeReason,)); - - let hold_reason_part = def - .composites - .iter() - .any(|c| matches!(c.composite_keyword, CompositeKeyword::HoldReason(_))) - .then_some(quote::quote!(HoldReason,)); - - let lock_id_part = def - .composites - .iter() - .any(|c| matches!(c.composite_keyword, CompositeKeyword::LockId(_))) - .then_some(quote::quote!(LockId,)); - - let slash_reason_part = def - .composites - .iter() - .any(|c| matches!(c.composite_keyword, CompositeKeyword::SlashReason(_))) - .then_some(quote::quote!(SlashReason,)); - - let call_part_v2 = def.call.as_ref().map(|_| quote::quote!(+ Call)); - - let task_part_v2 = def.task_enum.as_ref().map(|_| quote::quote!(+ Task)); - - let storage_part_v2 = (!def.storages.is_empty()).then(|| quote::quote!(+ Storage)); - - let event_part_v2 = def.event.as_ref().map(|event| { - let gen = event.gen_kind.is_generic().then(|| quote::quote!()); - quote::quote!(+ Event #gen) - }); - - let error_part_v2 = def.error.as_ref().map(|_| quote::quote!(+ Error)); - - let origin_part_v2 = def.origin.as_ref().map(|origin| { - let gen = origin.is_generic.then(|| quote::quote!()); - quote::quote!(+ Origin #gen) - }); - - let config_part_v2 = def.genesis_config.as_ref().map(|genesis_config| { - let gen = genesis_config.gen_kind.is_generic().then(|| quote::quote!()); - quote::quote!(+ Config #gen) - }); - - let inherent_part_v2 = def.inherent.as_ref().map(|_| quote::quote!(+ Inherent)); - - let validate_unsigned_part_v2 = - def.validate_unsigned.as_ref().map(|_| quote::quote!(+ ValidateUnsigned)); - - let freeze_reason_part_v2 = def - .composites - .iter() - .any(|c| matches!(c.composite_keyword, CompositeKeyword::FreezeReason(_))) - .then_some(quote::quote!(+ FreezeReason)); - - let hold_reason_part_v2 = def - .composites - .iter() - .any(|c| matches!(c.composite_keyword, CompositeKeyword::HoldReason(_))) - .then_some(quote::quote!(+ HoldReason)); - - let lock_id_part_v2 = def - .composites - .iter() - .any(|c| matches!(c.composite_keyword, CompositeKeyword::LockId(_))) - .then_some(quote::quote!(+ LockId)); - - let slash_reason_part_v2 = def - .composites - .iter() - .any(|c| matches!(c.composite_keyword, CompositeKeyword::SlashReason(_))) - .then_some(quote::quote!(+ SlashReason)); - - quote::quote!( - // This macro follows the conventions as laid out by the `tt-call` crate. It does not - // accept any arguments and simply returns the pallet parts, separated by commas, then - // wrapped inside of braces and finally prepended with double colons, to the caller inside - // of a key named `tokens`. - // - // We need to accept a path argument here, because this macro gets expanded on the - // crate that called the `construct_runtime!` macro, and the actual path is unknown. - #[macro_export] - #[doc(hidden)] - macro_rules! #default_parts_unique_id { - { - $caller:tt - your_tt_return = [{ $my_tt_return:path }] - } => { - $my_tt_return! { - $caller - tokens = [{ - expanded::{ - Pallet, #call_part #storage_part #event_part #error_part #origin_part #config_part - #inherent_part #validate_unsigned_part #freeze_reason_part #task_part - #hold_reason_part #lock_id_part #slash_reason_part - } - }] - } - }; - } - - pub use #default_parts_unique_id as tt_default_parts; - - - // This macro is similar to the `tt_default_parts!`. It expands the pallets that are declared - // explicitly (`System: frame_system::{Pallet, Call}`) with extra parts. - // - // For example, after expansion an explicit pallet would look like: - // `System: expanded::{Error} ::{Pallet, Call}`. - // - // The `expanded` keyword is a marker of the final state of the `construct_runtime!`. - #[macro_export] - #[doc(hidden)] - macro_rules! #extra_parts_unique_id { - { - $caller:tt - your_tt_return = [{ $my_tt_return:path }] - } => { - $my_tt_return! { - $caller - tokens = [{ - expanded::{ - #error_part - } - }] - } - }; - } - - pub use #extra_parts_unique_id as tt_extra_parts; - - #[macro_export] - #[doc(hidden)] - macro_rules! #default_parts_unique_id_v2 { - { - $caller:tt - frame_support = [{ $($frame_support:ident)::* }] - } => { - $($frame_support)*::__private::tt_return! { - $caller - tokens = [{ - + Pallet #call_part_v2 #storage_part_v2 #event_part_v2 #error_part_v2 #origin_part_v2 #config_part_v2 - #inherent_part_v2 #validate_unsigned_part_v2 #freeze_reason_part_v2 #task_part_v2 - #hold_reason_part_v2 #lock_id_part_v2 #slash_reason_part_v2 - }] - } - }; - } - - pub use #default_parts_unique_id_v2 as tt_default_parts_v2; - ) + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + let default_parts_unique_id = + syn::Ident::new(&format!("__tt_default_parts_{}", count), def.item.span()); + let extra_parts_unique_id = + syn::Ident::new(&format!("__tt_extra_parts_{}", count), def.item.span()); + let default_parts_unique_id_v2 = + syn::Ident::new(&format!("__tt_default_parts_v2_{}", count), def.item.span()); + + let call_part = def.call.as_ref().map(|_| quote::quote!(Call,)); + + let task_part = def.task_enum.as_ref().map(|_| quote::quote!(Task,)); + + let storage_part = (!def.storages.is_empty()).then(|| quote::quote!(Storage,)); + + let event_part = def.event.as_ref().map(|event| { + let gen = event.gen_kind.is_generic().then(|| quote::quote!( )); + quote::quote!( Event #gen , ) + }); + + let error_part = def.error.as_ref().map(|_| quote::quote!(Error,)); + + let origin_part = def.origin.as_ref().map(|origin| { + let gen = origin.is_generic.then(|| quote::quote!( )); + quote::quote!( Origin #gen , ) + }); + + let config_part = def.genesis_config.as_ref().map(|genesis_config| { + let gen = genesis_config + .gen_kind + .is_generic() + .then(|| quote::quote!( )); + quote::quote!( Config #gen , ) + }); + + let inherent_part = def.inherent.as_ref().map(|_| quote::quote!(Inherent,)); + + let validate_unsigned_part = def + .validate_unsigned + .as_ref() + .map(|_| quote::quote!(ValidateUnsigned,)); + + let freeze_reason_part = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::FreezeReason(_))) + .then_some(quote::quote!(FreezeReason,)); + + let hold_reason_part = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::HoldReason(_))) + .then_some(quote::quote!(HoldReason,)); + + let lock_id_part = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::LockId(_))) + .then_some(quote::quote!(LockId,)); + + let slash_reason_part = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::SlashReason(_))) + .then_some(quote::quote!(SlashReason,)); + + let call_part_v2 = def.call.as_ref().map(|_| quote::quote!(+ Call)); + + let task_part_v2 = def.task_enum.as_ref().map(|_| quote::quote!(+ Task)); + + let storage_part_v2 = (!def.storages.is_empty()).then(|| quote::quote!(+ Storage)); + + let event_part_v2 = def.event.as_ref().map(|event| { + let gen = event.gen_kind.is_generic().then(|| quote::quote!()); + quote::quote!(+ Event #gen) + }); + + let error_part_v2 = def.error.as_ref().map(|_| quote::quote!(+ Error)); + + let origin_part_v2 = def.origin.as_ref().map(|origin| { + let gen = origin.is_generic.then(|| quote::quote!()); + quote::quote!(+ Origin #gen) + }); + + let config_part_v2 = def.genesis_config.as_ref().map(|genesis_config| { + let gen = genesis_config + .gen_kind + .is_generic() + .then(|| quote::quote!()); + quote::quote!(+ Config #gen) + }); + + let inherent_part_v2 = def.inherent.as_ref().map(|_| quote::quote!(+ Inherent)); + + let validate_unsigned_part_v2 = def + .validate_unsigned + .as_ref() + .map(|_| quote::quote!(+ ValidateUnsigned)); + + let freeze_reason_part_v2 = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::FreezeReason(_))) + .then_some(quote::quote!(+ FreezeReason)); + + let hold_reason_part_v2 = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::HoldReason(_))) + .then_some(quote::quote!(+ HoldReason)); + + let lock_id_part_v2 = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::LockId(_))) + .then_some(quote::quote!(+ LockId)); + + let slash_reason_part_v2 = def + .composites + .iter() + .any(|c| matches!(c.composite_keyword, CompositeKeyword::SlashReason(_))) + .then_some(quote::quote!(+ SlashReason)); + + quote::quote!( + // This macro follows the conventions as laid out by the `tt-call` crate. It does not + // accept any arguments and simply returns the pallet parts, separated by commas, then + // wrapped inside of braces and finally prepended with double colons, to the caller inside + // of a key named `tokens`. + // + // We need to accept a path argument here, because this macro gets expanded on the + // crate that called the `construct_runtime!` macro, and the actual path is unknown. + #[macro_export] + #[doc(hidden)] + macro_rules! #default_parts_unique_id { + { + $caller:tt + your_tt_return = [{ $my_tt_return:path }] + } => { + $my_tt_return! { + $caller + tokens = [{ + expanded::{ + Pallet, #call_part #storage_part #event_part #error_part #origin_part #config_part + #inherent_part #validate_unsigned_part #freeze_reason_part #task_part + #hold_reason_part #lock_id_part #slash_reason_part + } + }] + } + }; + } + + pub use #default_parts_unique_id as tt_default_parts; + + + // This macro is similar to the `tt_default_parts!`. It expands the pallets that are declared + // explicitly (`System: frame_system::{Pallet, Call}`) with extra parts. + // + // For example, after expansion an explicit pallet would look like: + // `System: expanded::{Error} ::{Pallet, Call}`. + // + // The `expanded` keyword is a marker of the final state of the `construct_runtime!`. + #[macro_export] + #[doc(hidden)] + macro_rules! #extra_parts_unique_id { + { + $caller:tt + your_tt_return = [{ $my_tt_return:path }] + } => { + $my_tt_return! { + $caller + tokens = [{ + expanded::{ + #error_part + } + }] + } + }; + } + + pub use #extra_parts_unique_id as tt_extra_parts; + + #[macro_export] + #[doc(hidden)] + macro_rules! #default_parts_unique_id_v2 { + { + $caller:tt + frame_support = [{ $($frame_support:ident)::* }] + } => { + $($frame_support)*::__private::tt_return! { + $caller + tokens = [{ + + Pallet #call_part_v2 #storage_part_v2 #event_part_v2 #error_part_v2 #origin_part_v2 #config_part_v2 + #inherent_part_v2 #validate_unsigned_part_v2 #freeze_reason_part_v2 #task_part_v2 + #hold_reason_part_v2 #lock_id_part_v2 #slash_reason_part_v2 + }] + } + }; + } + + pub use #default_parts_unique_id_v2 as tt_default_parts_v2; + ) } diff --git a/support/procedural-fork/src/pallet/expand/type_value.rs b/support/procedural-fork/src/pallet/expand/type_value.rs index 5dc6309c0..84db3e431 100644 --- a/support/procedural-fork/src/pallet/expand/type_value.rs +++ b/support/procedural-fork/src/pallet/expand/type_value.rs @@ -22,56 +22,56 @@ use crate::pallet::Def; /// * implement the `Get<..>` on it /// * Rename the name of the function to internal name pub fn expand_type_values(def: &mut Def) -> proc_macro2::TokenStream { - let mut expand = quote::quote!(); - let frame_support = &def.frame_support; + let mut expand = quote::quote!(); + let frame_support = &def.frame_support; - for type_value in &def.type_values { - let fn_name_str = &type_value.ident.to_string(); - let fn_name_snakecase = inflector::cases::snakecase::to_snake_case(fn_name_str); - let fn_ident_renamed = syn::Ident::new( - &format!("__type_value_for_{}", fn_name_snakecase), - type_value.ident.span(), - ); + for type_value in &def.type_values { + let fn_name_str = &type_value.ident.to_string(); + let fn_name_snakecase = inflector::cases::snakecase::to_snake_case(fn_name_str); + let fn_ident_renamed = syn::Ident::new( + &format!("__type_value_for_{}", fn_name_snakecase), + type_value.ident.span(), + ); - let type_value_item = { - let item = &mut def.item.content.as_mut().expect("Checked by def").1[type_value.index]; - if let syn::Item::Fn(item) = item { - item - } else { - unreachable!("Checked by error parser") - } - }; + let type_value_item = { + let item = &mut def.item.content.as_mut().expect("Checked by def").1[type_value.index]; + if let syn::Item::Fn(item) = item { + item + } else { + unreachable!("Checked by error parser") + } + }; - // Rename the type_value function name - type_value_item.sig.ident = fn_ident_renamed.clone(); + // Rename the type_value function name + type_value_item.sig.ident = fn_ident_renamed.clone(); - let vis = &type_value.vis; - let ident = &type_value.ident; - let type_ = &type_value.type_; - let where_clause = &type_value.where_clause; + let vis = &type_value.vis; + let ident = &type_value.ident; + let type_ = &type_value.type_; + let where_clause = &type_value.where_clause; - let (struct_impl_gen, struct_use_gen) = if type_value.is_generic { - ( - def.type_impl_generics(type_value.attr_span), - def.type_use_generics(type_value.attr_span), - ) - } else { - (Default::default(), Default::default()) - }; + let (struct_impl_gen, struct_use_gen) = if type_value.is_generic { + ( + def.type_impl_generics(type_value.attr_span), + def.type_use_generics(type_value.attr_span), + ) + } else { + (Default::default(), Default::default()) + }; - let docs = &type_value.docs; + let docs = &type_value.docs; - expand.extend(quote::quote_spanned!(type_value.attr_span => - #( #[doc = #docs] )* - #vis struct #ident<#struct_use_gen>(core::marker::PhantomData<((), #struct_use_gen)>); - impl<#struct_impl_gen> #frame_support::traits::Get<#type_> for #ident<#struct_use_gen> - #where_clause - { - fn get() -> #type_ { - #fn_ident_renamed::<#struct_use_gen>() - } - } - )); - } - expand + expand.extend(quote::quote_spanned!(type_value.attr_span => + #( #[doc = #docs] )* + #vis struct #ident<#struct_use_gen>(core::marker::PhantomData<((), #struct_use_gen)>); + impl<#struct_impl_gen> #frame_support::traits::Get<#type_> for #ident<#struct_use_gen> + #where_clause + { + fn get() -> #type_ { + #fn_ident_renamed::<#struct_use_gen>() + } + } + )); + } + expand } diff --git a/support/procedural-fork/src/pallet/expand/validate_unsigned.rs b/support/procedural-fork/src/pallet/expand/validate_unsigned.rs index 876995585..28c78a1c6 100644 --- a/support/procedural-fork/src/pallet/expand/validate_unsigned.rs +++ b/support/procedural-fork/src/pallet/expand/validate_unsigned.rs @@ -21,36 +21,38 @@ use quote::quote; use syn::{spanned::Spanned, Ident}; pub fn expand_validate_unsigned(def: &mut Def) -> TokenStream { - let count = COUNTER.with(|counter| counter.borrow_mut().inc()); - let macro_ident = - Ident::new(&format!("__is_validate_unsigned_part_defined_{}", count), def.item.span()); + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + let macro_ident = Ident::new( + &format!("__is_validate_unsigned_part_defined_{}", count), + def.item.span(), + ); - let maybe_compile_error = if def.validate_unsigned.is_none() { - quote! { - compile_error!(concat!( - "`", - stringify!($pallet_name), - "` does not have #[pallet::validate_unsigned] defined, perhaps you should \ - remove `ValidateUnsigned` from construct_runtime?", - )); - } - } else { - TokenStream::new() - }; + let maybe_compile_error = if def.validate_unsigned.is_none() { + quote! { + compile_error!(concat!( + "`", + stringify!($pallet_name), + "` does not have #[pallet::validate_unsigned] defined, perhaps you should \ + remove `ValidateUnsigned` from construct_runtime?", + )); + } + } else { + TokenStream::new() + }; - quote! { - #[doc(hidden)] - pub mod __substrate_validate_unsigned_check { - #[macro_export] - #[doc(hidden)] - macro_rules! #macro_ident { - ($pallet_name:ident) => { - #maybe_compile_error - } - } + quote! { + #[doc(hidden)] + pub mod __substrate_validate_unsigned_check { + #[macro_export] + #[doc(hidden)] + macro_rules! #macro_ident { + ($pallet_name:ident) => { + #maybe_compile_error + } + } - #[doc(hidden)] - pub use #macro_ident as is_validate_unsigned_part_defined; - } - } + #[doc(hidden)] + pub use #macro_ident as is_validate_unsigned_part_defined; + } + } } diff --git a/support/procedural-fork/src/pallet/expand/warnings.rs b/support/procedural-fork/src/pallet/expand/warnings.rs index 030e3ddaf..3d71b83af 100644 --- a/support/procedural-fork/src/pallet/expand/warnings.rs +++ b/support/procedural-fork/src/pallet/expand/warnings.rs @@ -20,79 +20,84 @@ use crate::pallet::parse::call::{CallVariantDef, CallWeightDef}; use proc_macro_warning::Warning; use syn::{ - spanned::Spanned, - visit::{self, Visit}, + spanned::Spanned, + visit::{self, Visit}, }; /// Warn if any of the call arguments starts with a underscore and is used in a weight formula. pub(crate) fn weight_witness_warning( - method: &CallVariantDef, - dev_mode: bool, - warnings: &mut Vec, + method: &CallVariantDef, + dev_mode: bool, + warnings: &mut Vec, ) { - if dev_mode { - return - } - let CallWeightDef::Immediate(w) = &method.weight else { return }; + if dev_mode { + return; + } + let CallWeightDef::Immediate(w) = &method.weight else { + return; + }; - let partial_warning = Warning::new_deprecated("UncheckedWeightWitness") - .old("not check weight witness data") - .new("ensure that all witness data for weight calculation is checked before usage") - .help_link("https://github.com/paritytech/polkadot-sdk/pull/1818"); + let partial_warning = Warning::new_deprecated("UncheckedWeightWitness") + .old("not check weight witness data") + .new("ensure that all witness data for weight calculation is checked before usage") + .help_link("https://github.com/paritytech/polkadot-sdk/pull/1818"); - for (_, arg_ident, _) in method.args.iter() { - if !arg_ident.to_string().starts_with('_') || !contains_ident(w.clone(), &arg_ident) { - continue - } + for (_, arg_ident, _) in method.args.iter() { + if !arg_ident.to_string().starts_with('_') || !contains_ident(w.clone(), &arg_ident) { + continue; + } - let warning = partial_warning - .clone() - .index(warnings.len()) - .span(arg_ident.span()) - .build_or_panic(); + let warning = partial_warning + .clone() + .index(warnings.len()) + .span(arg_ident.span()) + .build_or_panic(); - warnings.push(warning); - } + warnings.push(warning); + } } /// Warn if the weight is a constant and the pallet not in `dev_mode`. pub(crate) fn weight_constant_warning( - weight: &syn::Expr, - dev_mode: bool, - warnings: &mut Vec, + weight: &syn::Expr, + dev_mode: bool, + warnings: &mut Vec, ) { - if dev_mode { - return - } - let syn::Expr::Lit(lit) = weight else { return }; + if dev_mode { + return; + } + let syn::Expr::Lit(lit) = weight else { return }; - let warning = Warning::new_deprecated("ConstantWeight") - .index(warnings.len()) - .old("use hard-coded constant as call weight") - .new("benchmark all calls or put the pallet into `dev` mode") - .help_link("https://github.com/paritytech/substrate/pull/13798") - .span(lit.span()) - .build_or_panic(); + let warning = Warning::new_deprecated("ConstantWeight") + .index(warnings.len()) + .old("use hard-coded constant as call weight") + .new("benchmark all calls or put the pallet into `dev` mode") + .help_link("https://github.com/paritytech/substrate/pull/13798") + .span(lit.span()) + .build_or_panic(); - warnings.push(warning); + warnings.push(warning); } /// Returns whether `expr` contains `ident`. fn contains_ident(mut expr: syn::Expr, ident: &syn::Ident) -> bool { - struct ContainsIdent { - ident: syn::Ident, - found: bool, - } + struct ContainsIdent { + ident: syn::Ident, + found: bool, + } - impl<'a> Visit<'a> for ContainsIdent { - fn visit_ident(&mut self, i: &syn::Ident) { - if *i == self.ident { - self.found = true; - } - } - } + impl<'a> Visit<'a> for ContainsIdent { + fn visit_ident(&mut self, i: &syn::Ident) { + if *i == self.ident { + self.found = true; + } + } + } - let mut visitor = ContainsIdent { ident: ident.clone(), found: false }; - visit::visit_expr(&mut visitor, &mut expr); - visitor.found + let mut visitor = ContainsIdent { + ident: ident.clone(), + found: false, + }; + visit::visit_expr(&mut visitor, &mut expr); + visitor.found } diff --git a/support/procedural-fork/src/pallet/mod.rs b/support/procedural-fork/src/pallet/mod.rs index 42d8272fb..d3796662f 100644 --- a/support/procedural-fork/src/pallet/mod.rs +++ b/support/procedural-fork/src/pallet/mod.rs @@ -32,30 +32,30 @@ pub use parse::{composite::keyword::CompositeKeyword, Def}; use syn::spanned::Spanned; mod keyword { - syn::custom_keyword!(dev_mode); + syn::custom_keyword!(dev_mode); } pub fn pallet( - attr: proc_macro::TokenStream, - item: proc_macro::TokenStream, + attr: proc_macro::TokenStream, + item: proc_macro::TokenStream, ) -> proc_macro::TokenStream { - let mut dev_mode = false; - if !attr.is_empty() { - if let Ok(_) = syn::parse::(attr.clone()) { - dev_mode = true; - } else { - let msg = "Invalid pallet macro call: unexpected attribute. Macro call must be \ + let mut dev_mode = false; + if !attr.is_empty() { + if let Ok(_) = syn::parse::(attr.clone()) { + dev_mode = true; + } else { + let msg = "Invalid pallet macro call: unexpected attribute. Macro call must be \ bare, such as `#[frame_support::pallet]` or `#[pallet]`, or must specify the \ `dev_mode` attribute, such as `#[frame_support::pallet(dev_mode)]` or \ #[pallet(dev_mode)]."; - let span = proc_macro2::TokenStream::from(attr).span(); - return syn::Error::new(span, msg).to_compile_error().into() - } - } - - let item = syn::parse_macro_input!(item as syn::ItemMod); - match parse::Def::try_from(item, dev_mode) { - Ok(def) => expand::expand(def).into(), - Err(e) => e.to_compile_error().into(), - } + let span = proc_macro2::TokenStream::from(attr).span(); + return syn::Error::new(span, msg).to_compile_error().into(); + } + } + + let item = syn::parse_macro_input!(item as syn::ItemMod); + match parse::Def::try_from(item, dev_mode) { + Ok(def) => expand::expand(def).into(), + Err(e) => e.to_compile_error().into(), + } } diff --git a/support/procedural-fork/src/pallet/parse/call.rs b/support/procedural-fork/src/pallet/parse/call.rs index 4e09b86fd..865c63473 100644 --- a/support/procedural-fork/src/pallet/parse/call.rs +++ b/support/procedural-fork/src/pallet/parse/call.rs @@ -24,124 +24,124 @@ use syn::{spanned::Spanned, ExprClosure}; /// List of additional token to be used for parsing. mod keyword { - syn::custom_keyword!(Call); - syn::custom_keyword!(OriginFor); - syn::custom_keyword!(RuntimeOrigin); - syn::custom_keyword!(weight); - syn::custom_keyword!(call_index); - syn::custom_keyword!(compact); - syn::custom_keyword!(T); - syn::custom_keyword!(pallet); - syn::custom_keyword!(feeless_if); + syn::custom_keyword!(Call); + syn::custom_keyword!(OriginFor); + syn::custom_keyword!(RuntimeOrigin); + syn::custom_keyword!(weight); + syn::custom_keyword!(call_index); + syn::custom_keyword!(compact); + syn::custom_keyword!(T); + syn::custom_keyword!(pallet); + syn::custom_keyword!(feeless_if); } /// Definition of dispatchables typically `impl Pallet { ... }` pub struct CallDef { - /// The where_clause used. - pub where_clause: Option, - /// A set of usage of instance, must be check for consistency with trait. - pub instances: Vec, - /// The index of call item in pallet module. - pub index: usize, - /// Information on methods (used for expansion). - pub methods: Vec, - /// The span of the pallet::call attribute. - pub attr_span: proc_macro2::Span, - /// Docs, specified on the impl Block. - pub docs: Vec, - /// The optional `weight` attribute on the `pallet::call`. - pub inherited_call_weight: Option, + /// The where_clause used. + pub where_clause: Option, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Vec, + /// The index of call item in pallet module. + pub index: usize, + /// Information on methods (used for expansion). + pub methods: Vec, + /// The span of the pallet::call attribute. + pub attr_span: proc_macro2::Span, + /// Docs, specified on the impl Block. + pub docs: Vec, + /// The optional `weight` attribute on the `pallet::call`. + pub inherited_call_weight: Option, } /// The weight of a call. #[derive(Clone)] pub enum CallWeightDef { - /// Explicitly set on the call itself with `#[pallet::weight(…)]`. This value is used. - Immediate(syn::Expr), + /// Explicitly set on the call itself with `#[pallet::weight(…)]`. This value is used. + Immediate(syn::Expr), - /// The default value that should be set for dev-mode pallets. Usually zero. - DevModeDefault, + /// The default value that should be set for dev-mode pallets. Usually zero. + DevModeDefault, - /// Inherits whatever value is configured on the pallet level. - /// - /// The concrete value is not known at this point. - Inherited, + /// Inherits whatever value is configured on the pallet level. + /// + /// The concrete value is not known at this point. + Inherited, } /// Definition of dispatchable typically: `#[weight...] fn foo(origin .., param1: ...) -> ..` #[derive(Clone)] pub struct CallVariantDef { - /// Function name. - pub name: syn::Ident, - /// Information on args: `(is_compact, name, type)` - pub args: Vec<(bool, syn::Ident, Box)>, - /// Weight for the call. - pub weight: CallWeightDef, - /// Call index of the dispatchable. - pub call_index: u8, - /// Whether an explicit call index was specified. - pub explicit_call_index: bool, - /// Docs, used for metadata. - pub docs: Vec, - /// Attributes annotated at the top of the dispatchable function. - pub attrs: Vec, - /// The `cfg` attributes. - pub cfg_attrs: Vec, - /// The optional `feeless_if` attribute on the `pallet::call`. - pub feeless_check: Option, + /// Function name. + pub name: syn::Ident, + /// Information on args: `(is_compact, name, type)` + pub args: Vec<(bool, syn::Ident, Box)>, + /// Weight for the call. + pub weight: CallWeightDef, + /// Call index of the dispatchable. + pub call_index: u8, + /// Whether an explicit call index was specified. + pub explicit_call_index: bool, + /// Docs, used for metadata. + pub docs: Vec, + /// Attributes annotated at the top of the dispatchable function. + pub attrs: Vec, + /// The `cfg` attributes. + pub cfg_attrs: Vec, + /// The optional `feeless_if` attribute on the `pallet::call`. + pub feeless_check: Option, } /// Attributes for functions in call impl block. pub enum FunctionAttr { - /// Parse for `#[pallet::call_index(expr)]` - CallIndex(u8), - /// Parse for `#[pallet::weight(expr)]` - Weight(syn::Expr), - /// Parse for `#[pallet::feeless_if(expr)]` - FeelessIf(Span, syn::ExprClosure), + /// Parse for `#[pallet::call_index(expr)]` + CallIndex(u8), + /// Parse for `#[pallet::weight(expr)]` + Weight(syn::Expr), + /// Parse for `#[pallet::feeless_if(expr)]` + FeelessIf(Span, syn::ExprClosure), } impl syn::parse::Parse for FunctionAttr { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - let content; - syn::bracketed!(content in input); - content.parse::()?; - content.parse::()?; - - let lookahead = content.lookahead1(); - if lookahead.peek(keyword::weight) { - content.parse::()?; - let weight_content; - syn::parenthesized!(weight_content in content); - Ok(FunctionAttr::Weight(weight_content.parse::()?)) - } else if lookahead.peek(keyword::call_index) { - content.parse::()?; - let call_index_content; - syn::parenthesized!(call_index_content in content); - let index = call_index_content.parse::()?; - if !index.suffix().is_empty() { - let msg = "Number literal must not have a suffix"; - return Err(syn::Error::new(index.span(), msg)) - } - Ok(FunctionAttr::CallIndex(index.base10_parse()?)) - } else if lookahead.peek(keyword::feeless_if) { - content.parse::()?; - let closure_content; - syn::parenthesized!(closure_content in content); - Ok(FunctionAttr::FeelessIf( - closure_content.span(), - closure_content.parse::().map_err(|e| { - let msg = "Invalid feeless_if attribute: expected a closure"; - let mut err = syn::Error::new(closure_content.span(), msg); - err.combine(e); - err - })?, - )) - } else { - Err(lookahead.error()) - } - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + let lookahead = content.lookahead1(); + if lookahead.peek(keyword::weight) { + content.parse::()?; + let weight_content; + syn::parenthesized!(weight_content in content); + Ok(FunctionAttr::Weight(weight_content.parse::()?)) + } else if lookahead.peek(keyword::call_index) { + content.parse::()?; + let call_index_content; + syn::parenthesized!(call_index_content in content); + let index = call_index_content.parse::()?; + if !index.suffix().is_empty() { + let msg = "Number literal must not have a suffix"; + return Err(syn::Error::new(index.span(), msg)); + } + Ok(FunctionAttr::CallIndex(index.base10_parse()?)) + } else if lookahead.peek(keyword::feeless_if) { + content.parse::()?; + let closure_content; + syn::parenthesized!(closure_content in content); + Ok(FunctionAttr::FeelessIf( + closure_content.span(), + closure_content.parse::().map_err(|e| { + let msg = "Invalid feeless_if attribute: expected a closure"; + let mut err = syn::Error::new(closure_content.span(), msg); + err.combine(e); + err + })?, + )) + } else { + Err(lookahead.error()) + } + } } /// Attribute for arguments in function in call impl block. @@ -149,319 +149,324 @@ impl syn::parse::Parse for FunctionAttr { pub struct ArgAttrIsCompact; impl syn::parse::Parse for ArgAttrIsCompact { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - let content; - syn::bracketed!(content in input); - content.parse::()?; - content.parse::()?; - - content.parse::()?; - Ok(ArgAttrIsCompact) - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + content.parse::()?; + Ok(ArgAttrIsCompact) + } } /// Check the syntax is `OriginFor`, `&OriginFor` or `T::RuntimeOrigin`. pub fn check_dispatchable_first_arg_type(ty: &syn::Type, is_ref: bool) -> syn::Result<()> { - pub struct CheckOriginFor(bool); - impl syn::parse::Parse for CheckOriginFor { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let is_ref = input.parse::().is_ok(); - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::]>()?; - - Ok(Self(is_ref)) - } - } - - pub struct CheckRuntimeOrigin; - impl syn::parse::Parse for CheckRuntimeOrigin { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - input.parse::()?; - input.parse::()?; - - Ok(Self) - } - } - - let result_origin_for = syn::parse2::(ty.to_token_stream()); - let result_runtime_origin = syn::parse2::(ty.to_token_stream()); - return match (result_origin_for, result_runtime_origin) { - (Ok(CheckOriginFor(has_ref)), _) if is_ref == has_ref => Ok(()), - (_, Ok(_)) => Ok(()), - (_, _) => { - let msg = if is_ref { - "Invalid type: expected `&OriginFor`" - } else { - "Invalid type: expected `OriginFor` or `T::RuntimeOrigin`" - }; - return Err(syn::Error::new(ty.span(), msg)) - }, - } + pub struct CheckOriginFor(bool); + impl syn::parse::Parse for CheckOriginFor { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let is_ref = input.parse::().is_ok(); + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::]>()?; + + Ok(Self(is_ref)) + } + } + + pub struct CheckRuntimeOrigin; + impl syn::parse::Parse for CheckRuntimeOrigin { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + input.parse::()?; + input.parse::()?; + + Ok(Self) + } + } + + let result_origin_for = syn::parse2::(ty.to_token_stream()); + let result_runtime_origin = syn::parse2::(ty.to_token_stream()); + return match (result_origin_for, result_runtime_origin) { + (Ok(CheckOriginFor(has_ref)), _) if is_ref == has_ref => Ok(()), + (_, Ok(_)) => Ok(()), + (_, _) => { + let msg = if is_ref { + "Invalid type: expected `&OriginFor`" + } else { + "Invalid type: expected `OriginFor` or `T::RuntimeOrigin`" + }; + return Err(syn::Error::new(ty.span(), msg)); + } + }; } impl CallDef { - pub fn try_from( - attr_span: proc_macro2::Span, - index: usize, - item: &mut syn::Item, - dev_mode: bool, - inherited_call_weight: Option, - ) -> syn::Result { - let item_impl = if let syn::Item::Impl(item) = item { - item - } else { - return Err(syn::Error::new(item.span(), "Invalid pallet::call, expected item impl")) - }; - - let instances = vec![ - helper::check_impl_gen(&item_impl.generics, item_impl.impl_token.span())?, - helper::check_pallet_struct_usage(&item_impl.self_ty)?, - ]; - - if let Some((_, _, for_)) = item_impl.trait_ { - let msg = "Invalid pallet::call, expected no trait ident as in \ + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + dev_mode: bool, + inherited_call_weight: Option, + ) -> syn::Result { + let item_impl = if let syn::Item::Impl(item) = item { + item + } else { + return Err(syn::Error::new( + item.span(), + "Invalid pallet::call, expected item impl", + )); + }; + + let instances = vec![ + helper::check_impl_gen(&item_impl.generics, item_impl.impl_token.span())?, + helper::check_pallet_struct_usage(&item_impl.self_ty)?, + ]; + + if let Some((_, _, for_)) = item_impl.trait_ { + let msg = "Invalid pallet::call, expected no trait ident as in \ `impl<..> Pallet<..> { .. }`"; - return Err(syn::Error::new(for_.span(), msg)) - } - - let mut methods = vec![]; - let mut indices = HashMap::new(); - let mut last_index: Option = None; - for item in &mut item_impl.items { - if let syn::ImplItem::Fn(method) = item { - if !matches!(method.vis, syn::Visibility::Public(_)) { - let msg = "Invalid pallet::call, dispatchable function must be public: \ + return Err(syn::Error::new(for_.span(), msg)); + } + + let mut methods = vec![]; + let mut indices = HashMap::new(); + let mut last_index: Option = None; + for item in &mut item_impl.items { + if let syn::ImplItem::Fn(method) = item { + if !matches!(method.vis, syn::Visibility::Public(_)) { + let msg = "Invalid pallet::call, dispatchable function must be public: \ `pub fn`"; - let span = match method.vis { - syn::Visibility::Inherited => method.sig.span(), - _ => method.vis.span(), - }; - - return Err(syn::Error::new(span, msg)) - } - - match method.sig.inputs.first() { - None => { - let msg = "Invalid pallet::call, must have at least origin arg"; - return Err(syn::Error::new(method.sig.span(), msg)) - }, - Some(syn::FnArg::Receiver(_)) => { - let msg = "Invalid pallet::call, first argument must be a typed argument, \ + let span = match method.vis { + syn::Visibility::Inherited => method.sig.span(), + _ => method.vis.span(), + }; + + return Err(syn::Error::new(span, msg)); + } + + match method.sig.inputs.first() { + None => { + let msg = "Invalid pallet::call, must have at least origin arg"; + return Err(syn::Error::new(method.sig.span(), msg)); + } + Some(syn::FnArg::Receiver(_)) => { + let msg = "Invalid pallet::call, first argument must be a typed argument, \ e.g. `origin: OriginFor`"; - return Err(syn::Error::new(method.sig.span(), msg)) - }, - Some(syn::FnArg::Typed(arg)) => { - check_dispatchable_first_arg_type(&arg.ty, false)?; - }, - } - - if let syn::ReturnType::Type(_, type_) = &method.sig.output { - helper::check_pallet_call_return_type(type_)?; - } else { - let msg = "Invalid pallet::call, require return type \ + return Err(syn::Error::new(method.sig.span(), msg)); + } + Some(syn::FnArg::Typed(arg)) => { + check_dispatchable_first_arg_type(&arg.ty, false)?; + } + } + + if let syn::ReturnType::Type(_, type_) = &method.sig.output { + helper::check_pallet_call_return_type(type_)?; + } else { + let msg = "Invalid pallet::call, require return type \ DispatchResultWithPostInfo"; - return Err(syn::Error::new(method.sig.span(), msg)) - } - - let cfg_attrs: Vec = helper::get_item_cfg_attrs(&method.attrs); - let mut call_idx_attrs = vec![]; - let mut weight_attrs = vec![]; - let mut feeless_attrs = vec![]; - for attr in helper::take_item_pallet_attrs(&mut method.attrs)?.into_iter() { - match attr { - FunctionAttr::CallIndex(_) => { - call_idx_attrs.push(attr); - }, - FunctionAttr::Weight(_) => { - weight_attrs.push(attr); - }, - FunctionAttr::FeelessIf(span, _) => { - feeless_attrs.push((span, attr)); - }, - } - } - - if weight_attrs.is_empty() && dev_mode { - // inject a default O(1) weight when dev mode is enabled and no weight has - // been specified on the call - let empty_weight: syn::Expr = syn::parse_quote!(0); - weight_attrs.push(FunctionAttr::Weight(empty_weight)); - } - - let weight = match weight_attrs.len() { - 0 if inherited_call_weight.is_some() => CallWeightDef::Inherited, - 0 if dev_mode => CallWeightDef::DevModeDefault, - 0 => return Err(syn::Error::new( - method.sig.span(), - "A pallet::call requires either a concrete `#[pallet::weight($expr)]` or an + return Err(syn::Error::new(method.sig.span(), msg)); + } + + let cfg_attrs: Vec = helper::get_item_cfg_attrs(&method.attrs); + let mut call_idx_attrs = vec![]; + let mut weight_attrs = vec![]; + let mut feeless_attrs = vec![]; + for attr in helper::take_item_pallet_attrs(&mut method.attrs)?.into_iter() { + match attr { + FunctionAttr::CallIndex(_) => { + call_idx_attrs.push(attr); + } + FunctionAttr::Weight(_) => { + weight_attrs.push(attr); + } + FunctionAttr::FeelessIf(span, _) => { + feeless_attrs.push((span, attr)); + } + } + } + + if weight_attrs.is_empty() && dev_mode { + // inject a default O(1) weight when dev mode is enabled and no weight has + // been specified on the call + let empty_weight: syn::Expr = syn::parse_quote!(0); + weight_attrs.push(FunctionAttr::Weight(empty_weight)); + } + + let weight = match weight_attrs.len() { + 0 if inherited_call_weight.is_some() => CallWeightDef::Inherited, + 0 if dev_mode => CallWeightDef::DevModeDefault, + 0 => return Err(syn::Error::new( + method.sig.span(), + "A pallet::call requires either a concrete `#[pallet::weight($expr)]` or an inherited weight from the `#[pallet:call(weight($type))]` attribute, but none were given.", - )), - 1 => match weight_attrs.pop().unwrap() { - FunctionAttr::Weight(w) => CallWeightDef::Immediate(w), - _ => unreachable!("checked during creation of the let binding"), - }, - _ => { - let msg = "Invalid pallet::call, too many weight attributes given"; - return Err(syn::Error::new(method.sig.span(), msg)) - }, - }; - - if call_idx_attrs.len() > 1 { - let msg = "Invalid pallet::call, too many call_index attributes given"; - return Err(syn::Error::new(method.sig.span(), msg)) - } - let call_index = call_idx_attrs.pop().map(|attr| match attr { - FunctionAttr::CallIndex(idx) => idx, - _ => unreachable!("checked during creation of the let binding"), - }); - let explicit_call_index = call_index.is_some(); - - let final_index = match call_index { - Some(i) => i, - None => - last_index.map_or(Some(0), |idx| idx.checked_add(1)).ok_or_else(|| { - let msg = "Call index doesn't fit into u8, index is 256"; - syn::Error::new(method.sig.span(), msg) - })?, - }; - last_index = Some(final_index); - - if let Some(used_fn) = indices.insert(final_index, method.sig.ident.clone()) { - let msg = format!( - "Call indices are conflicting: Both functions {} and {} are at index {}", - used_fn, method.sig.ident, final_index, - ); - let mut err = syn::Error::new(used_fn.span(), &msg); - err.combine(syn::Error::new(method.sig.ident.span(), msg)); - return Err(err) - } - - let mut args = vec![]; - for arg in method.sig.inputs.iter_mut().skip(1) { - let arg = if let syn::FnArg::Typed(arg) = arg { - arg - } else { - unreachable!("Only first argument can be receiver"); - }; - - let arg_attrs: Vec = - helper::take_item_pallet_attrs(&mut arg.attrs)?; - - if arg_attrs.len() > 1 { - let msg = "Invalid pallet::call, argument has too many attributes"; - return Err(syn::Error::new(arg.span(), msg)) - } - - let arg_ident = if let syn::Pat::Ident(pat) = &*arg.pat { - pat.ident.clone() - } else { - let msg = "Invalid pallet::call, argument must be ident"; - return Err(syn::Error::new(arg.pat.span(), msg)) - }; - - args.push((!arg_attrs.is_empty(), arg_ident, arg.ty.clone())); - } - - let docs = get_doc_literals(&method.attrs); - - if feeless_attrs.len() > 1 { - let msg = "Invalid pallet::call, there can only be one feeless_if attribute"; - return Err(syn::Error::new(feeless_attrs[1].0, msg)) - } - let feeless_check: Option = - feeless_attrs.pop().map(|(_, attr)| match attr { - FunctionAttr::FeelessIf(_, closure) => closure, - _ => unreachable!("checked during creation of the let binding"), - }); - - if let Some(ref feeless_check) = feeless_check { - if feeless_check.inputs.len() != args.len() + 1 { - let msg = "Invalid pallet::call, feeless_if closure must have same \ + )), + 1 => match weight_attrs.pop().unwrap() { + FunctionAttr::Weight(w) => CallWeightDef::Immediate(w), + _ => unreachable!("checked during creation of the let binding"), + }, + _ => { + let msg = "Invalid pallet::call, too many weight attributes given"; + return Err(syn::Error::new(method.sig.span(), msg)); + } + }; + + if call_idx_attrs.len() > 1 { + let msg = "Invalid pallet::call, too many call_index attributes given"; + return Err(syn::Error::new(method.sig.span(), msg)); + } + let call_index = call_idx_attrs.pop().map(|attr| match attr { + FunctionAttr::CallIndex(idx) => idx, + _ => unreachable!("checked during creation of the let binding"), + }); + let explicit_call_index = call_index.is_some(); + + let final_index = match call_index { + Some(i) => i, + None => last_index + .map_or(Some(0), |idx| idx.checked_add(1)) + .ok_or_else(|| { + let msg = "Call index doesn't fit into u8, index is 256"; + syn::Error::new(method.sig.span(), msg) + })?, + }; + last_index = Some(final_index); + + if let Some(used_fn) = indices.insert(final_index, method.sig.ident.clone()) { + let msg = format!( + "Call indices are conflicting: Both functions {} and {} are at index {}", + used_fn, method.sig.ident, final_index, + ); + let mut err = syn::Error::new(used_fn.span(), &msg); + err.combine(syn::Error::new(method.sig.ident.span(), msg)); + return Err(err); + } + + let mut args = vec![]; + for arg in method.sig.inputs.iter_mut().skip(1) { + let arg = if let syn::FnArg::Typed(arg) = arg { + arg + } else { + unreachable!("Only first argument can be receiver"); + }; + + let arg_attrs: Vec = + helper::take_item_pallet_attrs(&mut arg.attrs)?; + + if arg_attrs.len() > 1 { + let msg = "Invalid pallet::call, argument has too many attributes"; + return Err(syn::Error::new(arg.span(), msg)); + } + + let arg_ident = if let syn::Pat::Ident(pat) = &*arg.pat { + pat.ident.clone() + } else { + let msg = "Invalid pallet::call, argument must be ident"; + return Err(syn::Error::new(arg.pat.span(), msg)); + }; + + args.push((!arg_attrs.is_empty(), arg_ident, arg.ty.clone())); + } + + let docs = get_doc_literals(&method.attrs); + + if feeless_attrs.len() > 1 { + let msg = "Invalid pallet::call, there can only be one feeless_if attribute"; + return Err(syn::Error::new(feeless_attrs[1].0, msg)); + } + let feeless_check: Option = + feeless_attrs.pop().map(|(_, attr)| match attr { + FunctionAttr::FeelessIf(_, closure) => closure, + _ => unreachable!("checked during creation of the let binding"), + }); + + if let Some(ref feeless_check) = feeless_check { + if feeless_check.inputs.len() != args.len() + 1 { + let msg = "Invalid pallet::call, feeless_if closure must have same \ number of arguments as the dispatchable function"; - return Err(syn::Error::new(feeless_check.span(), msg)) - } - - match feeless_check.inputs.first() { - None => { - let msg = "Invalid pallet::call, feeless_if closure must have at least origin arg"; - return Err(syn::Error::new(feeless_check.span(), msg)) - }, - Some(syn::Pat::Type(arg)) => { - check_dispatchable_first_arg_type(&arg.ty, true)?; - }, - _ => { - let msg = "Invalid pallet::call, feeless_if closure first argument must be a typed argument, \ + return Err(syn::Error::new(feeless_check.span(), msg)); + } + + match feeless_check.inputs.first() { + None => { + let msg = "Invalid pallet::call, feeless_if closure must have at least origin arg"; + return Err(syn::Error::new(feeless_check.span(), msg)); + } + Some(syn::Pat::Type(arg)) => { + check_dispatchable_first_arg_type(&arg.ty, true)?; + } + _ => { + let msg = "Invalid pallet::call, feeless_if closure first argument must be a typed argument, \ e.g. `origin: OriginFor`"; - return Err(syn::Error::new(feeless_check.span(), msg)) - }, - } - - for (feeless_arg, arg) in feeless_check.inputs.iter().skip(1).zip(args.iter()) { - let feeless_arg_type = - if let syn::Pat::Type(syn::PatType { ty, .. }) = feeless_arg.clone() { - if let syn::Type::Reference(pat) = *ty { - pat.elem.clone() - } else { - let msg = "Invalid pallet::call, feeless_if closure argument must be a reference"; - return Err(syn::Error::new(ty.span(), msg)) - } - } else { - let msg = "Invalid pallet::call, feeless_if closure argument must be a type ascription pattern"; - return Err(syn::Error::new(feeless_arg.span(), msg)) - }; - - if feeless_arg_type != arg.2 { - let msg = - "Invalid pallet::call, feeless_if closure argument must have \ + return Err(syn::Error::new(feeless_check.span(), msg)); + } + } + + for (feeless_arg, arg) in feeless_check.inputs.iter().skip(1).zip(args.iter()) { + let feeless_arg_type = if let syn::Pat::Type(syn::PatType { ty, .. }) = + feeless_arg.clone() + { + if let syn::Type::Reference(pat) = *ty { + pat.elem.clone() + } else { + let msg = "Invalid pallet::call, feeless_if closure argument must be a reference"; + return Err(syn::Error::new(ty.span(), msg)); + } + } else { + let msg = "Invalid pallet::call, feeless_if closure argument must be a type ascription pattern"; + return Err(syn::Error::new(feeless_arg.span(), msg)); + }; + + if feeless_arg_type != arg.2 { + let msg = + "Invalid pallet::call, feeless_if closure argument must have \ a reference to the same type as the dispatchable function argument"; - return Err(syn::Error::new(feeless_arg.span(), msg)) - } - } - - let valid_return = match &feeless_check.output { - syn::ReturnType::Type(_, type_) => match *(type_.clone()) { - syn::Type::Path(syn::TypePath { path, .. }) => path.is_ident("bool"), - _ => false, - }, - _ => false, - }; - if !valid_return { - let msg = "Invalid pallet::call, feeless_if closure must return `bool`"; - return Err(syn::Error::new(feeless_check.output.span(), msg)) - } - } - - methods.push(CallVariantDef { - name: method.sig.ident.clone(), - weight, - call_index: final_index, - explicit_call_index, - args, - docs, - attrs: method.attrs.clone(), - cfg_attrs, - feeless_check, - }); - } else { - let msg = "Invalid pallet::call, only method accepted"; - return Err(syn::Error::new(item.span(), msg)) - } - } - - Ok(Self { - index, - attr_span, - instances, - methods, - where_clause: item_impl.generics.where_clause.clone(), - docs: get_doc_literals(&item_impl.attrs), - inherited_call_weight, - }) - } + return Err(syn::Error::new(feeless_arg.span(), msg)); + } + } + + let valid_return = match &feeless_check.output { + syn::ReturnType::Type(_, type_) => match *(type_.clone()) { + syn::Type::Path(syn::TypePath { path, .. }) => path.is_ident("bool"), + _ => false, + }, + _ => false, + }; + if !valid_return { + let msg = "Invalid pallet::call, feeless_if closure must return `bool`"; + return Err(syn::Error::new(feeless_check.output.span(), msg)); + } + } + + methods.push(CallVariantDef { + name: method.sig.ident.clone(), + weight, + call_index: final_index, + explicit_call_index, + args, + docs, + attrs: method.attrs.clone(), + cfg_attrs, + feeless_check, + }); + } else { + let msg = "Invalid pallet::call, only method accepted"; + return Err(syn::Error::new(item.span(), msg)); + } + } + + Ok(Self { + index, + attr_span, + instances, + methods, + where_clause: item_impl.generics.where_clause.clone(), + docs: get_doc_literals(&item_impl.attrs), + inherited_call_weight, + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/composite.rs b/support/procedural-fork/src/pallet/parse/composite.rs index c3ac74846..38da1f205 100644 --- a/support/procedural-fork/src/pallet/parse/composite.rs +++ b/support/procedural-fork/src/pallet/parse/composite.rs @@ -20,172 +20,178 @@ use quote::ToTokens; use syn::spanned::Spanned; pub mod keyword { - use super::*; - - syn::custom_keyword!(FreezeReason); - syn::custom_keyword!(HoldReason); - syn::custom_keyword!(LockId); - syn::custom_keyword!(SlashReason); - syn::custom_keyword!(Task); - - pub enum CompositeKeyword { - FreezeReason(FreezeReason), - HoldReason(HoldReason), - LockId(LockId), - SlashReason(SlashReason), - Task(Task), - } - - impl ToTokens for CompositeKeyword { - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - use CompositeKeyword::*; - match self { - FreezeReason(inner) => inner.to_tokens(tokens), - HoldReason(inner) => inner.to_tokens(tokens), - LockId(inner) => inner.to_tokens(tokens), - SlashReason(inner) => inner.to_tokens(tokens), - Task(inner) => inner.to_tokens(tokens), - } - } - } - - impl syn::parse::Parse for CompositeKeyword { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let lookahead = input.lookahead1(); - if lookahead.peek(FreezeReason) { - Ok(Self::FreezeReason(input.parse()?)) - } else if lookahead.peek(HoldReason) { - Ok(Self::HoldReason(input.parse()?)) - } else if lookahead.peek(LockId) { - Ok(Self::LockId(input.parse()?)) - } else if lookahead.peek(SlashReason) { - Ok(Self::SlashReason(input.parse()?)) - } else if lookahead.peek(Task) { - Ok(Self::Task(input.parse()?)) - } else { - Err(lookahead.error()) - } - } - } - - impl std::fmt::Display for CompositeKeyword { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use CompositeKeyword::*; - write!( - f, - "{}", - match self { - FreezeReason(_) => "FreezeReason", - HoldReason(_) => "HoldReason", - Task(_) => "Task", - LockId(_) => "LockId", - SlashReason(_) => "SlashReason", - } - ) - } - } + use super::*; + + syn::custom_keyword!(FreezeReason); + syn::custom_keyword!(HoldReason); + syn::custom_keyword!(LockId); + syn::custom_keyword!(SlashReason); + syn::custom_keyword!(Task); + + pub enum CompositeKeyword { + FreezeReason(FreezeReason), + HoldReason(HoldReason), + LockId(LockId), + SlashReason(SlashReason), + Task(Task), + } + + impl ToTokens for CompositeKeyword { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + use CompositeKeyword::*; + match self { + FreezeReason(inner) => inner.to_tokens(tokens), + HoldReason(inner) => inner.to_tokens(tokens), + LockId(inner) => inner.to_tokens(tokens), + SlashReason(inner) => inner.to_tokens(tokens), + Task(inner) => inner.to_tokens(tokens), + } + } + } + + impl syn::parse::Parse for CompositeKeyword { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let lookahead = input.lookahead1(); + if lookahead.peek(FreezeReason) { + Ok(Self::FreezeReason(input.parse()?)) + } else if lookahead.peek(HoldReason) { + Ok(Self::HoldReason(input.parse()?)) + } else if lookahead.peek(LockId) { + Ok(Self::LockId(input.parse()?)) + } else if lookahead.peek(SlashReason) { + Ok(Self::SlashReason(input.parse()?)) + } else if lookahead.peek(Task) { + Ok(Self::Task(input.parse()?)) + } else { + Err(lookahead.error()) + } + } + } + + impl std::fmt::Display for CompositeKeyword { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use CompositeKeyword::*; + write!( + f, + "{}", + match self { + FreezeReason(_) => "FreezeReason", + HoldReason(_) => "HoldReason", + Task(_) => "Task", + LockId(_) => "LockId", + SlashReason(_) => "SlashReason", + } + ) + } + } } pub struct CompositeDef { - /// The index of the CompositeDef item in the pallet module. - pub index: usize, - /// The composite keyword used (contains span). - pub composite_keyword: keyword::CompositeKeyword, - /// Name of the associated type. - pub ident: syn::Ident, - /// Type parameters and where clause attached to a declaration of the pallet::composite_enum. - pub generics: syn::Generics, - /// The span of the pallet::composite_enum attribute. - pub attr_span: proc_macro2::Span, - /// Variant count of the pallet::composite_enum. - pub variant_count: u32, + /// The index of the CompositeDef item in the pallet module. + pub index: usize, + /// The composite keyword used (contains span). + pub composite_keyword: keyword::CompositeKeyword, + /// Name of the associated type. + pub ident: syn::Ident, + /// Type parameters and where clause attached to a declaration of the pallet::composite_enum. + pub generics: syn::Generics, + /// The span of the pallet::composite_enum attribute. + pub attr_span: proc_macro2::Span, + /// Variant count of the pallet::composite_enum. + pub variant_count: u32, } impl CompositeDef { - pub fn try_from( - attr_span: proc_macro2::Span, - index: usize, - scrate: &syn::Path, - item: &mut syn::Item, - ) -> syn::Result { - let item = if let syn::Item::Enum(item) = item { - // check variants: composite enums support only field-less enum variants. This is - // because fields can introduce too many possibilities, making it challenging to compute - // a fixed variant count. - for variant in &item.variants { - match variant.fields { - syn::Fields::Named(_) | syn::Fields::Unnamed(_) => - return Err(syn::Error::new( - variant.ident.span(), - "The composite enum does not support variants with fields!", - )), - syn::Fields::Unit => (), - } - } - item - } else { - return Err(syn::Error::new( - item.span(), - "Invalid pallet::composite_enum, expected enum item", - )) - }; - - if !matches!(item.vis, syn::Visibility::Public(_)) { - let msg = format!("Invalid pallet::composite_enum, `{}` must be public", item.ident); - return Err(syn::Error::new(item.span(), msg)) - } - - let has_instance = if item.generics.params.first().is_some() { - helper::check_config_def_gen(&item.generics, item.ident.span())?; - true - } else { - false - }; - - let has_derive_attr = item.attrs.iter().any(|attr| { - if let syn::Meta::List(syn::MetaList { path, .. }) = &attr.meta { - path.get_ident().map(|ident| ident == "derive").unwrap_or(false) - } else { - false - } - }); - - if !has_derive_attr { - let derive_attr: syn::Attribute = syn::parse_quote! { - #[derive( - Copy, Clone, Eq, PartialEq, - #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, - #scrate::__private::scale_info::TypeInfo, - #scrate::__private::RuntimeDebug, - )] - }; - item.attrs.push(derive_attr); - } - - if has_instance { - item.attrs.push(syn::parse_quote! { - #[scale_info(skip_type_params(I))] - }); - - item.variants.push(syn::parse_quote! { - #[doc(hidden)] - #[codec(skip)] - __Ignore( - ::core::marker::PhantomData, - ) - }); - } - - let composite_keyword = - syn::parse2::(item.ident.to_token_stream())?; - - Ok(CompositeDef { - index, - composite_keyword, - attr_span, - generics: item.generics.clone(), - variant_count: item.variants.len() as u32, - ident: item.ident.clone(), - }) - } + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + scrate: &syn::Path, + item: &mut syn::Item, + ) -> syn::Result { + let item = if let syn::Item::Enum(item) = item { + // check variants: composite enums support only field-less enum variants. This is + // because fields can introduce too many possibilities, making it challenging to compute + // a fixed variant count. + for variant in &item.variants { + match variant.fields { + syn::Fields::Named(_) | syn::Fields::Unnamed(_) => { + return Err(syn::Error::new( + variant.ident.span(), + "The composite enum does not support variants with fields!", + )) + } + syn::Fields::Unit => (), + } + } + item + } else { + return Err(syn::Error::new( + item.span(), + "Invalid pallet::composite_enum, expected enum item", + )); + }; + + if !matches!(item.vis, syn::Visibility::Public(_)) { + let msg = format!( + "Invalid pallet::composite_enum, `{}` must be public", + item.ident + ); + return Err(syn::Error::new(item.span(), msg)); + } + + let has_instance = if item.generics.params.first().is_some() { + helper::check_config_def_gen(&item.generics, item.ident.span())?; + true + } else { + false + }; + + let has_derive_attr = item.attrs.iter().any(|attr| { + if let syn::Meta::List(syn::MetaList { path, .. }) = &attr.meta { + path.get_ident() + .map(|ident| ident == "derive") + .unwrap_or(false) + } else { + false + } + }); + + if !has_derive_attr { + let derive_attr: syn::Attribute = syn::parse_quote! { + #[derive( + Copy, Clone, Eq, PartialEq, + #scrate::__private::codec::Encode, #scrate::__private::codec::Decode, #scrate::__private::codec::MaxEncodedLen, + #scrate::__private::scale_info::TypeInfo, + #scrate::__private::RuntimeDebug, + )] + }; + item.attrs.push(derive_attr); + } + + if has_instance { + item.attrs.push(syn::parse_quote! { + #[scale_info(skip_type_params(I))] + }); + + item.variants.push(syn::parse_quote! { + #[doc(hidden)] + #[codec(skip)] + __Ignore( + ::core::marker::PhantomData, + ) + }); + } + + let composite_keyword = + syn::parse2::(item.ident.to_token_stream())?; + + Ok(CompositeDef { + index, + composite_keyword, + attr_span, + generics: item.generics.clone(), + variant_count: item.variants.len() as u32, + ident: item.ident.clone(), + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/config.rs b/support/procedural-fork/src/pallet/parse/config.rs index fbab92db1..cde565245 100644 --- a/support/procedural-fork/src/pallet/parse/config.rs +++ b/support/procedural-fork/src/pallet/parse/config.rs @@ -22,569 +22,592 @@ use syn::{spanned::Spanned, token, Token}; /// List of additional token to be used for parsing. mod keyword { - syn::custom_keyword!(Config); - syn::custom_keyword!(From); - syn::custom_keyword!(T); - syn::custom_keyword!(I); - syn::custom_keyword!(config); - syn::custom_keyword!(pallet); - syn::custom_keyword!(IsType); - syn::custom_keyword!(RuntimeEvent); - syn::custom_keyword!(Event); - syn::custom_keyword!(frame_system); - syn::custom_keyword!(disable_frame_system_supertrait_check); - syn::custom_keyword!(no_default); - syn::custom_keyword!(no_default_bounds); - syn::custom_keyword!(constant); + syn::custom_keyword!(Config); + syn::custom_keyword!(From); + syn::custom_keyword!(T); + syn::custom_keyword!(I); + syn::custom_keyword!(config); + syn::custom_keyword!(pallet); + syn::custom_keyword!(IsType); + syn::custom_keyword!(RuntimeEvent); + syn::custom_keyword!(Event); + syn::custom_keyword!(frame_system); + syn::custom_keyword!(disable_frame_system_supertrait_check); + syn::custom_keyword!(no_default); + syn::custom_keyword!(no_default_bounds); + syn::custom_keyword!(constant); } #[derive(Default)] pub struct DefaultTrait { - /// A bool for each sub-trait item indicates whether the item has - /// `#[pallet::no_default_bounds]` attached to it. If true, the item will not have any bounds - /// in the generated default sub-trait. - pub items: Vec<(syn::TraitItem, bool)>, - pub has_system: bool, + /// A bool for each sub-trait item indicates whether the item has + /// `#[pallet::no_default_bounds]` attached to it. If true, the item will not have any bounds + /// in the generated default sub-trait. + pub items: Vec<(syn::TraitItem, bool)>, + pub has_system: bool, } /// Input definition for the pallet config. pub struct ConfigDef { - /// The index of item in pallet module. - pub index: usize, - /// Whether the trait has instance (i.e. define with `Config`) - pub has_instance: bool, - /// Const associated type. - pub consts_metadata: Vec, - /// Whether the trait has the associated type `Event`, note that those bounds are - /// checked: - /// * `IsType::RuntimeEvent` - /// * `From` or `From>` or `From>` - pub has_event_type: bool, - /// The where clause on trait definition but modified so `Self` is `T`. - pub where_clause: Option, - /// The span of the pallet::config attribute. - pub attr_span: proc_macro2::Span, - /// Whether a default sub-trait should be generated. - /// - /// Contains default sub-trait items (instantiated by `#[pallet::config(with_default)]`). - /// Vec will be empty if `#[pallet::config(with_default)]` is not specified or if there are - /// no trait items. - pub default_sub_trait: Option, + /// The index of item in pallet module. + pub index: usize, + /// Whether the trait has instance (i.e. define with `Config`) + pub has_instance: bool, + /// Const associated type. + pub consts_metadata: Vec, + /// Whether the trait has the associated type `Event`, note that those bounds are + /// checked: + /// * `IsType::RuntimeEvent` + /// * `From` or `From>` or `From>` + pub has_event_type: bool, + /// The where clause on trait definition but modified so `Self` is `T`. + pub where_clause: Option, + /// The span of the pallet::config attribute. + pub attr_span: proc_macro2::Span, + /// Whether a default sub-trait should be generated. + /// + /// Contains default sub-trait items (instantiated by `#[pallet::config(with_default)]`). + /// Vec will be empty if `#[pallet::config(with_default)]` is not specified or if there are + /// no trait items. + pub default_sub_trait: Option, } /// Input definition for a constant in pallet config. pub struct ConstMetadataDef { - /// Name of the associated type. - pub ident: syn::Ident, - /// The type in Get, e.g. `u32` in `type Foo: Get;`, but `Self` is replaced by `T` - pub type_: syn::Type, - /// The doc associated - pub doc: Vec, + /// Name of the associated type. + pub ident: syn::Ident, + /// The type in Get, e.g. `u32` in `type Foo: Get;`, but `Self` is replaced by `T` + pub type_: syn::Type, + /// The doc associated + pub doc: Vec, } impl TryFrom<&syn::TraitItemType> for ConstMetadataDef { - type Error = syn::Error; - - fn try_from(trait_ty: &syn::TraitItemType) -> Result { - let err = |span, msg| { - syn::Error::new(span, format!("Invalid usage of `#[pallet::constant]`: {}", msg)) - }; - let doc = get_doc_literals(&trait_ty.attrs); - let ident = trait_ty.ident.clone(); - let bound = trait_ty - .bounds - .iter() - .find_map(|b| { - if let syn::TypeParamBound::Trait(tb) = b { - tb.path - .segments - .last() - .and_then(|s| if s.ident == "Get" { Some(s) } else { None }) - } else { - None - } - }) - .ok_or_else(|| err(trait_ty.span(), "`Get` trait bound not found"))?; - let type_arg = if let syn::PathArguments::AngleBracketed(ref ab) = bound.arguments { - if ab.args.len() == 1 { - if let syn::GenericArgument::Type(ref ty) = ab.args[0] { - Ok(ty) - } else { - Err(err(ab.args[0].span(), "Expected a type argument")) - } - } else { - Err(err(bound.span(), "Expected a single type argument")) - } - } else { - Err(err(bound.span(), "Expected trait generic args")) - }?; - let type_ = syn::parse2::(replace_self_by_t(type_arg.to_token_stream())) - .expect("Internal error: replacing `Self` by `T` should result in valid type"); - - Ok(Self { ident, type_, doc }) - } + type Error = syn::Error; + + fn try_from(trait_ty: &syn::TraitItemType) -> Result { + let err = |span, msg| { + syn::Error::new( + span, + format!("Invalid usage of `#[pallet::constant]`: {}", msg), + ) + }; + let doc = get_doc_literals(&trait_ty.attrs); + let ident = trait_ty.ident.clone(); + let bound = trait_ty + .bounds + .iter() + .find_map(|b| { + if let syn::TypeParamBound::Trait(tb) = b { + tb.path + .segments + .last() + .and_then(|s| if s.ident == "Get" { Some(s) } else { None }) + } else { + None + } + }) + .ok_or_else(|| err(trait_ty.span(), "`Get` trait bound not found"))?; + let type_arg = if let syn::PathArguments::AngleBracketed(ref ab) = bound.arguments { + if ab.args.len() == 1 { + if let syn::GenericArgument::Type(ref ty) = ab.args[0] { + Ok(ty) + } else { + Err(err(ab.args[0].span(), "Expected a type argument")) + } + } else { + Err(err(bound.span(), "Expected a single type argument")) + } + } else { + Err(err(bound.span(), "Expected trait generic args")) + }?; + let type_ = syn::parse2::(replace_self_by_t(type_arg.to_token_stream())) + .expect("Internal error: replacing `Self` by `T` should result in valid type"); + + Ok(Self { ident, type_, doc }) + } } /// Parse for `#[pallet::disable_frame_system_supertrait_check]` pub struct DisableFrameSystemSupertraitCheck; impl syn::parse::Parse for DisableFrameSystemSupertraitCheck { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - let content; - syn::bracketed!(content in input); - content.parse::()?; - content.parse::()?; - - content.parse::()?; - Ok(Self) - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + content.parse::()?; + Ok(Self) + } } /// Parsing for the `typ` portion of `PalletAttr` #[derive(derive_syn_parse::Parse, PartialEq, Eq)] pub enum PalletAttrType { - #[peek(keyword::no_default, name = "no_default")] - NoDefault(keyword::no_default), - #[peek(keyword::no_default_bounds, name = "no_default_bounds")] - NoBounds(keyword::no_default_bounds), - #[peek(keyword::constant, name = "constant")] - Constant(keyword::constant), + #[peek(keyword::no_default, name = "no_default")] + NoDefault(keyword::no_default), + #[peek(keyword::no_default_bounds, name = "no_default_bounds")] + NoBounds(keyword::no_default_bounds), + #[peek(keyword::constant, name = "constant")] + Constant(keyword::constant), } /// Parsing for `#[pallet::X]` #[derive(derive_syn_parse::Parse)] pub struct PalletAttr { - _pound: Token![#], - #[bracket] - _bracket: token::Bracket, - #[inside(_bracket)] - _pallet: keyword::pallet, - #[prefix(Token![::] in _bracket)] - #[inside(_bracket)] - typ: PalletAttrType, + _pound: Token![#], + #[bracket] + _bracket: token::Bracket, + #[inside(_bracket)] + _pallet: keyword::pallet, + #[prefix(Token![::] in _bracket)] + #[inside(_bracket)] + typ: PalletAttrType, } /// Parse for `IsType<::RuntimeEvent>` and retrieve `$path` pub struct IsTypeBoundEventParse(syn::Path); impl syn::parse::Parse for IsTypeBoundEventParse { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - let config_path = input.parse::()?; - input.parse::]>()?; - input.parse::()?; - input.parse::()?; - input.parse::]>()?; - - Ok(Self(config_path)) - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + let config_path = input.parse::()?; + input.parse::]>()?; + input.parse::()?; + input.parse::()?; + input.parse::]>()?; + + Ok(Self(config_path)) + } } /// Parse for `From` or `From>` or `From>` pub struct FromEventParse { - is_generic: bool, - has_instance: bool, + is_generic: bool, + has_instance: bool, } impl syn::parse::Parse for FromEventParse { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let mut is_generic = false; - let mut has_instance = false; - - input.parse::()?; - input.parse::()?; - input.parse::()?; - if input.peek(syn::Token![<]) { - is_generic = true; - input.parse::()?; - input.parse::()?; - if input.peek(syn::Token![,]) { - input.parse::()?; - input.parse::()?; - has_instance = true; - } - input.parse::]>()?; - } - input.parse::]>()?; - - Ok(Self { is_generic, has_instance }) - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut is_generic = false; + let mut has_instance = false; + + input.parse::()?; + input.parse::()?; + input.parse::()?; + if input.peek(syn::Token![<]) { + is_generic = true; + input.parse::()?; + input.parse::()?; + if input.peek(syn::Token![,]) { + input.parse::()?; + input.parse::()?; + has_instance = true; + } + input.parse::]>()?; + } + input.parse::]>()?; + + Ok(Self { + is_generic, + has_instance, + }) + } } /// Check if trait_item is `type RuntimeEvent`, if so checks its bounds are those expected. /// (Event type is reserved type) fn check_event_type( - frame_system: &syn::Path, - trait_item: &syn::TraitItem, - trait_has_instance: bool, + frame_system: &syn::Path, + trait_item: &syn::TraitItem, + trait_has_instance: bool, ) -> syn::Result { - if let syn::TraitItem::Type(type_) = trait_item { - if type_.ident == "RuntimeEvent" { - // Check event has no generics - if !type_.generics.params.is_empty() || type_.generics.where_clause.is_some() { - let msg = "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must have\ + if let syn::TraitItem::Type(type_) = trait_item { + if type_.ident == "RuntimeEvent" { + // Check event has no generics + if !type_.generics.params.is_empty() || type_.generics.where_clause.is_some() { + let msg = "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must have\ no generics nor where_clause"; - return Err(syn::Error::new(trait_item.span(), msg)) - } + return Err(syn::Error::new(trait_item.span(), msg)); + } - // Check bound contains IsType and From - let has_is_type_bound = type_.bounds.iter().any(|s| { - syn::parse2::(s.to_token_stream()) - .map_or(false, |b| has_expected_system_config(b.0, frame_system)) - }); + // Check bound contains IsType and From + let has_is_type_bound = type_.bounds.iter().any(|s| { + syn::parse2::(s.to_token_stream()) + .map_or(false, |b| has_expected_system_config(b.0, frame_system)) + }); - if !has_is_type_bound { - let msg = "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must \ + if !has_is_type_bound { + let msg = "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must \ bound: `IsType<::RuntimeEvent>`".to_string(); - return Err(syn::Error::new(type_.span(), msg)) - } - - let from_event_bound = type_ - .bounds - .iter() - .find_map(|s| syn::parse2::(s.to_token_stream()).ok()); - - let from_event_bound = if let Some(b) = from_event_bound { - b - } else { - let msg = "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must \ + return Err(syn::Error::new(type_.span(), msg)); + } + + let from_event_bound = type_ + .bounds + .iter() + .find_map(|s| syn::parse2::(s.to_token_stream()).ok()); + + let from_event_bound = if let Some(b) = from_event_bound { + b + } else { + let msg = "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must \ bound: `From` or `From>` or `From>`"; - return Err(syn::Error::new(type_.span(), msg)) - }; + return Err(syn::Error::new(type_.span(), msg)); + }; - if from_event_bound.is_generic && (from_event_bound.has_instance != trait_has_instance) - { - let msg = "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` bounds inconsistent \ + if from_event_bound.is_generic && (from_event_bound.has_instance != trait_has_instance) + { + let msg = "Invalid `type RuntimeEvent`, associated type `RuntimeEvent` bounds inconsistent \ `From`. Config and generic Event must be both with instance or \ without instance"; - return Err(syn::Error::new(type_.span(), msg)) - } - - Ok(true) - } else { - Ok(false) - } - } else { - Ok(false) - } + return Err(syn::Error::new(type_.span(), msg)); + } + + Ok(true) + } else { + Ok(false) + } + } else { + Ok(false) + } } /// Check that the path to `frame_system::Config` is valid, this is that the path is just /// `frame_system::Config` or when using the `frame` crate it is `frame::xyz::frame_system::Config`. fn has_expected_system_config(path: syn::Path, frame_system: &syn::Path) -> bool { - // Check if `frame_system` is actually 'frame_system'. - if path.segments.iter().all(|s| s.ident != "frame_system") { - return false - } - - let mut expected_system_config = - match (is_using_frame_crate(&path), is_using_frame_crate(&frame_system)) { - (true, false) => - // We can't use the path to `frame_system` from `frame` if `frame_system` is not being - // in scope through `frame`. - return false, - (false, true) => - // We know that the only valid frame_system path is one that is `frame_system`, as - // `frame` re-exports it as such. - syn::parse2::(quote::quote!(frame_system)).expect("is a valid path; qed"), - (_, _) => - // They are either both `frame_system` or both `frame::xyz::frame_system`. - frame_system.clone(), - }; - - expected_system_config - .segments - .push(syn::PathSegment::from(syn::Ident::new("Config", path.span()))); - - // the parse path might be something like `frame_system::Config<...>`, so we - // only compare the idents along the path. - expected_system_config - .segments - .into_iter() - .map(|ps| ps.ident) - .collect::>() == - path.segments.into_iter().map(|ps| ps.ident).collect::>() + // Check if `frame_system` is actually 'frame_system'. + if path.segments.iter().all(|s| s.ident != "frame_system") { + return false; + } + + let mut expected_system_config = match ( + is_using_frame_crate(&path), + is_using_frame_crate(&frame_system), + ) { + (true, false) => + // We can't use the path to `frame_system` from `frame` if `frame_system` is not being + // in scope through `frame`. + { + return false + } + (false, true) => + // We know that the only valid frame_system path is one that is `frame_system`, as + // `frame` re-exports it as such. + { + syn::parse2::(quote::quote!(frame_system)).expect("is a valid path; qed") + } + (_, _) => + // They are either both `frame_system` or both `frame::xyz::frame_system`. + { + frame_system.clone() + } + }; + + expected_system_config + .segments + .push(syn::PathSegment::from(syn::Ident::new( + "Config", + path.span(), + ))); + + // the parse path might be something like `frame_system::Config<...>`, so we + // only compare the idents along the path. + expected_system_config + .segments + .into_iter() + .map(|ps| ps.ident) + .collect::>() + == path + .segments + .into_iter() + .map(|ps| ps.ident) + .collect::>() } /// Replace ident `Self` by `T` pub fn replace_self_by_t(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream { - input - .into_iter() - .map(|token_tree| match token_tree { - proc_macro2::TokenTree::Group(group) => - proc_macro2::Group::new(group.delimiter(), replace_self_by_t(group.stream())).into(), - proc_macro2::TokenTree::Ident(ident) if ident == "Self" => - proc_macro2::Ident::new("T", ident.span()).into(), - other => other, - }) - .collect() + input + .into_iter() + .map(|token_tree| match token_tree { + proc_macro2::TokenTree::Group(group) => { + proc_macro2::Group::new(group.delimiter(), replace_self_by_t(group.stream())).into() + } + proc_macro2::TokenTree::Ident(ident) if ident == "Self" => { + proc_macro2::Ident::new("T", ident.span()).into() + } + other => other, + }) + .collect() } impl ConfigDef { - pub fn try_from( - frame_system: &syn::Path, - attr_span: proc_macro2::Span, - index: usize, - item: &mut syn::Item, - enable_default: bool, - ) -> syn::Result { - let item = if let syn::Item::Trait(item) = item { - item - } else { - let msg = "Invalid pallet::config, expected trait definition"; - return Err(syn::Error::new(item.span(), msg)) - }; - - if !matches!(item.vis, syn::Visibility::Public(_)) { - let msg = "Invalid pallet::config, trait must be public"; - return Err(syn::Error::new(item.span(), msg)) - } - - syn::parse2::(item.ident.to_token_stream())?; - - let where_clause = { - let stream = replace_self_by_t(item.generics.where_clause.to_token_stream()); - syn::parse2::>(stream).expect( - "Internal error: replacing `Self` by `T` should result in valid where + pub fn try_from( + frame_system: &syn::Path, + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + enable_default: bool, + ) -> syn::Result { + let item = if let syn::Item::Trait(item) = item { + item + } else { + let msg = "Invalid pallet::config, expected trait definition"; + return Err(syn::Error::new(item.span(), msg)); + }; + + if !matches!(item.vis, syn::Visibility::Public(_)) { + let msg = "Invalid pallet::config, trait must be public"; + return Err(syn::Error::new(item.span(), msg)); + } + + syn::parse2::(item.ident.to_token_stream())?; + + let where_clause = { + let stream = replace_self_by_t(item.generics.where_clause.to_token_stream()); + syn::parse2::>(stream).expect( + "Internal error: replacing `Self` by `T` should result in valid where clause", - ) - }; - - if item.generics.params.len() > 1 { - let msg = "Invalid pallet::config, expected no more than one generic"; - return Err(syn::Error::new(item.generics.params[2].span(), msg)) - } - - let has_instance = if item.generics.params.first().is_some() { - helper::check_config_def_gen(&item.generics, item.ident.span())?; - true - } else { - false - }; - - let has_frame_system_supertrait = item.supertraits.iter().any(|s| { - syn::parse2::(s.to_token_stream()) - .map_or(false, |b| has_expected_system_config(b, frame_system)) - }); - - let mut has_event_type = false; - let mut consts_metadata = vec![]; - let mut default_sub_trait = if enable_default { - Some(DefaultTrait { - items: Default::default(), - has_system: has_frame_system_supertrait, - }) - } else { - None - }; - for trait_item in &mut item.items { - let is_event = check_event_type(frame_system, trait_item, has_instance)?; - has_event_type = has_event_type || is_event; - - let mut already_no_default = false; - let mut already_constant = false; - let mut already_no_default_bounds = false; - - while let Ok(Some(pallet_attr)) = - helper::take_first_item_pallet_attr::(trait_item) - { - match (pallet_attr.typ, &trait_item) { - (PalletAttrType::Constant(_), syn::TraitItem::Type(ref typ)) => { - if already_constant { - return Err(syn::Error::new( - pallet_attr._bracket.span.join(), - "Duplicate #[pallet::constant] attribute not allowed.", - )) - } - already_constant = true; - consts_metadata.push(ConstMetadataDef::try_from(typ)?); - }, - (PalletAttrType::Constant(_), _) => - return Err(syn::Error::new( - trait_item.span(), - "Invalid #[pallet::constant] in #[pallet::config], expected type item", - )), - (PalletAttrType::NoDefault(_), _) => { - if !enable_default { - return Err(syn::Error::new( + ) + }; + + if item.generics.params.len() > 1 { + let msg = "Invalid pallet::config, expected no more than one generic"; + return Err(syn::Error::new(item.generics.params[2].span(), msg)); + } + + let has_instance = if item.generics.params.first().is_some() { + helper::check_config_def_gen(&item.generics, item.ident.span())?; + true + } else { + false + }; + + let has_frame_system_supertrait = item.supertraits.iter().any(|s| { + syn::parse2::(s.to_token_stream()) + .map_or(false, |b| has_expected_system_config(b, frame_system)) + }); + + let mut has_event_type = false; + let mut consts_metadata = vec![]; + let mut default_sub_trait = if enable_default { + Some(DefaultTrait { + items: Default::default(), + has_system: has_frame_system_supertrait, + }) + } else { + None + }; + for trait_item in &mut item.items { + let is_event = check_event_type(frame_system, trait_item, has_instance)?; + has_event_type = has_event_type || is_event; + + let mut already_no_default = false; + let mut already_constant = false; + let mut already_no_default_bounds = false; + + while let Ok(Some(pallet_attr)) = + helper::take_first_item_pallet_attr::(trait_item) + { + match (pallet_attr.typ, &trait_item) { + (PalletAttrType::Constant(_), syn::TraitItem::Type(ref typ)) => { + if already_constant { + return Err(syn::Error::new( + pallet_attr._bracket.span.join(), + "Duplicate #[pallet::constant] attribute not allowed.", + )); + } + already_constant = true; + consts_metadata.push(ConstMetadataDef::try_from(typ)?); + } + (PalletAttrType::Constant(_), _) => { + return Err(syn::Error::new( + trait_item.span(), + "Invalid #[pallet::constant] in #[pallet::config], expected type item", + )) + } + (PalletAttrType::NoDefault(_), _) => { + if !enable_default { + return Err(syn::Error::new( pallet_attr._bracket.span.join(), "`#[pallet:no_default]` can only be used if `#[pallet::config(with_default)]` \ has been specified" - )) - } - if already_no_default { - return Err(syn::Error::new( - pallet_attr._bracket.span.join(), - "Duplicate #[pallet::no_default] attribute not allowed.", - )) - } - - already_no_default = true; - }, - (PalletAttrType::NoBounds(_), _) => { - if !enable_default { - return Err(syn::Error::new( + )); + } + if already_no_default { + return Err(syn::Error::new( + pallet_attr._bracket.span.join(), + "Duplicate #[pallet::no_default] attribute not allowed.", + )); + } + + already_no_default = true; + } + (PalletAttrType::NoBounds(_), _) => { + if !enable_default { + return Err(syn::Error::new( pallet_attr._bracket.span.join(), "`#[pallet:no_default_bounds]` can only be used if `#[pallet::config(with_default)]` \ has been specified" - )) - } - if already_no_default_bounds { - return Err(syn::Error::new( - pallet_attr._bracket.span.join(), - "Duplicate #[pallet::no_default_bounds] attribute not allowed.", - )) - } - already_no_default_bounds = true; - }, - } - } - - if !already_no_default && enable_default { - default_sub_trait - .as_mut() - .expect("is 'Some(_)' if 'enable_default'; qed") - .items - .push((trait_item.clone(), already_no_default_bounds)); - } - } - - let attr: Option = - helper::take_first_item_pallet_attr(&mut item.attrs)?; - let disable_system_supertrait_check = attr.is_some(); - - if !has_frame_system_supertrait && !disable_system_supertrait_check { - let found = if item.supertraits.is_empty() { - "none".to_string() - } else { - let mut found = item - .supertraits - .iter() - .fold(String::new(), |acc, s| format!("{}`{}`, ", acc, quote::quote!(#s))); - found.pop(); - found.pop(); - found - }; - - let msg = format!( - "Invalid pallet::trait, expected explicit `{}::Config` as supertrait, \ + )); + } + if already_no_default_bounds { + return Err(syn::Error::new( + pallet_attr._bracket.span.join(), + "Duplicate #[pallet::no_default_bounds] attribute not allowed.", + )); + } + already_no_default_bounds = true; + } + } + } + + if !already_no_default && enable_default { + default_sub_trait + .as_mut() + .expect("is 'Some(_)' if 'enable_default'; qed") + .items + .push((trait_item.clone(), already_no_default_bounds)); + } + } + + let attr: Option = + helper::take_first_item_pallet_attr(&mut item.attrs)?; + let disable_system_supertrait_check = attr.is_some(); + + if !has_frame_system_supertrait && !disable_system_supertrait_check { + let found = if item.supertraits.is_empty() { + "none".to_string() + } else { + let mut found = item.supertraits.iter().fold(String::new(), |acc, s| { + format!("{}`{}`, ", acc, quote::quote!(#s)) + }); + found.pop(); + found.pop(); + found + }; + + let msg = format!( + "Invalid pallet::trait, expected explicit `{}::Config` as supertrait, \ found {}. \ (try `pub trait Config: frame_system::Config {{ ...` or \ `pub trait Config: frame_system::Config {{ ...`). \ To disable this check, use `#[pallet::disable_frame_system_supertrait_check]`", - frame_system.to_token_stream(), - found, - ); - return Err(syn::Error::new(item.span(), msg)) - } - - Ok(Self { - index, - has_instance, - consts_metadata, - has_event_type, - where_clause, - attr_span, - default_sub_trait, - }) - } + frame_system.to_token_stream(), + found, + ); + return Err(syn::Error::new(item.span(), msg)); + } + + Ok(Self { + index, + has_instance, + consts_metadata, + has_event_type, + where_clause, + attr_span, + default_sub_trait, + }) + } } #[cfg(test)] mod tests { - use super::*; - #[test] - fn has_expected_system_config_works() { - let frame_system = syn::parse2::(quote::quote!(frame_system)).unwrap(); - let path = syn::parse2::(quote::quote!(frame_system::Config)).unwrap(); - assert!(has_expected_system_config(path, &frame_system)); - } - - #[test] - fn has_expected_system_config_works_with_assoc_type() { - let frame_system = syn::parse2::(quote::quote!(frame_system)).unwrap(); - let path = - syn::parse2::(quote::quote!(frame_system::Config)) - .unwrap(); - assert!(has_expected_system_config(path, &frame_system)); - } - - #[test] - fn has_expected_system_config_works_with_frame() { - let frame_system = - syn::parse2::(quote::quote!(frame::deps::frame_system)).unwrap(); - let path = syn::parse2::(quote::quote!(frame_system::Config)).unwrap(); - assert!(has_expected_system_config(path, &frame_system)); - } - - #[test] - fn has_expected_system_config_works_with_frame_full_path() { - let frame_system = - syn::parse2::(quote::quote!(frame::deps::frame_system)).unwrap(); - let path = - syn::parse2::(quote::quote!(frame::deps::frame_system::Config)).unwrap(); - assert!(has_expected_system_config(path, &frame_system)); - } - - #[test] - fn has_expected_system_config_works_with_other_frame_full_path() { - let frame_system = - syn::parse2::(quote::quote!(frame::xyz::frame_system)).unwrap(); - let path = - syn::parse2::(quote::quote!(frame::xyz::frame_system::Config)).unwrap(); - assert!(has_expected_system_config(path, &frame_system)); - } - - #[test] - fn has_expected_system_config_does_not_works_with_mixed_frame_full_path() { - let frame_system = - syn::parse2::(quote::quote!(frame::xyz::frame_system)).unwrap(); - let path = - syn::parse2::(quote::quote!(frame::deps::frame_system::Config)).unwrap(); - assert!(!has_expected_system_config(path, &frame_system)); - } - - #[test] - fn has_expected_system_config_does_not_works_with_other_mixed_frame_full_path() { - let frame_system = - syn::parse2::(quote::quote!(frame::deps::frame_system)).unwrap(); - let path = - syn::parse2::(quote::quote!(frame::xyz::frame_system::Config)).unwrap(); - assert!(!has_expected_system_config(path, &frame_system)); - } - - #[test] - fn has_expected_system_config_does_not_work_with_frame_full_path_if_not_frame_crate() { - let frame_system = syn::parse2::(quote::quote!(frame_system)).unwrap(); - let path = - syn::parse2::(quote::quote!(frame::deps::frame_system::Config)).unwrap(); - assert!(!has_expected_system_config(path, &frame_system)); - } - - #[test] - fn has_expected_system_config_unexpected_frame_system() { - let frame_system = - syn::parse2::(quote::quote!(framez::deps::frame_system)).unwrap(); - let path = syn::parse2::(quote::quote!(frame_system::Config)).unwrap(); - assert!(!has_expected_system_config(path, &frame_system)); - } - - #[test] - fn has_expected_system_config_unexpected_path() { - let frame_system = syn::parse2::(quote::quote!(frame_system)).unwrap(); - let path = syn::parse2::(quote::quote!(frame_system::ConfigSystem)).unwrap(); - assert!(!has_expected_system_config(path, &frame_system)); - } - - #[test] - fn has_expected_system_config_not_frame_system() { - let frame_system = syn::parse2::(quote::quote!(something)).unwrap(); - let path = syn::parse2::(quote::quote!(something::Config)).unwrap(); - assert!(!has_expected_system_config(path, &frame_system)); - } + use super::*; + #[test] + fn has_expected_system_config_works() { + let frame_system = syn::parse2::(quote::quote!(frame_system)).unwrap(); + let path = syn::parse2::(quote::quote!(frame_system::Config)).unwrap(); + assert!(has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_works_with_assoc_type() { + let frame_system = syn::parse2::(quote::quote!(frame_system)).unwrap(); + let path = + syn::parse2::(quote::quote!(frame_system::Config)) + .unwrap(); + assert!(has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_works_with_frame() { + let frame_system = + syn::parse2::(quote::quote!(frame::deps::frame_system)).unwrap(); + let path = syn::parse2::(quote::quote!(frame_system::Config)).unwrap(); + assert!(has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_works_with_frame_full_path() { + let frame_system = + syn::parse2::(quote::quote!(frame::deps::frame_system)).unwrap(); + let path = + syn::parse2::(quote::quote!(frame::deps::frame_system::Config)).unwrap(); + assert!(has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_works_with_other_frame_full_path() { + let frame_system = + syn::parse2::(quote::quote!(frame::xyz::frame_system)).unwrap(); + let path = + syn::parse2::(quote::quote!(frame::xyz::frame_system::Config)).unwrap(); + assert!(has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_does_not_works_with_mixed_frame_full_path() { + let frame_system = + syn::parse2::(quote::quote!(frame::xyz::frame_system)).unwrap(); + let path = + syn::parse2::(quote::quote!(frame::deps::frame_system::Config)).unwrap(); + assert!(!has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_does_not_works_with_other_mixed_frame_full_path() { + let frame_system = + syn::parse2::(quote::quote!(frame::deps::frame_system)).unwrap(); + let path = + syn::parse2::(quote::quote!(frame::xyz::frame_system::Config)).unwrap(); + assert!(!has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_does_not_work_with_frame_full_path_if_not_frame_crate() { + let frame_system = syn::parse2::(quote::quote!(frame_system)).unwrap(); + let path = + syn::parse2::(quote::quote!(frame::deps::frame_system::Config)).unwrap(); + assert!(!has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_unexpected_frame_system() { + let frame_system = + syn::parse2::(quote::quote!(framez::deps::frame_system)).unwrap(); + let path = syn::parse2::(quote::quote!(frame_system::Config)).unwrap(); + assert!(!has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_unexpected_path() { + let frame_system = syn::parse2::(quote::quote!(frame_system)).unwrap(); + let path = syn::parse2::(quote::quote!(frame_system::ConfigSystem)).unwrap(); + assert!(!has_expected_system_config(path, &frame_system)); + } + + #[test] + fn has_expected_system_config_not_frame_system() { + let frame_system = syn::parse2::(quote::quote!(something)).unwrap(); + let path = syn::parse2::(quote::quote!(something::Config)).unwrap(); + assert!(!has_expected_system_config(path, &frame_system)); + } } diff --git a/support/procedural-fork/src/pallet/parse/error.rs b/support/procedural-fork/src/pallet/parse/error.rs index 362df8d73..e93e2113f 100644 --- a/support/procedural-fork/src/pallet/parse/error.rs +++ b/support/procedural-fork/src/pallet/parse/error.rs @@ -22,94 +22,108 @@ use syn::{spanned::Spanned, Fields}; /// List of additional token to be used for parsing. mod keyword { - syn::custom_keyword!(Error); + syn::custom_keyword!(Error); } /// Records information about the error enum variant field. pub struct VariantField { - /// Whether or not the field is named, i.e. whether it is a tuple variant or struct variant. - pub is_named: bool, + /// Whether or not the field is named, i.e. whether it is a tuple variant or struct variant. + pub is_named: bool, } /// Records information about the error enum variants. pub struct VariantDef { - /// The variant ident. - pub ident: syn::Ident, - /// The variant field, if any. - pub field: Option, - /// The variant doc literals. - pub docs: Vec, - /// The `cfg` attributes. - pub cfg_attrs: Vec, + /// The variant ident. + pub ident: syn::Ident, + /// The variant field, if any. + pub field: Option, + /// The variant doc literals. + pub docs: Vec, + /// The `cfg` attributes. + pub cfg_attrs: Vec, } /// This checks error declaration as a enum declaration with only variants without fields nor /// discriminant. pub struct ErrorDef { - /// The index of error item in pallet module. - pub index: usize, - /// Variant definitions. - pub variants: Vec, - /// A set of usage of instance, must be check for consistency with trait. - pub instances: Vec, - /// The keyword error used (contains span). - pub error: keyword::Error, - /// The span of the pallet::error attribute. - pub attr_span: proc_macro2::Span, + /// The index of error item in pallet module. + pub index: usize, + /// Variant definitions. + pub variants: Vec, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Vec, + /// The keyword error used (contains span). + pub error: keyword::Error, + /// The span of the pallet::error attribute. + pub attr_span: proc_macro2::Span, } impl ErrorDef { - pub fn try_from( - attr_span: proc_macro2::Span, - index: usize, - item: &mut syn::Item, - ) -> syn::Result { - let item = if let syn::Item::Enum(item) = item { - item - } else { - return Err(syn::Error::new(item.span(), "Invalid pallet::error, expected item enum")) - }; - if !matches!(item.vis, syn::Visibility::Public(_)) { - let msg = "Invalid pallet::error, `Error` must be public"; - return Err(syn::Error::new(item.span(), msg)) - } + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + ) -> syn::Result { + let item = if let syn::Item::Enum(item) = item { + item + } else { + return Err(syn::Error::new( + item.span(), + "Invalid pallet::error, expected item enum", + )); + }; + if !matches!(item.vis, syn::Visibility::Public(_)) { + let msg = "Invalid pallet::error, `Error` must be public"; + return Err(syn::Error::new(item.span(), msg)); + } - let instances = - vec![helper::check_type_def_gen_no_bounds(&item.generics, item.ident.span())?]; + let instances = vec![helper::check_type_def_gen_no_bounds( + &item.generics, + item.ident.span(), + )?]; - if item.generics.where_clause.is_some() { - let msg = "Invalid pallet::error, where clause is not allowed on pallet error item"; - return Err(syn::Error::new(item.generics.where_clause.as_ref().unwrap().span(), msg)) - } + if item.generics.where_clause.is_some() { + let msg = "Invalid pallet::error, where clause is not allowed on pallet error item"; + return Err(syn::Error::new( + item.generics.where_clause.as_ref().unwrap().span(), + msg, + )); + } - let error = syn::parse2::(item.ident.to_token_stream())?; + let error = syn::parse2::(item.ident.to_token_stream())?; - let variants = item - .variants - .iter() - .map(|variant| { - let field_ty = match &variant.fields { - Fields::Unit => None, - Fields::Named(_) => Some(VariantField { is_named: true }), - Fields::Unnamed(_) => Some(VariantField { is_named: false }), - }; - if variant.discriminant.is_some() { - let msg = "Invalid pallet::error, unexpected discriminant, discriminants \ + let variants = item + .variants + .iter() + .map(|variant| { + let field_ty = match &variant.fields { + Fields::Unit => None, + Fields::Named(_) => Some(VariantField { is_named: true }), + Fields::Unnamed(_) => Some(VariantField { is_named: false }), + }; + if variant.discriminant.is_some() { + let msg = "Invalid pallet::error, unexpected discriminant, discriminants \ are not supported"; - let span = variant.discriminant.as_ref().unwrap().0.span(); - return Err(syn::Error::new(span, msg)) - } - let cfg_attrs: Vec = helper::get_item_cfg_attrs(&variant.attrs); + let span = variant.discriminant.as_ref().unwrap().0.span(); + return Err(syn::Error::new(span, msg)); + } + let cfg_attrs: Vec = helper::get_item_cfg_attrs(&variant.attrs); - Ok(VariantDef { - ident: variant.ident.clone(), - field: field_ty, - docs: get_doc_literals(&variant.attrs), - cfg_attrs, - }) - }) - .collect::>()?; + Ok(VariantDef { + ident: variant.ident.clone(), + field: field_ty, + docs: get_doc_literals(&variant.attrs), + cfg_attrs, + }) + }) + .collect::>()?; - Ok(ErrorDef { attr_span, index, variants, instances, error }) - } + Ok(ErrorDef { + attr_span, + index, + variants, + instances, + error, + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/event.rs b/support/procedural-fork/src/pallet/parse/event.rs index 0fb8ee4f5..6102dd31f 100644 --- a/support/procedural-fork/src/pallet/parse/event.rs +++ b/support/procedural-fork/src/pallet/parse/event.rs @@ -21,28 +21,28 @@ use syn::spanned::Spanned; /// List of additional token to be used for parsing. mod keyword { - syn::custom_keyword!(Event); - syn::custom_keyword!(pallet); - syn::custom_keyword!(generate_deposit); - syn::custom_keyword!(deposit_event); + syn::custom_keyword!(Event); + syn::custom_keyword!(pallet); + syn::custom_keyword!(generate_deposit); + syn::custom_keyword!(deposit_event); } /// Definition for pallet event enum. pub struct EventDef { - /// The index of event item in pallet module. - pub index: usize, - /// The keyword Event used (contains span). - pub event: keyword::Event, - /// A set of usage of instance, must be check for consistency with trait. - pub instances: Vec, - /// The kind of generic the type `Event` has. - pub gen_kind: super::GenericKind, - /// Whether the function `deposit_event` must be generated. - pub deposit_event: Option, - /// Where clause used in event definition. - pub where_clause: Option, - /// The span of the pallet::event attribute. - pub attr_span: proc_macro2::Span, + /// The index of event item in pallet module. + pub index: usize, + /// The keyword Event used (contains span). + pub event: keyword::Event, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Vec, + /// The kind of generic the type `Event` has. + pub gen_kind: super::GenericKind, + /// Whether the function `deposit_event` must be generated. + pub deposit_event: Option, + /// Where clause used in event definition. + pub where_clause: Option, + /// The span of the pallet::event attribute. + pub attr_span: proc_macro2::Span, } /// Attribute for a pallet's Event. @@ -50,92 +50,110 @@ pub struct EventDef { /// Syntax is: /// * `#[pallet::generate_deposit($vis fn deposit_event)]` pub struct PalletEventDepositAttr { - pub fn_vis: syn::Visibility, - // Span for the keyword deposit_event - pub fn_span: proc_macro2::Span, - // Span of the attribute - pub span: proc_macro2::Span, + pub fn_vis: syn::Visibility, + // Span for the keyword deposit_event + pub fn_span: proc_macro2::Span, + // Span of the attribute + pub span: proc_macro2::Span, } impl syn::parse::Parse for PalletEventDepositAttr { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - let content; - syn::bracketed!(content in input); - content.parse::()?; - content.parse::()?; - - let span = content.parse::()?.span(); - let generate_content; - syn::parenthesized!(generate_content in content); - let fn_vis = generate_content.parse::()?; - generate_content.parse::()?; - let fn_span = generate_content.parse::()?.span(); - - Ok(PalletEventDepositAttr { fn_vis, span, fn_span }) - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + let span = content.parse::()?.span(); + let generate_content; + syn::parenthesized!(generate_content in content); + let fn_vis = generate_content.parse::()?; + generate_content.parse::()?; + let fn_span = generate_content.parse::()?.span(); + + Ok(PalletEventDepositAttr { + fn_vis, + span, + fn_span, + }) + } } struct PalletEventAttrInfo { - deposit_event: Option, + deposit_event: Option, } impl PalletEventAttrInfo { - fn from_attrs(attrs: Vec) -> syn::Result { - let mut deposit_event = None; - for attr in attrs { - if deposit_event.is_none() { - deposit_event = Some(attr) - } else { - return Err(syn::Error::new(attr.span, "Duplicate attribute")) - } - } - - Ok(PalletEventAttrInfo { deposit_event }) - } + fn from_attrs(attrs: Vec) -> syn::Result { + let mut deposit_event = None; + for attr in attrs { + if deposit_event.is_none() { + deposit_event = Some(attr) + } else { + return Err(syn::Error::new(attr.span, "Duplicate attribute")); + } + } + + Ok(PalletEventAttrInfo { deposit_event }) + } } impl EventDef { - pub fn try_from( - attr_span: proc_macro2::Span, - index: usize, - item: &mut syn::Item, - ) -> syn::Result { - let item = if let syn::Item::Enum(item) = item { - item - } else { - return Err(syn::Error::new(item.span(), "Invalid pallet::event, expected enum item")) - }; - - let event_attrs: Vec = - helper::take_item_pallet_attrs(&mut item.attrs)?; - let attr_info = PalletEventAttrInfo::from_attrs(event_attrs)?; - let deposit_event = attr_info.deposit_event; - - if !matches!(item.vis, syn::Visibility::Public(_)) { - let msg = "Invalid pallet::event, `Event` must be public"; - return Err(syn::Error::new(item.span(), msg)) - } - - let where_clause = item.generics.where_clause.clone(); - - let mut instances = vec![]; - // NOTE: Event is not allowed to be only generic on I because it is not supported - // by construct_runtime. - if let Some(u) = helper::check_type_def_optional_gen(&item.generics, item.ident.span())? { - instances.push(u); - } else { - // construct_runtime only allow non generic event for non instantiable pallet. - instances.push(helper::InstanceUsage { has_instance: false, span: item.ident.span() }) - } - - let has_instance = item.generics.type_params().any(|t| t.ident == "I"); - let has_config = item.generics.type_params().any(|t| t.ident == "T"); - let gen_kind = super::GenericKind::from_gens(has_config, has_instance) - .expect("Checked by `helper::check_type_def_optional_gen` above"); - - let event = syn::parse2::(item.ident.to_token_stream())?; - - Ok(EventDef { attr_span, index, instances, deposit_event, event, gen_kind, where_clause }) - } + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + ) -> syn::Result { + let item = if let syn::Item::Enum(item) = item { + item + } else { + return Err(syn::Error::new( + item.span(), + "Invalid pallet::event, expected enum item", + )); + }; + + let event_attrs: Vec = + helper::take_item_pallet_attrs(&mut item.attrs)?; + let attr_info = PalletEventAttrInfo::from_attrs(event_attrs)?; + let deposit_event = attr_info.deposit_event; + + if !matches!(item.vis, syn::Visibility::Public(_)) { + let msg = "Invalid pallet::event, `Event` must be public"; + return Err(syn::Error::new(item.span(), msg)); + } + + let where_clause = item.generics.where_clause.clone(); + + let mut instances = vec![]; + // NOTE: Event is not allowed to be only generic on I because it is not supported + // by construct_runtime. + if let Some(u) = helper::check_type_def_optional_gen(&item.generics, item.ident.span())? { + instances.push(u); + } else { + // construct_runtime only allow non generic event for non instantiable pallet. + instances.push(helper::InstanceUsage { + has_instance: false, + span: item.ident.span(), + }) + } + + let has_instance = item.generics.type_params().any(|t| t.ident == "I"); + let has_config = item.generics.type_params().any(|t| t.ident == "T"); + let gen_kind = super::GenericKind::from_gens(has_config, has_instance) + .expect("Checked by `helper::check_type_def_optional_gen` above"); + + let event = syn::parse2::(item.ident.to_token_stream())?; + + Ok(EventDef { + attr_span, + index, + instances, + deposit_event, + event, + gen_kind, + where_clause, + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/extra_constants.rs b/support/procedural-fork/src/pallet/parse/extra_constants.rs index 2ba6c44b7..38acea21a 100644 --- a/support/procedural-fork/src/pallet/parse/extra_constants.rs +++ b/support/procedural-fork/src/pallet/parse/extra_constants.rs @@ -21,140 +21,148 @@ use syn::spanned::Spanned; /// List of additional token to be used for parsing. mod keyword { - syn::custom_keyword!(DispatchResultWithPostInfo); - syn::custom_keyword!(Call); - syn::custom_keyword!(OriginFor); - syn::custom_keyword!(weight); - syn::custom_keyword!(compact); - syn::custom_keyword!(T); - syn::custom_keyword!(pallet); - syn::custom_keyword!(constant_name); + syn::custom_keyword!(DispatchResultWithPostInfo); + syn::custom_keyword!(Call); + syn::custom_keyword!(OriginFor); + syn::custom_keyword!(weight); + syn::custom_keyword!(compact); + syn::custom_keyword!(T); + syn::custom_keyword!(pallet); + syn::custom_keyword!(constant_name); } /// Definition of extra constants typically `impl Pallet { ... }` pub struct ExtraConstantsDef { - /// The where_clause used. - pub where_clause: Option, - /// A set of usage of instance, must be check for consistency with trait. - pub instances: Vec, - /// The index of call item in pallet module. - pub index: usize, - /// The extra constant defined. - pub extra_constants: Vec, + /// The where_clause used. + pub where_clause: Option, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Vec, + /// The index of call item in pallet module. + pub index: usize, + /// The extra constant defined. + pub extra_constants: Vec, } /// Input definition for an constant in pallet. pub struct ExtraConstantDef { - /// Name of the function - pub ident: syn::Ident, - /// The type returned by the function - pub type_: syn::Type, - /// The doc associated - pub doc: Vec, - /// Optional MetaData Name - pub metadata_name: Option, + /// Name of the function + pub ident: syn::Ident, + /// The type returned by the function + pub type_: syn::Type, + /// The doc associated + pub doc: Vec, + /// Optional MetaData Name + pub metadata_name: Option, } /// Attributes for functions in extra_constants impl block. /// Parse for `#[pallet::constant_name(ConstantName)]` pub struct ExtraConstAttr { - metadata_name: syn::Ident, + metadata_name: syn::Ident, } impl syn::parse::Parse for ExtraConstAttr { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - let content; - syn::bracketed!(content in input); - content.parse::()?; - content.parse::()?; - content.parse::()?; - - let metadata_name; - syn::parenthesized!(metadata_name in content); - Ok(ExtraConstAttr { metadata_name: metadata_name.parse::()? }) - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + content.parse::()?; + + let metadata_name; + syn::parenthesized!(metadata_name in content); + Ok(ExtraConstAttr { + metadata_name: metadata_name.parse::()?, + }) + } } impl ExtraConstantsDef { - pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { - let item = if let syn::Item::Impl(item) = item { - item - } else { - return Err(syn::Error::new( - item.span(), - "Invalid pallet::extra_constants, expected item impl", - )) - }; - - let instances = vec![ - helper::check_impl_gen(&item.generics, item.impl_token.span())?, - helper::check_pallet_struct_usage(&item.self_ty)?, - ]; - - if let Some((_, _, for_)) = item.trait_ { - let msg = "Invalid pallet::call, expected no trait ident as in \ + pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { + let item = if let syn::Item::Impl(item) = item { + item + } else { + return Err(syn::Error::new( + item.span(), + "Invalid pallet::extra_constants, expected item impl", + )); + }; + + let instances = vec![ + helper::check_impl_gen(&item.generics, item.impl_token.span())?, + helper::check_pallet_struct_usage(&item.self_ty)?, + ]; + + if let Some((_, _, for_)) = item.trait_ { + let msg = "Invalid pallet::call, expected no trait ident as in \ `impl<..> Pallet<..> { .. }`"; - return Err(syn::Error::new(for_.span(), msg)) - } - - let mut extra_constants = vec![]; - for impl_item in &mut item.items { - let method = if let syn::ImplItem::Fn(method) = impl_item { - method - } else { - let msg = "Invalid pallet::call, only method accepted"; - return Err(syn::Error::new(impl_item.span(), msg)) - }; - - if !method.sig.inputs.is_empty() { - let msg = "Invalid pallet::extra_constants, method must have 0 args"; - return Err(syn::Error::new(method.sig.span(), msg)) - } - - if !method.sig.generics.params.is_empty() { - let msg = "Invalid pallet::extra_constants, method must have 0 generics"; - return Err(syn::Error::new(method.sig.generics.params[0].span(), msg)) - } - - if method.sig.generics.where_clause.is_some() { - let msg = "Invalid pallet::extra_constants, method must have no where clause"; - return Err(syn::Error::new(method.sig.generics.where_clause.span(), msg)) - } - - let type_ = match &method.sig.output { - syn::ReturnType::Default => { - let msg = "Invalid pallet::extra_constants, method must have a return type"; - return Err(syn::Error::new(method.span(), msg)) - }, - syn::ReturnType::Type(_, type_) => *type_.clone(), - }; - - // parse metadata_name - let mut extra_constant_attrs: Vec = - helper::take_item_pallet_attrs(method)?; - - if extra_constant_attrs.len() > 1 { - let msg = - "Invalid attribute in pallet::constant_name, only one attribute is expected"; - return Err(syn::Error::new(extra_constant_attrs[1].metadata_name.span(), msg)) - } - - let metadata_name = extra_constant_attrs.pop().map(|attr| attr.metadata_name); - - extra_constants.push(ExtraConstantDef { - ident: method.sig.ident.clone(), - type_, - doc: get_doc_literals(&method.attrs), - metadata_name, - }); - } - - Ok(Self { - index, - instances, - where_clause: item.generics.where_clause.clone(), - extra_constants, - }) - } + return Err(syn::Error::new(for_.span(), msg)); + } + + let mut extra_constants = vec![]; + for impl_item in &mut item.items { + let method = if let syn::ImplItem::Fn(method) = impl_item { + method + } else { + let msg = "Invalid pallet::call, only method accepted"; + return Err(syn::Error::new(impl_item.span(), msg)); + }; + + if !method.sig.inputs.is_empty() { + let msg = "Invalid pallet::extra_constants, method must have 0 args"; + return Err(syn::Error::new(method.sig.span(), msg)); + } + + if !method.sig.generics.params.is_empty() { + let msg = "Invalid pallet::extra_constants, method must have 0 generics"; + return Err(syn::Error::new(method.sig.generics.params[0].span(), msg)); + } + + if method.sig.generics.where_clause.is_some() { + let msg = "Invalid pallet::extra_constants, method must have no where clause"; + return Err(syn::Error::new( + method.sig.generics.where_clause.span(), + msg, + )); + } + + let type_ = match &method.sig.output { + syn::ReturnType::Default => { + let msg = "Invalid pallet::extra_constants, method must have a return type"; + return Err(syn::Error::new(method.span(), msg)); + } + syn::ReturnType::Type(_, type_) => *type_.clone(), + }; + + // parse metadata_name + let mut extra_constant_attrs: Vec = + helper::take_item_pallet_attrs(method)?; + + if extra_constant_attrs.len() > 1 { + let msg = + "Invalid attribute in pallet::constant_name, only one attribute is expected"; + return Err(syn::Error::new( + extra_constant_attrs[1].metadata_name.span(), + msg, + )); + } + + let metadata_name = extra_constant_attrs.pop().map(|attr| attr.metadata_name); + + extra_constants.push(ExtraConstantDef { + ident: method.sig.ident.clone(), + type_, + doc: get_doc_literals(&method.attrs), + metadata_name, + }); + } + + Ok(Self { + index, + instances, + where_clause: item.generics.where_clause.clone(), + extra_constants, + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/genesis_build.rs b/support/procedural-fork/src/pallet/parse/genesis_build.rs index d0e1d9ec9..670d4d5ef 100644 --- a/support/procedural-fork/src/pallet/parse/genesis_build.rs +++ b/support/procedural-fork/src/pallet/parse/genesis_build.rs @@ -20,42 +20,47 @@ use syn::spanned::Spanned; /// Definition for pallet genesis build implementation. pub struct GenesisBuildDef { - /// The index of item in pallet module. - pub index: usize, - /// A set of usage of instance, must be check for consistency with trait. - pub instances: Option>, - /// The where_clause used. - pub where_clause: Option, - /// The span of the pallet::genesis_build attribute. - pub attr_span: proc_macro2::Span, + /// The index of item in pallet module. + pub index: usize, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Option>, + /// The where_clause used. + pub where_clause: Option, + /// The span of the pallet::genesis_build attribute. + pub attr_span: proc_macro2::Span, } impl GenesisBuildDef { - pub fn try_from( - attr_span: proc_macro2::Span, - index: usize, - item: &mut syn::Item, - ) -> syn::Result { - let item = if let syn::Item::Impl(item) = item { - item - } else { - let msg = "Invalid pallet::genesis_build, expected item impl"; - return Err(syn::Error::new(item.span(), msg)) - }; - - let item_trait = &item - .trait_ - .as_ref() - .ok_or_else(|| { - let msg = "Invalid pallet::genesis_build, expected impl<..> GenesisBuild<..> \ + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + ) -> syn::Result { + let item = if let syn::Item::Impl(item) = item { + item + } else { + let msg = "Invalid pallet::genesis_build, expected item impl"; + return Err(syn::Error::new(item.span(), msg)); + }; + + let item_trait = &item + .trait_ + .as_ref() + .ok_or_else(|| { + let msg = "Invalid pallet::genesis_build, expected impl<..> GenesisBuild<..> \ for GenesisConfig<..>"; - syn::Error::new(item.span(), msg) - })? - .1; + syn::Error::new(item.span(), msg) + })? + .1; - let instances = - helper::check_genesis_builder_usage(item_trait)?.map(|instances| vec![instances]); + let instances = + helper::check_genesis_builder_usage(item_trait)?.map(|instances| vec![instances]); - Ok(Self { attr_span, index, instances, where_clause: item.generics.where_clause.clone() }) - } + Ok(Self { + attr_span, + index, + instances, + where_clause: item.generics.where_clause.clone(), + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/genesis_config.rs b/support/procedural-fork/src/pallet/parse/genesis_config.rs index 62da6ba13..1c52345eb 100644 --- a/support/procedural-fork/src/pallet/parse/genesis_config.rs +++ b/support/procedural-fork/src/pallet/parse/genesis_config.rs @@ -24,50 +24,55 @@ use syn::spanned::Spanned; /// * `struct GenesisConfig` /// * `enum GenesisConfig` pub struct GenesisConfigDef { - /// The index of item in pallet module. - pub index: usize, - /// The kind of generic the type `GenesisConfig` has. - pub gen_kind: super::GenericKind, - /// A set of usage of instance, must be check for consistency with trait. - pub instances: Vec, - /// The ident of genesis_config, can be used for span. - pub genesis_config: syn::Ident, + /// The index of item in pallet module. + pub index: usize, + /// The kind of generic the type `GenesisConfig` has. + pub gen_kind: super::GenericKind, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Vec, + /// The ident of genesis_config, can be used for span. + pub genesis_config: syn::Ident, } impl GenesisConfigDef { - pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { - let item_span = item.span(); - let (vis, ident, generics) = match &item { - syn::Item::Enum(item) => (&item.vis, &item.ident, &item.generics), - syn::Item::Struct(item) => (&item.vis, &item.ident, &item.generics), - _ => { - let msg = "Invalid pallet::genesis_config, expected enum or struct"; - return Err(syn::Error::new(item.span(), msg)) - }, - }; + pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { + let item_span = item.span(); + let (vis, ident, generics) = match &item { + syn::Item::Enum(item) => (&item.vis, &item.ident, &item.generics), + syn::Item::Struct(item) => (&item.vis, &item.ident, &item.generics), + _ => { + let msg = "Invalid pallet::genesis_config, expected enum or struct"; + return Err(syn::Error::new(item.span(), msg)); + } + }; - let mut instances = vec![]; - // NOTE: GenesisConfig is not allowed to be only generic on I because it is not supported - // by construct_runtime. - if let Some(u) = helper::check_type_def_optional_gen(generics, ident.span())? { - instances.push(u); - } + let mut instances = vec![]; + // NOTE: GenesisConfig is not allowed to be only generic on I because it is not supported + // by construct_runtime. + if let Some(u) = helper::check_type_def_optional_gen(generics, ident.span())? { + instances.push(u); + } - let has_instance = generics.type_params().any(|t| t.ident == "I"); - let has_config = generics.type_params().any(|t| t.ident == "T"); - let gen_kind = super::GenericKind::from_gens(has_config, has_instance) - .expect("Checked by `helper::check_type_def_optional_gen` above"); + let has_instance = generics.type_params().any(|t| t.ident == "I"); + let has_config = generics.type_params().any(|t| t.ident == "T"); + let gen_kind = super::GenericKind::from_gens(has_config, has_instance) + .expect("Checked by `helper::check_type_def_optional_gen` above"); - if !matches!(vis, syn::Visibility::Public(_)) { - let msg = "Invalid pallet::genesis_config, GenesisConfig must be public"; - return Err(syn::Error::new(item_span, msg)) - } + if !matches!(vis, syn::Visibility::Public(_)) { + let msg = "Invalid pallet::genesis_config, GenesisConfig must be public"; + return Err(syn::Error::new(item_span, msg)); + } - if ident != "GenesisConfig" { - let msg = "Invalid pallet::genesis_config, ident must `GenesisConfig`"; - return Err(syn::Error::new(ident.span(), msg)) - } + if ident != "GenesisConfig" { + let msg = "Invalid pallet::genesis_config, ident must `GenesisConfig`"; + return Err(syn::Error::new(ident.span(), msg)); + } - Ok(GenesisConfigDef { index, genesis_config: ident.clone(), instances, gen_kind }) - } + Ok(GenesisConfigDef { + index, + genesis_config: ident.clone(), + instances, + gen_kind, + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/helper.rs b/support/procedural-fork/src/pallet/parse/helper.rs index 3187c9139..f58c8d81c 100644 --- a/support/procedural-fork/src/pallet/parse/helper.rs +++ b/support/procedural-fork/src/pallet/parse/helper.rs @@ -21,164 +21,176 @@ use syn::spanned::Spanned; /// List of additional token to be used for parsing. mod keyword { - syn::custom_keyword!(I); - syn::custom_keyword!(compact); - syn::custom_keyword!(GenesisBuild); - syn::custom_keyword!(BuildGenesisConfig); - syn::custom_keyword!(Config); - syn::custom_keyword!(T); - syn::custom_keyword!(Pallet); - syn::custom_keyword!(origin); - syn::custom_keyword!(DispatchResult); - syn::custom_keyword!(DispatchResultWithPostInfo); + syn::custom_keyword!(I); + syn::custom_keyword!(compact); + syn::custom_keyword!(GenesisBuild); + syn::custom_keyword!(BuildGenesisConfig); + syn::custom_keyword!(Config); + syn::custom_keyword!(T); + syn::custom_keyword!(Pallet); + syn::custom_keyword!(origin); + syn::custom_keyword!(DispatchResult); + syn::custom_keyword!(DispatchResultWithPostInfo); } /// A usage of instance, either the trait `Config` has been used with instance or without instance. /// Used to check for consistency. #[derive(Clone)] pub struct InstanceUsage { - pub has_instance: bool, - pub span: proc_macro2::Span, + pub has_instance: bool, + pub span: proc_macro2::Span, } /// Trait implemented for syn items to get mutable references on their attributes. /// /// NOTE: verbatim variants are not supported. pub trait MutItemAttrs { - fn mut_item_attrs(&mut self) -> Option<&mut Vec>; + fn mut_item_attrs(&mut self) -> Option<&mut Vec>; } /// Take the first pallet attribute (e.g. attribute like `#[pallet..]`) and decode it to `Attr` pub(crate) fn take_first_item_pallet_attr( - item: &mut impl MutItemAttrs, + item: &mut impl MutItemAttrs, ) -> syn::Result> where - Attr: syn::parse::Parse, + Attr: syn::parse::Parse, { - let attrs = if let Some(attrs) = item.mut_item_attrs() { attrs } else { return Ok(None) }; - - if let Some(index) = attrs.iter().position(|attr| { - attr.path().segments.first().map_or(false, |segment| segment.ident == "pallet") - }) { - let pallet_attr = attrs.remove(index); - Ok(Some(syn::parse2(pallet_attr.into_token_stream())?)) - } else { - Ok(None) - } + let attrs = if let Some(attrs) = item.mut_item_attrs() { + attrs + } else { + return Ok(None); + }; + + if let Some(index) = attrs.iter().position(|attr| { + attr.path() + .segments + .first() + .map_or(false, |segment| segment.ident == "pallet") + }) { + let pallet_attr = attrs.remove(index); + Ok(Some(syn::parse2(pallet_attr.into_token_stream())?)) + } else { + Ok(None) + } } /// Take all the pallet attributes (e.g. attribute like `#[pallet..]`) and decode them to `Attr` pub(crate) fn take_item_pallet_attrs(item: &mut impl MutItemAttrs) -> syn::Result> where - Attr: syn::parse::Parse, + Attr: syn::parse::Parse, { - let mut pallet_attrs = Vec::new(); + let mut pallet_attrs = Vec::new(); - while let Some(attr) = take_first_item_pallet_attr(item)? { - pallet_attrs.push(attr) - } + while let Some(attr) = take_first_item_pallet_attr(item)? { + pallet_attrs.push(attr) + } - Ok(pallet_attrs) + Ok(pallet_attrs) } /// Get all the cfg attributes (e.g. attribute like `#[cfg..]`) and decode them to `Attr` pub fn get_item_cfg_attrs(attrs: &[syn::Attribute]) -> Vec { - attrs - .iter() - .filter_map(|attr| { - if attr.path().segments.first().map_or(false, |segment| segment.ident == "cfg") { - Some(attr.clone()) - } else { - None - } - }) - .collect::>() + attrs + .iter() + .filter_map(|attr| { + if attr + .path() + .segments + .first() + .map_or(false, |segment| segment.ident == "cfg") + { + Some(attr.clone()) + } else { + None + } + }) + .collect::>() } impl MutItemAttrs for syn::Item { - fn mut_item_attrs(&mut self) -> Option<&mut Vec> { - match self { - Self::Const(item) => Some(item.attrs.as_mut()), - Self::Enum(item) => Some(item.attrs.as_mut()), - Self::ExternCrate(item) => Some(item.attrs.as_mut()), - Self::Fn(item) => Some(item.attrs.as_mut()), - Self::ForeignMod(item) => Some(item.attrs.as_mut()), - Self::Impl(item) => Some(item.attrs.as_mut()), - Self::Macro(item) => Some(item.attrs.as_mut()), - Self::Mod(item) => Some(item.attrs.as_mut()), - Self::Static(item) => Some(item.attrs.as_mut()), - Self::Struct(item) => Some(item.attrs.as_mut()), - Self::Trait(item) => Some(item.attrs.as_mut()), - Self::TraitAlias(item) => Some(item.attrs.as_mut()), - Self::Type(item) => Some(item.attrs.as_mut()), - Self::Union(item) => Some(item.attrs.as_mut()), - Self::Use(item) => Some(item.attrs.as_mut()), - _ => None, - } - } + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + match self { + Self::Const(item) => Some(item.attrs.as_mut()), + Self::Enum(item) => Some(item.attrs.as_mut()), + Self::ExternCrate(item) => Some(item.attrs.as_mut()), + Self::Fn(item) => Some(item.attrs.as_mut()), + Self::ForeignMod(item) => Some(item.attrs.as_mut()), + Self::Impl(item) => Some(item.attrs.as_mut()), + Self::Macro(item) => Some(item.attrs.as_mut()), + Self::Mod(item) => Some(item.attrs.as_mut()), + Self::Static(item) => Some(item.attrs.as_mut()), + Self::Struct(item) => Some(item.attrs.as_mut()), + Self::Trait(item) => Some(item.attrs.as_mut()), + Self::TraitAlias(item) => Some(item.attrs.as_mut()), + Self::Type(item) => Some(item.attrs.as_mut()), + Self::Union(item) => Some(item.attrs.as_mut()), + Self::Use(item) => Some(item.attrs.as_mut()), + _ => None, + } + } } impl MutItemAttrs for syn::TraitItem { - fn mut_item_attrs(&mut self) -> Option<&mut Vec> { - match self { - Self::Const(item) => Some(item.attrs.as_mut()), - Self::Fn(item) => Some(item.attrs.as_mut()), - Self::Type(item) => Some(item.attrs.as_mut()), - Self::Macro(item) => Some(item.attrs.as_mut()), - _ => None, - } - } + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + match self { + Self::Const(item) => Some(item.attrs.as_mut()), + Self::Fn(item) => Some(item.attrs.as_mut()), + Self::Type(item) => Some(item.attrs.as_mut()), + Self::Macro(item) => Some(item.attrs.as_mut()), + _ => None, + } + } } impl MutItemAttrs for Vec { - fn mut_item_attrs(&mut self) -> Option<&mut Vec> { - Some(self) - } + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + Some(self) + } } impl MutItemAttrs for syn::ItemMod { - fn mut_item_attrs(&mut self) -> Option<&mut Vec> { - Some(&mut self.attrs) - } + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + Some(&mut self.attrs) + } } impl MutItemAttrs for syn::ImplItemFn { - fn mut_item_attrs(&mut self) -> Option<&mut Vec> { - Some(&mut self.attrs) - } + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + Some(&mut self.attrs) + } } impl MutItemAttrs for syn::ItemType { - fn mut_item_attrs(&mut self) -> Option<&mut Vec> { - Some(&mut self.attrs) - } + fn mut_item_attrs(&mut self) -> Option<&mut Vec> { + Some(&mut self.attrs) + } } /// Parse for `()` struct Unit; impl syn::parse::Parse for Unit { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let content; - syn::parenthesized!(content in input); - if !content.is_empty() { - let msg = "unexpected tokens, expected nothing inside parenthesis as `()`"; - return Err(syn::Error::new(content.span(), msg)) - } - Ok(Self) - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let content; + syn::parenthesized!(content in input); + if !content.is_empty() { + let msg = "unexpected tokens, expected nothing inside parenthesis as `()`"; + return Err(syn::Error::new(content.span(), msg)); + } + Ok(Self) + } } /// Parse for `'static` struct StaticLifetime; impl syn::parse::Parse for StaticLifetime { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let lifetime = input.parse::()?; - if lifetime.ident != "static" { - let msg = "unexpected tokens, expected `static`"; - return Err(syn::Error::new(lifetime.ident.span(), msg)) - } - Ok(Self) - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let lifetime = input.parse::()?; + if lifetime.ident != "static" { + let msg = "unexpected tokens, expected `static`"; + return Err(syn::Error::new(lifetime.ident.span(), msg)); + } + Ok(Self) + } } /// Check the syntax: `I: 'static = ()` @@ -187,28 +199,28 @@ impl syn::parse::Parse for StaticLifetime { /// /// return the instance if found. pub fn check_config_def_gen(gen: &syn::Generics, span: proc_macro2::Span) -> syn::Result<()> { - let expected = "expected `I: 'static = ()`"; - pub struct CheckTraitDefGenerics; - impl syn::parse::Parse for CheckTraitDefGenerics { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - - Ok(Self) - } - } - - syn::parse2::(gen.params.to_token_stream()).map_err(|e| { - let msg = format!("Invalid generics: {}", expected); - let mut err = syn::Error::new(span, msg); - err.combine(e); - err - })?; - - Ok(()) + let expected = "expected `I: 'static = ()`"; + pub struct CheckTraitDefGenerics; + impl syn::parse::Parse for CheckTraitDefGenerics { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + + Ok(Self) + } + } + + syn::parse2::(gen.params.to_token_stream()).map_err(|e| { + let msg = format!("Invalid generics: {}", expected); + let mut err = syn::Error::new(span, msg); + err.combine(e); + err + })?; + + Ok(()) } /// Check the syntax: @@ -219,38 +231,41 @@ pub fn check_config_def_gen(gen: &syn::Generics, span: proc_macro2::Span) -> syn /// /// return the instance if found. pub fn check_type_def_gen_no_bounds( - gen: &syn::Generics, - span: proc_macro2::Span, + gen: &syn::Generics, + span: proc_macro2::Span, ) -> syn::Result { - let expected = "expected `T` or `T, I = ()`"; - pub struct Checker(InstanceUsage); - impl syn::parse::Parse for Checker { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let mut instance_usage = InstanceUsage { has_instance: false, span: input.span() }; - - input.parse::()?; - if input.peek(syn::Token![,]) { - instance_usage.has_instance = true; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - } - - Ok(Self(instance_usage)) - } - } - - let i = syn::parse2::(gen.params.to_token_stream()) - .map_err(|e| { - let msg = format!("Invalid type def generics: {}", expected); - let mut err = syn::Error::new(span, msg); - err.combine(e); - err - })? - .0; - - Ok(i) + let expected = "expected `T` or `T, I = ()`"; + pub struct Checker(InstanceUsage); + impl syn::parse::Parse for Checker { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut instance_usage = InstanceUsage { + has_instance: false, + span: input.span(), + }; + + input.parse::()?; + if input.peek(syn::Token![,]) { + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + } + + Ok(Self(instance_usage)) + } + } + + let i = syn::parse2::(gen.params.to_token_stream()) + .map_err(|e| { + let msg = format!("Invalid type def generics: {}", expected); + let mut err = syn::Error::new(span, msg); + err.combine(e); + err + })? + .0; + + Ok(i) } /// Check the syntax: @@ -264,76 +279,79 @@ pub fn check_type_def_gen_no_bounds( /// /// return some instance usage if there is some generic, or none otherwise. pub fn check_type_def_optional_gen( - gen: &syn::Generics, - span: proc_macro2::Span, + gen: &syn::Generics, + span: proc_macro2::Span, ) -> syn::Result> { - let expected = "expected `` or `T` or `T: Config` or `T, I = ()` or \ + let expected = "expected `` or `T` or `T: Config` or `T, I = ()` or \ `T: Config, I: 'static = ()`"; - pub struct Checker(Option); - impl syn::parse::Parse for Checker { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - if input.is_empty() { - return Ok(Self(None)) - } - - let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false }; - - input.parse::()?; - - if input.is_empty() { - return Ok(Self(Some(instance_usage))) - } - - let lookahead = input.lookahead1(); - if lookahead.peek(syn::Token![,]) { - instance_usage.has_instance = true; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - - Ok(Self(Some(instance_usage))) - } else if lookahead.peek(syn::Token![:]) { - input.parse::()?; - input.parse::()?; - - if input.is_empty() { - return Ok(Self(Some(instance_usage))) - } - - instance_usage.has_instance = true; - input.parse::()?; - input.parse::()?; - input.parse::]>()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - - Ok(Self(Some(instance_usage))) - } else { - Err(lookahead.error()) - } - } - } - - let i = syn::parse2::(gen.params.to_token_stream()) - .map_err(|e| { - let msg = format!("Invalid type def generics: {}", expected); - let mut err = syn::Error::new(span, msg); - err.combine(e); - err - })? - .0 - // Span can be call_site if generic is empty. Thus we replace it. - .map(|mut i| { - i.span = span; - i - }); - - Ok(i) + pub struct Checker(Option); + impl syn::parse::Parse for Checker { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + if input.is_empty() { + return Ok(Self(None)); + } + + let mut instance_usage = InstanceUsage { + span: input.span(), + has_instance: false, + }; + + input.parse::()?; + + if input.is_empty() { + return Ok(Self(Some(instance_usage))); + } + + let lookahead = input.lookahead1(); + if lookahead.peek(syn::Token![,]) { + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + + Ok(Self(Some(instance_usage))) + } else if lookahead.peek(syn::Token![:]) { + input.parse::()?; + input.parse::()?; + + if input.is_empty() { + return Ok(Self(Some(instance_usage))); + } + + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + input.parse::]>()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + + Ok(Self(Some(instance_usage))) + } else { + Err(lookahead.error()) + } + } + } + + let i = syn::parse2::(gen.params.to_token_stream()) + .map_err(|e| { + let msg = format!("Invalid type def generics: {}", expected); + let mut err = syn::Error::new(span, msg); + err.combine(e); + err + })? + .0 + // Span can be call_site if generic is empty. Thus we replace it. + .map(|mut i| { + i.span = span; + i + }); + + Ok(i) } /// Check the syntax: @@ -342,36 +360,39 @@ pub fn check_type_def_optional_gen( /// /// return the instance if found. pub fn check_pallet_struct_usage(type_: &Box) -> syn::Result { - let expected = "expected `Pallet` or `Pallet`"; - pub struct Checker(InstanceUsage); - impl syn::parse::Parse for Checker { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false }; - - input.parse::()?; - input.parse::()?; - input.parse::()?; - if input.peek(syn::Token![,]) { - instance_usage.has_instance = true; - input.parse::()?; - input.parse::()?; - } - input.parse::]>()?; - - Ok(Self(instance_usage)) - } - } - - let i = syn::parse2::(type_.to_token_stream()) - .map_err(|e| { - let msg = format!("Invalid pallet struct: {}", expected); - let mut err = syn::Error::new(type_.span(), msg); - err.combine(e); - err - })? - .0; - - Ok(i) + let expected = "expected `Pallet` or `Pallet`"; + pub struct Checker(InstanceUsage); + impl syn::parse::Parse for Checker { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut instance_usage = InstanceUsage { + span: input.span(), + has_instance: false, + }; + + input.parse::()?; + input.parse::()?; + input.parse::()?; + if input.peek(syn::Token![,]) { + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + } + input.parse::]>()?; + + Ok(Self(instance_usage)) + } + } + + let i = syn::parse2::(type_.to_token_stream()) + .map_err(|e| { + let msg = format!("Invalid pallet struct: {}", expected); + let mut err = syn::Error::new(type_.span(), msg); + err.combine(e); + err + })? + .0; + + Ok(i) } /// Check the generic is: @@ -382,39 +403,42 @@ pub fn check_pallet_struct_usage(type_: &Box) -> syn::Result syn::Result { - let expected = "expected `impl` or `impl, I: 'static>`"; - pub struct Checker(InstanceUsage); - impl syn::parse::Parse for Checker { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false }; - - input.parse::()?; - input.parse::()?; - input.parse::()?; - if input.peek(syn::Token![<]) { - instance_usage.has_instance = true; - input.parse::()?; - input.parse::()?; - input.parse::]>()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - } - - Ok(Self(instance_usage)) - } - } - - let i = syn::parse2::(gen.params.to_token_stream()) - .map_err(|e| { - let mut err = syn::Error::new(span, format!("Invalid generics: {}", expected)); - err.combine(e); - err - })? - .0; - - Ok(i) + let expected = "expected `impl` or `impl, I: 'static>`"; + pub struct Checker(InstanceUsage); + impl syn::parse::Parse for Checker { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut instance_usage = InstanceUsage { + span: input.span(), + has_instance: false, + }; + + input.parse::()?; + input.parse::()?; + input.parse::()?; + if input.peek(syn::Token![<]) { + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + input.parse::]>()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + } + + Ok(Self(instance_usage)) + } + } + + let i = syn::parse2::(gen.params.to_token_stream()) + .map_err(|e| { + let mut err = syn::Error::new(span, format!("Invalid generics: {}", expected)); + err.combine(e); + err + })? + .0; + + Ok(i) } /// Check the syntax: @@ -427,70 +451,73 @@ pub fn check_impl_gen(gen: &syn::Generics, span: proc_macro2::Span) -> syn::Resu /// /// return the instance if found. pub fn check_type_def_gen( - gen: &syn::Generics, - span: proc_macro2::Span, + gen: &syn::Generics, + span: proc_macro2::Span, ) -> syn::Result { - let expected = "expected `T` or `T: Config` or `T, I = ()` or \ + let expected = "expected `T` or `T: Config` or `T, I = ()` or \ `T: Config, I: 'static = ()`"; - pub struct Checker(InstanceUsage); - impl syn::parse::Parse for Checker { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false }; - - input.parse::()?; - - if input.is_empty() { - return Ok(Self(instance_usage)) - } - - let lookahead = input.lookahead1(); - if lookahead.peek(syn::Token![,]) { - instance_usage.has_instance = true; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - - Ok(Self(instance_usage)) - } else if lookahead.peek(syn::Token![:]) { - input.parse::()?; - input.parse::()?; - - if input.is_empty() { - return Ok(Self(instance_usage)) - } - - instance_usage.has_instance = true; - input.parse::()?; - input.parse::()?; - input.parse::]>()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - - Ok(Self(instance_usage)) - } else { - Err(lookahead.error()) - } - } - } - - let mut i = syn::parse2::(gen.params.to_token_stream()) - .map_err(|e| { - let msg = format!("Invalid type def generics: {}", expected); - let mut err = syn::Error::new(span, msg); - err.combine(e); - err - })? - .0; - - // Span can be call_site if generic is empty. Thus we replace it. - i.span = span; - - Ok(i) + pub struct Checker(InstanceUsage); + impl syn::parse::Parse for Checker { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut instance_usage = InstanceUsage { + span: input.span(), + has_instance: false, + }; + + input.parse::()?; + + if input.is_empty() { + return Ok(Self(instance_usage)); + } + + let lookahead = input.lookahead1(); + if lookahead.peek(syn::Token![,]) { + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + + Ok(Self(instance_usage)) + } else if lookahead.peek(syn::Token![:]) { + input.parse::()?; + input.parse::()?; + + if input.is_empty() { + return Ok(Self(instance_usage)); + } + + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + input.parse::]>()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + + Ok(Self(instance_usage)) + } else { + Err(lookahead.error()) + } + } + } + + let mut i = syn::parse2::(gen.params.to_token_stream()) + .map_err(|e| { + let msg = format!("Invalid type def generics: {}", expected); + let mut err = syn::Error::new(span, msg); + err.combine(e); + err + })? + .0; + + // Span can be call_site if generic is empty. Thus we replace it. + i.span = span; + + Ok(i) } /// Check the syntax: @@ -501,40 +528,43 @@ pub fn check_type_def_gen( /// return the instance if found for `GenesisBuild` /// return None for BuildGenesisConfig pub fn check_genesis_builder_usage(type_: &syn::Path) -> syn::Result> { - let expected = "expected `BuildGenesisConfig` (or the deprecated `GenesisBuild` or `GenesisBuild`)"; - pub struct Checker(Option); - impl syn::parse::Parse for Checker { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false }; - - if input.peek(keyword::GenesisBuild) { - input.parse::()?; - input.parse::()?; - input.parse::()?; - if input.peek(syn::Token![,]) { - instance_usage.has_instance = true; - input.parse::()?; - input.parse::()?; - } - input.parse::]>()?; - return Ok(Self(Some(instance_usage))) - } else { - input.parse::()?; - return Ok(Self(None)) - } - } - } - - let i = syn::parse2::(type_.to_token_stream()) - .map_err(|e| { - let msg = format!("Invalid genesis builder: {}", expected); - let mut err = syn::Error::new(type_.span(), msg); - err.combine(e); - err - })? - .0; - - Ok(i) + let expected = "expected `BuildGenesisConfig` (or the deprecated `GenesisBuild` or `GenesisBuild`)"; + pub struct Checker(Option); + impl syn::parse::Parse for Checker { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut instance_usage = InstanceUsage { + span: input.span(), + has_instance: false, + }; + + if input.peek(keyword::GenesisBuild) { + input.parse::()?; + input.parse::()?; + input.parse::()?; + if input.peek(syn::Token![,]) { + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + } + input.parse::]>()?; + return Ok(Self(Some(instance_usage))); + } else { + input.parse::()?; + return Ok(Self(None)); + } + } + } + + let i = syn::parse2::(type_.to_token_stream()) + .map_err(|e| { + let msg = format!("Invalid genesis builder: {}", expected); + let mut err = syn::Error::new(type_.span(), msg); + err.combine(e); + err + })? + .0; + + Ok(i) } /// Check the syntax: @@ -546,87 +576,90 @@ pub fn check_genesis_builder_usage(type_: &syn::Path) -> syn::Result syn::Result> { - let expected = "expected `` or `T: Config` or `T: Config, I: 'static`"; - pub struct Checker(Option); - impl syn::parse::Parse for Checker { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - if input.is_empty() { - return Ok(Self(None)) - } - - input.parse::()?; - input.parse::()?; - input.parse::()?; - - let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false }; - - if input.is_empty() { - return Ok(Self(Some(instance_usage))) - } - - instance_usage.has_instance = true; - input.parse::()?; - input.parse::()?; - input.parse::]>()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - input.parse::()?; - - Ok(Self(Some(instance_usage))) - } - } - - let i = syn::parse2::(gen.params.to_token_stream()) - .map_err(|e| { - let msg = format!("Invalid type def generics: {}", expected); - let mut err = syn::Error::new(span, msg); - err.combine(e); - err - })? - .0 - // Span can be call_site if generic is empty. Thus we replace it. - .map(|mut i| { - i.span = span; - i - }); - - Ok(i) + let expected = "expected `` or `T: Config` or `T: Config, I: 'static`"; + pub struct Checker(Option); + impl syn::parse::Parse for Checker { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + if input.is_empty() { + return Ok(Self(None)); + } + + input.parse::()?; + input.parse::()?; + input.parse::()?; + + let mut instance_usage = InstanceUsage { + span: input.span(), + has_instance: false, + }; + + if input.is_empty() { + return Ok(Self(Some(instance_usage))); + } + + instance_usage.has_instance = true; + input.parse::()?; + input.parse::()?; + input.parse::]>()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + input.parse::()?; + + Ok(Self(Some(instance_usage))) + } + } + + let i = syn::parse2::(gen.params.to_token_stream()) + .map_err(|e| { + let msg = format!("Invalid type def generics: {}", expected); + let mut err = syn::Error::new(span, msg); + err.combine(e); + err + })? + .0 + // Span can be call_site if generic is empty. Thus we replace it. + .map(|mut i| { + i.span = span; + i + }); + + Ok(i) } /// Check the keyword `DispatchResultWithPostInfo` or `DispatchResult`. pub fn check_pallet_call_return_type(type_: &syn::Type) -> syn::Result<()> { - pub struct Checker; - impl syn::parse::Parse for Checker { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let lookahead = input.lookahead1(); - if lookahead.peek(keyword::DispatchResultWithPostInfo) { - input.parse::()?; - Ok(Self) - } else if lookahead.peek(keyword::DispatchResult) { - input.parse::()?; - Ok(Self) - } else { - Err(lookahead.error()) - } - } - } - - syn::parse2::(type_.to_token_stream()).map(|_| ()) + pub struct Checker; + impl syn::parse::Parse for Checker { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let lookahead = input.lookahead1(); + if lookahead.peek(keyword::DispatchResultWithPostInfo) { + input.parse::()?; + Ok(Self) + } else if lookahead.peek(keyword::DispatchResult) { + input.parse::()?; + Ok(Self) + } else { + Err(lookahead.error()) + } + } + } + + syn::parse2::(type_.to_token_stream()).map(|_| ()) } pub(crate) fn two128_str(s: &str) -> TokenStream { - bytes_to_array(sp_crypto_hashing::twox_128(s.as_bytes()).into_iter()) + bytes_to_array(sp_crypto_hashing::twox_128(s.as_bytes()).into_iter()) } pub(crate) fn bytes_to_array(bytes: impl IntoIterator) -> TokenStream { - let bytes = bytes.into_iter(); + let bytes = bytes.into_iter(); - quote!( - [ #( #bytes ),* ] - ) - .into() + quote!( + [ #( #bytes ),* ] + ) + .into() } diff --git a/support/procedural-fork/src/pallet/parse/hooks.rs b/support/procedural-fork/src/pallet/parse/hooks.rs index 37d7d22f4..1cf5c72cc 100644 --- a/support/procedural-fork/src/pallet/parse/hooks.rs +++ b/support/procedural-fork/src/pallet/parse/hooks.rs @@ -20,67 +20,67 @@ use syn::spanned::Spanned; /// Implementation of the pallet hooks. pub struct HooksDef { - /// The index of item in pallet. - pub index: usize, - /// A set of usage of instance, must be check for consistency with trait. - pub instances: Vec, - /// The where_clause used. - pub where_clause: Option, - /// The span of the pallet::hooks attribute. - pub attr_span: proc_macro2::Span, - /// Boolean flag, set to true if the `on_runtime_upgrade` method of hooks was implemented. - pub has_runtime_upgrade: bool, + /// The index of item in pallet. + pub index: usize, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Vec, + /// The where_clause used. + pub where_clause: Option, + /// The span of the pallet::hooks attribute. + pub attr_span: proc_macro2::Span, + /// Boolean flag, set to true if the `on_runtime_upgrade` method of hooks was implemented. + pub has_runtime_upgrade: bool, } impl HooksDef { - pub fn try_from( - attr_span: proc_macro2::Span, - index: usize, - item: &mut syn::Item, - ) -> syn::Result { - let item = if let syn::Item::Impl(item) = item { - item - } else { - let msg = "Invalid pallet::hooks, expected item impl"; - return Err(syn::Error::new(item.span(), msg)) - }; + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + ) -> syn::Result { + let item = if let syn::Item::Impl(item) = item { + item + } else { + let msg = "Invalid pallet::hooks, expected item impl"; + return Err(syn::Error::new(item.span(), msg)); + }; - let instances = vec![ - helper::check_pallet_struct_usage(&item.self_ty)?, - helper::check_impl_gen(&item.generics, item.impl_token.span())?, - ]; + let instances = vec![ + helper::check_pallet_struct_usage(&item.self_ty)?, + helper::check_impl_gen(&item.generics, item.impl_token.span())?, + ]; - let item_trait = &item - .trait_ - .as_ref() - .ok_or_else(|| { - let msg = "Invalid pallet::hooks, expected impl<..> Hooks \ + let item_trait = &item + .trait_ + .as_ref() + .ok_or_else(|| { + let msg = "Invalid pallet::hooks, expected impl<..> Hooks \ for Pallet<..>"; - syn::Error::new(item.span(), msg) - })? - .1; + syn::Error::new(item.span(), msg) + })? + .1; - if item_trait.segments.len() != 1 || item_trait.segments[0].ident != "Hooks" { - let msg = format!( - "Invalid pallet::hooks, expected trait to be `Hooks` found `{}`\ + if item_trait.segments.len() != 1 || item_trait.segments[0].ident != "Hooks" { + let msg = format!( + "Invalid pallet::hooks, expected trait to be `Hooks` found `{}`\ , you can import from `frame_support::pallet_prelude`", - quote::quote!(#item_trait) - ); + quote::quote!(#item_trait) + ); - return Err(syn::Error::new(item_trait.span(), msg)) - } + return Err(syn::Error::new(item_trait.span(), msg)); + } - let has_runtime_upgrade = item.items.iter().any(|i| match i { - syn::ImplItem::Fn(method) => method.sig.ident == "on_runtime_upgrade", - _ => false, - }); + let has_runtime_upgrade = item.items.iter().any(|i| match i { + syn::ImplItem::Fn(method) => method.sig.ident == "on_runtime_upgrade", + _ => false, + }); - Ok(Self { - attr_span, - index, - instances, - has_runtime_upgrade, - where_clause: item.generics.where_clause.clone(), - }) - } + Ok(Self { + attr_span, + index, + instances, + has_runtime_upgrade, + where_clause: item.generics.where_clause.clone(), + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/inherent.rs b/support/procedural-fork/src/pallet/parse/inherent.rs index d8641691a..4eb04e914 100644 --- a/support/procedural-fork/src/pallet/parse/inherent.rs +++ b/support/procedural-fork/src/pallet/parse/inherent.rs @@ -20,41 +20,41 @@ use syn::spanned::Spanned; /// The definition of the pallet inherent implementation. pub struct InherentDef { - /// The index of inherent item in pallet module. - pub index: usize, - /// A set of usage of instance, must be check for consistency with trait. - pub instances: Vec, + /// The index of inherent item in pallet module. + pub index: usize, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Vec, } impl InherentDef { - pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { - let item = if let syn::Item::Impl(item) = item { - item - } else { - let msg = "Invalid pallet::inherent, expected item impl"; - return Err(syn::Error::new(item.span(), msg)) - }; - - if item.trait_.is_none() { - let msg = "Invalid pallet::inherent, expected impl<..> ProvideInherent for Pallet<..>"; - return Err(syn::Error::new(item.span(), msg)) - } - - if let Some(last) = item.trait_.as_ref().unwrap().1.segments.last() { - if last.ident != "ProvideInherent" { - let msg = "Invalid pallet::inherent, expected trait ProvideInherent"; - return Err(syn::Error::new(last.span(), msg)) - } - } else { - let msg = "Invalid pallet::inherent, expected impl<..> ProvideInherent for Pallet<..>"; - return Err(syn::Error::new(item.span(), msg)) - } - - let instances = vec![ - helper::check_pallet_struct_usage(&item.self_ty)?, - helper::check_impl_gen(&item.generics, item.impl_token.span())?, - ]; - - Ok(InherentDef { index, instances }) - } + pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { + let item = if let syn::Item::Impl(item) = item { + item + } else { + let msg = "Invalid pallet::inherent, expected item impl"; + return Err(syn::Error::new(item.span(), msg)); + }; + + if item.trait_.is_none() { + let msg = "Invalid pallet::inherent, expected impl<..> ProvideInherent for Pallet<..>"; + return Err(syn::Error::new(item.span(), msg)); + } + + if let Some(last) = item.trait_.as_ref().unwrap().1.segments.last() { + if last.ident != "ProvideInherent" { + let msg = "Invalid pallet::inherent, expected trait ProvideInherent"; + return Err(syn::Error::new(last.span(), msg)); + } + } else { + let msg = "Invalid pallet::inherent, expected impl<..> ProvideInherent for Pallet<..>"; + return Err(syn::Error::new(item.span(), msg)); + } + + let instances = vec![ + helper::check_pallet_struct_usage(&item.self_ty)?, + helper::check_impl_gen(&item.generics, item.impl_token.span())?, + ]; + + Ok(InherentDef { index, instances }) + } } diff --git a/support/procedural-fork/src/pallet/parse/mod.rs b/support/procedural-fork/src/pallet/parse/mod.rs index 6e1277461..57c252473 100644 --- a/support/procedural-fork/src/pallet/parse/mod.rs +++ b/support/procedural-fork/src/pallet/parse/mod.rs @@ -47,68 +47,68 @@ use syn::spanned::Spanned; /// Parsed definition of a pallet. pub struct Def { - /// The module items. - /// (their order must not be modified because they are registered in individual definitions). - pub item: syn::ItemMod, - pub config: config::ConfigDef, - pub pallet_struct: pallet_struct::PalletStructDef, - pub hooks: Option, - pub call: Option, - pub tasks: Option, - pub task_enum: Option, - pub storages: Vec, - pub error: Option, - pub event: Option, - pub origin: Option, - pub inherent: Option, - pub genesis_config: Option, - pub genesis_build: Option, - pub validate_unsigned: Option, - pub extra_constants: Option, - pub composites: Vec, - pub type_values: Vec, - pub frame_system: syn::Path, - pub frame_support: syn::Path, - pub dev_mode: bool, + /// The module items. + /// (their order must not be modified because they are registered in individual definitions). + pub item: syn::ItemMod, + pub config: config::ConfigDef, + pub pallet_struct: pallet_struct::PalletStructDef, + pub hooks: Option, + pub call: Option, + pub tasks: Option, + pub task_enum: Option, + pub storages: Vec, + pub error: Option, + pub event: Option, + pub origin: Option, + pub inherent: Option, + pub genesis_config: Option, + pub genesis_build: Option, + pub validate_unsigned: Option, + pub extra_constants: Option, + pub composites: Vec, + pub type_values: Vec, + pub frame_system: syn::Path, + pub frame_support: syn::Path, + pub dev_mode: bool, } impl Def { - pub fn try_from(mut item: syn::ItemMod, dev_mode: bool) -> syn::Result { - let frame_system = generate_access_from_frame_or_crate("frame-system")?; - let frame_support = generate_access_from_frame_or_crate("frame-support")?; - - let item_span = item.span(); - let items = &mut item - .content - .as_mut() - .ok_or_else(|| { - let msg = "Invalid pallet definition, expected mod to be inlined."; - syn::Error::new(item_span, msg) - })? - .1; - - let mut config = None; - let mut pallet_struct = None; - let mut hooks = None; - let mut call = None; - let mut tasks = None; - let mut task_enum = None; - let mut error = None; - let mut event = None; - let mut origin = None; - let mut inherent = None; - let mut genesis_config = None; - let mut genesis_build = None; - let mut validate_unsigned = None; - let mut extra_constants = None; - let mut storages = vec![]; - let mut type_values = vec![]; - let mut composites: Vec = vec![]; - - for (index, item) in items.iter_mut().enumerate() { - let pallet_attr: Option = helper::take_first_item_pallet_attr(item)?; - - match pallet_attr { + pub fn try_from(mut item: syn::ItemMod, dev_mode: bool) -> syn::Result { + let frame_system = generate_access_from_frame_or_crate("frame-system")?; + let frame_support = generate_access_from_frame_or_crate("frame-support")?; + + let item_span = item.span(); + let items = &mut item + .content + .as_mut() + .ok_or_else(|| { + let msg = "Invalid pallet definition, expected mod to be inlined."; + syn::Error::new(item_span, msg) + })? + .1; + + let mut config = None; + let mut pallet_struct = None; + let mut hooks = None; + let mut call = None; + let mut tasks = None; + let mut task_enum = None; + let mut error = None; + let mut event = None; + let mut origin = None; + let mut inherent = None; + let mut genesis_config = None; + let mut genesis_build = None; + let mut validate_unsigned = None; + let mut extra_constants = None; + let mut storages = vec![]; + let mut type_values = vec![]; + let mut composites: Vec = vec![]; + + for (index, item) in items.iter_mut().enumerate() { + let pallet_attr: Option = helper::take_first_item_pallet_attr(item)?; + + match pallet_attr { Some(PalletAttr::Config(span, with_default)) if config.is_none() => config = Some(config::ConfigDef::try_from( &frame_system, @@ -212,538 +212,596 @@ impl Def { }, None => (), } - } + } - if genesis_config.is_some() != genesis_build.is_some() { - let msg = format!( - "`#[pallet::genesis_config]` and `#[pallet::genesis_build]` attributes must be \ + if genesis_config.is_some() != genesis_build.is_some() { + let msg = format!( + "`#[pallet::genesis_config]` and `#[pallet::genesis_build]` attributes must be \ either both used or both not used, instead genesis_config is {} and genesis_build \ is {}", - genesis_config.as_ref().map_or("unused", |_| "used"), - genesis_build.as_ref().map_or("unused", |_| "used"), - ); - return Err(syn::Error::new(item_span, msg)) - } - - Self::resolve_tasks(&item_span, &mut tasks, &mut task_enum, items)?; - - let def = Def { - item, - config: config - .ok_or_else(|| syn::Error::new(item_span, "Missing `#[pallet::config]`"))?, - pallet_struct: pallet_struct - .ok_or_else(|| syn::Error::new(item_span, "Missing `#[pallet::pallet]`"))?, - hooks, - call, - tasks, - task_enum, - extra_constants, - genesis_config, - genesis_build, - validate_unsigned, - error, - event, - origin, - inherent, - storages, - composites, - type_values, - frame_system, - frame_support, - dev_mode, - }; - - def.check_instance_usage()?; - def.check_event_usage()?; - - Ok(def) - } - - /// Performs extra logic checks necessary for the `#[pallet::tasks_experimental]` feature. - fn resolve_tasks( - item_span: &proc_macro2::Span, - tasks: &mut Option, - task_enum: &mut Option, - items: &mut Vec, - ) -> syn::Result<()> { - // fallback for manual (without macros) definition of tasks impl - Self::resolve_manual_tasks_impl(tasks, task_enum, items)?; - - // fallback for manual (without macros) definition of task enum - Self::resolve_manual_task_enum(tasks, task_enum, items)?; - - // ensure that if `task_enum` is specified, `tasks` is also specified - match (&task_enum, &tasks) { - (Some(_), None) => - return Err(syn::Error::new( - *item_span, - "Missing `#[pallet::tasks_experimental]` impl", - )), - (None, Some(tasks)) => - if tasks.tasks_attr.is_none() { - return Err(syn::Error::new( + genesis_config.as_ref().map_or("unused", |_| "used"), + genesis_build.as_ref().map_or("unused", |_| "used"), + ); + return Err(syn::Error::new(item_span, msg)); + } + + Self::resolve_tasks(&item_span, &mut tasks, &mut task_enum, items)?; + + let def = Def { + item, + config: config + .ok_or_else(|| syn::Error::new(item_span, "Missing `#[pallet::config]`"))?, + pallet_struct: pallet_struct + .ok_or_else(|| syn::Error::new(item_span, "Missing `#[pallet::pallet]`"))?, + hooks, + call, + tasks, + task_enum, + extra_constants, + genesis_config, + genesis_build, + validate_unsigned, + error, + event, + origin, + inherent, + storages, + composites, + type_values, + frame_system, + frame_support, + dev_mode, + }; + + def.check_instance_usage()?; + def.check_event_usage()?; + + Ok(def) + } + + /// Performs extra logic checks necessary for the `#[pallet::tasks_experimental]` feature. + fn resolve_tasks( + item_span: &proc_macro2::Span, + tasks: &mut Option, + task_enum: &mut Option, + items: &mut Vec, + ) -> syn::Result<()> { + // fallback for manual (without macros) definition of tasks impl + Self::resolve_manual_tasks_impl(tasks, task_enum, items)?; + + // fallback for manual (without macros) definition of task enum + Self::resolve_manual_task_enum(tasks, task_enum, items)?; + + // ensure that if `task_enum` is specified, `tasks` is also specified + match (&task_enum, &tasks) { + (Some(_), None) => { + return Err(syn::Error::new( + *item_span, + "Missing `#[pallet::tasks_experimental]` impl", + )) + } + (None, Some(tasks)) => { + if tasks.tasks_attr.is_none() { + return Err(syn::Error::new( tasks.item_impl.impl_token.span(), "A `#[pallet::tasks_experimental]` attribute must be attached to your `Task` impl if the \ task enum has been omitted", - )) - } else { - }, - _ => (), - } - - Ok(()) - } - - /// Tries to locate task enum based on the tasks impl target if attribute is not specified - /// but impl is present. If one is found, `task_enum` is set appropriately. - fn resolve_manual_task_enum( - tasks: &Option, - task_enum: &mut Option, - items: &mut Vec, - ) -> syn::Result<()> { - let (None, Some(tasks)) = (&task_enum, &tasks) else { return Ok(()) }; - let syn::Type::Path(type_path) = &*tasks.item_impl.self_ty else { return Ok(()) }; - let type_path = type_path.path.segments.iter().collect::>(); - let (Some(seg), None) = (type_path.get(0), type_path.get(1)) else { return Ok(()) }; - let mut result = None; - for item in items { - let syn::Item::Enum(item_enum) = item else { continue }; - if item_enum.ident == seg.ident { - result = Some(syn::parse2::(item_enum.to_token_stream())?); - // replace item with a no-op because it will be handled by the expansion of - // `task_enum`. We use a no-op instead of simply removing it from the vec - // so that any indices collected by `Def::try_from` remain accurate - *item = syn::Item::Verbatim(quote::quote!()); - break - } - } - *task_enum = result; - Ok(()) - } - - /// Tries to locate a manual tasks impl (an impl implementing a trait whose last path segment is - /// `Task`) in the event that one has not been found already via the attribute macro - pub fn resolve_manual_tasks_impl( - tasks: &mut Option, - task_enum: &Option, - items: &Vec, - ) -> syn::Result<()> { - let None = tasks else { return Ok(()) }; - let mut result = None; - for item in items { - let syn::Item::Impl(item_impl) = item else { continue }; - let Some((_, path, _)) = &item_impl.trait_ else { continue }; - let Some(trait_last_seg) = path.segments.last() else { continue }; - let syn::Type::Path(target_path) = &*item_impl.self_ty else { continue }; - let target_path = target_path.path.segments.iter().collect::>(); - let (Some(target_ident), None) = (target_path.get(0), target_path.get(1)) else { - continue - }; - let matches_task_enum = match task_enum { - Some(task_enum) => task_enum.item_enum.ident == target_ident.ident, - None => true, - }; - if trait_last_seg.ident == "Task" && matches_task_enum { - result = Some(syn::parse2::(item_impl.to_token_stream())?); - break - } - } - *tasks = result; - Ok(()) - } - - /// Check that usage of trait `Event` is consistent with the definition, i.e. it is declared - /// and trait defines type RuntimeEvent, or not declared and no trait associated type. - fn check_event_usage(&self) -> syn::Result<()> { - match (self.config.has_event_type, self.event.is_some()) { - (true, false) => { - let msg = "Invalid usage of RuntimeEvent, `Config` contains associated type `RuntimeEvent`, \ + )); + } else { + } + } + _ => (), + } + + Ok(()) + } + + /// Tries to locate task enum based on the tasks impl target if attribute is not specified + /// but impl is present. If one is found, `task_enum` is set appropriately. + fn resolve_manual_task_enum( + tasks: &Option, + task_enum: &mut Option, + items: &mut Vec, + ) -> syn::Result<()> { + let (None, Some(tasks)) = (&task_enum, &tasks) else { + return Ok(()); + }; + let syn::Type::Path(type_path) = &*tasks.item_impl.self_ty else { + return Ok(()); + }; + let type_path = type_path.path.segments.iter().collect::>(); + let (Some(seg), None) = (type_path.get(0), type_path.get(1)) else { + return Ok(()); + }; + let mut result = None; + for item in items { + let syn::Item::Enum(item_enum) = item else { + continue; + }; + if item_enum.ident == seg.ident { + result = Some(syn::parse2::( + item_enum.to_token_stream(), + )?); + // replace item with a no-op because it will be handled by the expansion of + // `task_enum`. We use a no-op instead of simply removing it from the vec + // so that any indices collected by `Def::try_from` remain accurate + *item = syn::Item::Verbatim(quote::quote!()); + break; + } + } + *task_enum = result; + Ok(()) + } + + /// Tries to locate a manual tasks impl (an impl implementing a trait whose last path segment is + /// `Task`) in the event that one has not been found already via the attribute macro + pub fn resolve_manual_tasks_impl( + tasks: &mut Option, + task_enum: &Option, + items: &Vec, + ) -> syn::Result<()> { + let None = tasks else { return Ok(()) }; + let mut result = None; + for item in items { + let syn::Item::Impl(item_impl) = item else { + continue; + }; + let Some((_, path, _)) = &item_impl.trait_ else { + continue; + }; + let Some(trait_last_seg) = path.segments.last() else { + continue; + }; + let syn::Type::Path(target_path) = &*item_impl.self_ty else { + continue; + }; + let target_path = target_path.path.segments.iter().collect::>(); + let (Some(target_ident), None) = (target_path.get(0), target_path.get(1)) else { + continue; + }; + let matches_task_enum = match task_enum { + Some(task_enum) => task_enum.item_enum.ident == target_ident.ident, + None => true, + }; + if trait_last_seg.ident == "Task" && matches_task_enum { + result = Some(syn::parse2::(item_impl.to_token_stream())?); + break; + } + } + *tasks = result; + Ok(()) + } + + /// Check that usage of trait `Event` is consistent with the definition, i.e. it is declared + /// and trait defines type RuntimeEvent, or not declared and no trait associated type. + fn check_event_usage(&self) -> syn::Result<()> { + match (self.config.has_event_type, self.event.is_some()) { + (true, false) => { + let msg = "Invalid usage of RuntimeEvent, `Config` contains associated type `RuntimeEvent`, \ but enum `Event` is not declared (i.e. no use of `#[pallet::event]`). \ Note that type `RuntimeEvent` in trait is reserved to work alongside pallet event."; - Err(syn::Error::new(proc_macro2::Span::call_site(), msg)) - }, - (false, true) => { - let msg = "Invalid usage of RuntimeEvent, `Config` contains no associated type \ + Err(syn::Error::new(proc_macro2::Span::call_site(), msg)) + } + (false, true) => { + let msg = "Invalid usage of RuntimeEvent, `Config` contains no associated type \ `RuntimeEvent`, but enum `Event` is declared (in use of `#[pallet::event]`). \ An RuntimeEvent associated type must be declare on trait `Config`."; - Err(syn::Error::new(proc_macro2::Span::call_site(), msg)) - }, - _ => Ok(()), - } - } - - /// Check that usage of trait `Config` is consistent with the definition, i.e. it is used with - /// instance iff it is defined with instance. - fn check_instance_usage(&self) -> syn::Result<()> { - let mut instances = vec![]; - instances.extend_from_slice(&self.pallet_struct.instances[..]); - instances.extend(&mut self.storages.iter().flat_map(|s| s.instances.clone())); - if let Some(call) = &self.call { - instances.extend_from_slice(&call.instances[..]); - } - if let Some(hooks) = &self.hooks { - instances.extend_from_slice(&hooks.instances[..]); - } - if let Some(event) = &self.event { - instances.extend_from_slice(&event.instances[..]); - } - if let Some(error) = &self.error { - instances.extend_from_slice(&error.instances[..]); - } - if let Some(inherent) = &self.inherent { - instances.extend_from_slice(&inherent.instances[..]); - } - if let Some(origin) = &self.origin { - instances.extend_from_slice(&origin.instances[..]); - } - if let Some(genesis_config) = &self.genesis_config { - instances.extend_from_slice(&genesis_config.instances[..]); - } - if let Some(genesis_build) = &self.genesis_build { - genesis_build.instances.as_ref().map(|i| instances.extend_from_slice(&i)); - } - if let Some(extra_constants) = &self.extra_constants { - instances.extend_from_slice(&extra_constants.instances[..]); - } - - let mut errors = instances.into_iter().filter_map(|instances| { - if instances.has_instance == self.config.has_instance { - return None - } - let msg = if self.config.has_instance { - "Invalid generic declaration, trait is defined with instance but generic use none" - } else { - "Invalid generic declaration, trait is defined without instance but generic use \ + Err(syn::Error::new(proc_macro2::Span::call_site(), msg)) + } + _ => Ok(()), + } + } + + /// Check that usage of trait `Config` is consistent with the definition, i.e. it is used with + /// instance iff it is defined with instance. + fn check_instance_usage(&self) -> syn::Result<()> { + let mut instances = vec![]; + instances.extend_from_slice(&self.pallet_struct.instances[..]); + instances.extend(&mut self.storages.iter().flat_map(|s| s.instances.clone())); + if let Some(call) = &self.call { + instances.extend_from_slice(&call.instances[..]); + } + if let Some(hooks) = &self.hooks { + instances.extend_from_slice(&hooks.instances[..]); + } + if let Some(event) = &self.event { + instances.extend_from_slice(&event.instances[..]); + } + if let Some(error) = &self.error { + instances.extend_from_slice(&error.instances[..]); + } + if let Some(inherent) = &self.inherent { + instances.extend_from_slice(&inherent.instances[..]); + } + if let Some(origin) = &self.origin { + instances.extend_from_slice(&origin.instances[..]); + } + if let Some(genesis_config) = &self.genesis_config { + instances.extend_from_slice(&genesis_config.instances[..]); + } + if let Some(genesis_build) = &self.genesis_build { + genesis_build + .instances + .as_ref() + .map(|i| instances.extend_from_slice(&i)); + } + if let Some(extra_constants) = &self.extra_constants { + instances.extend_from_slice(&extra_constants.instances[..]); + } + + let mut errors = instances.into_iter().filter_map(|instances| { + if instances.has_instance == self.config.has_instance { + return None; + } + let msg = if self.config.has_instance { + "Invalid generic declaration, trait is defined with instance but generic use none" + } else { + "Invalid generic declaration, trait is defined without instance but generic use \ some" - }; - Some(syn::Error::new(instances.span, msg)) - }); - - if let Some(mut first_error) = errors.next() { - for error in errors { - first_error.combine(error) - } - Err(first_error) - } else { - Ok(()) - } - } - - /// Depending on if pallet is instantiable: - /// * either `T: Config` - /// * or `T: Config, I: 'static` - pub fn type_impl_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { - if self.config.has_instance { - quote::quote_spanned!(span => T: Config, I: 'static) - } else { - quote::quote_spanned!(span => T: Config) - } - } - - /// Depending on if pallet is instantiable: - /// * either `T: Config` - /// * or `T: Config, I: 'static = ()` - pub fn type_decl_bounded_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { - if self.config.has_instance { - quote::quote_spanned!(span => T: Config, I: 'static = ()) - } else { - quote::quote_spanned!(span => T: Config) - } - } - - /// Depending on if pallet is instantiable: - /// * either `T` - /// * or `T, I = ()` - pub fn type_decl_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { - if self.config.has_instance { - quote::quote_spanned!(span => T, I = ()) - } else { - quote::quote_spanned!(span => T) - } - } - - /// Depending on if pallet is instantiable: - /// * either `` - /// * or `` - /// to be used when using pallet trait `Config` - pub fn trait_use_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { - if self.config.has_instance { - quote::quote_spanned!(span => ) - } else { - quote::quote_spanned!(span => ) - } - } - - /// Depending on if pallet is instantiable: - /// * either `T` - /// * or `T, I` - pub fn type_use_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { - if self.config.has_instance { - quote::quote_spanned!(span => T, I) - } else { - quote::quote_spanned!(span => T) - } - } + }; + Some(syn::Error::new(instances.span, msg)) + }); + + if let Some(mut first_error) = errors.next() { + for error in errors { + first_error.combine(error) + } + Err(first_error) + } else { + Ok(()) + } + } + + /// Depending on if pallet is instantiable: + /// * either `T: Config` + /// * or `T: Config, I: 'static` + pub fn type_impl_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { + if self.config.has_instance { + quote::quote_spanned!(span => T: Config, I: 'static) + } else { + quote::quote_spanned!(span => T: Config) + } + } + + /// Depending on if pallet is instantiable: + /// * either `T: Config` + /// * or `T: Config, I: 'static = ()` + pub fn type_decl_bounded_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { + if self.config.has_instance { + quote::quote_spanned!(span => T: Config, I: 'static = ()) + } else { + quote::quote_spanned!(span => T: Config) + } + } + + /// Depending on if pallet is instantiable: + /// * either `T` + /// * or `T, I = ()` + pub fn type_decl_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { + if self.config.has_instance { + quote::quote_spanned!(span => T, I = ()) + } else { + quote::quote_spanned!(span => T) + } + } + + /// Depending on if pallet is instantiable: + /// * either `` + /// * or `` + /// to be used when using pallet trait `Config` + pub fn trait_use_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { + if self.config.has_instance { + quote::quote_spanned!(span => ) + } else { + quote::quote_spanned!(span => ) + } + } + + /// Depending on if pallet is instantiable: + /// * either `T` + /// * or `T, I` + pub fn type_use_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { + if self.config.has_instance { + quote::quote_spanned!(span => T, I) + } else { + quote::quote_spanned!(span => T) + } + } } /// Some generic kind for type which can be not generic, or generic over config, /// or generic over config and instance, but not generic only over instance. pub enum GenericKind { - None, - Config, - ConfigAndInstance, + None, + Config, + ConfigAndInstance, } impl GenericKind { - /// Return Err if it is only generics over instance but not over config. - pub fn from_gens(has_config: bool, has_instance: bool) -> Result { - match (has_config, has_instance) { - (false, false) => Ok(GenericKind::None), - (true, false) => Ok(GenericKind::Config), - (true, true) => Ok(GenericKind::ConfigAndInstance), - (false, true) => Err(()), - } - } - - /// Return the generic to be used when using the type. - /// - /// Depending on its definition it can be: ``, `T` or `T, I` - pub fn type_use_gen(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { - match self { - GenericKind::None => quote::quote!(), - GenericKind::Config => quote::quote_spanned!(span => T), - GenericKind::ConfigAndInstance => quote::quote_spanned!(span => T, I), - } - } - - /// Return the generic to be used in `impl<..>` when implementing on the type. - pub fn type_impl_gen(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { - match self { - GenericKind::None => quote::quote!(), - GenericKind::Config => quote::quote_spanned!(span => T: Config), - GenericKind::ConfigAndInstance => { - quote::quote_spanned!(span => T: Config, I: 'static) - }, - } - } - - /// Return whereas the type has some generic. - pub fn is_generic(&self) -> bool { - match self { - GenericKind::None => false, - GenericKind::Config | GenericKind::ConfigAndInstance => true, - } - } + /// Return Err if it is only generics over instance but not over config. + pub fn from_gens(has_config: bool, has_instance: bool) -> Result { + match (has_config, has_instance) { + (false, false) => Ok(GenericKind::None), + (true, false) => Ok(GenericKind::Config), + (true, true) => Ok(GenericKind::ConfigAndInstance), + (false, true) => Err(()), + } + } + + /// Return the generic to be used when using the type. + /// + /// Depending on its definition it can be: ``, `T` or `T, I` + pub fn type_use_gen(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { + match self { + GenericKind::None => quote::quote!(), + GenericKind::Config => quote::quote_spanned!(span => T), + GenericKind::ConfigAndInstance => quote::quote_spanned!(span => T, I), + } + } + + /// Return the generic to be used in `impl<..>` when implementing on the type. + pub fn type_impl_gen(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream { + match self { + GenericKind::None => quote::quote!(), + GenericKind::Config => quote::quote_spanned!(span => T: Config), + GenericKind::ConfigAndInstance => { + quote::quote_spanned!(span => T: Config, I: 'static) + } + } + } + + /// Return whereas the type has some generic. + pub fn is_generic(&self) -> bool { + match self { + GenericKind::None => false, + GenericKind::Config | GenericKind::ConfigAndInstance => true, + } + } } /// List of additional token to be used for parsing. mod keyword { - syn::custom_keyword!(origin); - syn::custom_keyword!(call); - syn::custom_keyword!(tasks_experimental); - syn::custom_keyword!(task_enum); - syn::custom_keyword!(task_list); - syn::custom_keyword!(task_condition); - syn::custom_keyword!(task_index); - syn::custom_keyword!(weight); - syn::custom_keyword!(event); - syn::custom_keyword!(config); - syn::custom_keyword!(with_default); - syn::custom_keyword!(hooks); - syn::custom_keyword!(inherent); - syn::custom_keyword!(error); - syn::custom_keyword!(storage); - syn::custom_keyword!(genesis_build); - syn::custom_keyword!(genesis_config); - syn::custom_keyword!(validate_unsigned); - syn::custom_keyword!(type_value); - syn::custom_keyword!(pallet); - syn::custom_keyword!(extra_constants); - syn::custom_keyword!(composite_enum); + syn::custom_keyword!(origin); + syn::custom_keyword!(call); + syn::custom_keyword!(tasks_experimental); + syn::custom_keyword!(task_enum); + syn::custom_keyword!(task_list); + syn::custom_keyword!(task_condition); + syn::custom_keyword!(task_index); + syn::custom_keyword!(weight); + syn::custom_keyword!(event); + syn::custom_keyword!(config); + syn::custom_keyword!(with_default); + syn::custom_keyword!(hooks); + syn::custom_keyword!(inherent); + syn::custom_keyword!(error); + syn::custom_keyword!(storage); + syn::custom_keyword!(genesis_build); + syn::custom_keyword!(genesis_config); + syn::custom_keyword!(validate_unsigned); + syn::custom_keyword!(type_value); + syn::custom_keyword!(pallet); + syn::custom_keyword!(extra_constants); + syn::custom_keyword!(composite_enum); } /// Parse attributes for item in pallet module /// syntax must be `pallet::` (e.g. `#[pallet::config]`) enum PalletAttr { - Config(proc_macro2::Span, bool), - Pallet(proc_macro2::Span), - Hooks(proc_macro2::Span), - /// A `#[pallet::call]` with optional attributes to specialize the behaviour. - /// - /// # Attributes - /// - /// Each attribute `attr` can take the form of `#[pallet::call(attr = …)]` or - /// `#[pallet::call(attr(…))]`. The possible attributes are: - /// - /// ## `weight` - /// - /// Can be used to reduce the repetitive weight annotation in the trivial case. It accepts one - /// argument that is expected to be an implementation of the `WeightInfo` or something that - /// behaves syntactically equivalent. This allows to annotate a `WeightInfo` for all the calls. - /// Now each call does not need to specify its own `#[pallet::weight]` but can instead use the - /// one from the `#[pallet::call]` definition. So instead of having to write it on each call: - /// - /// ```ignore - /// #[pallet::call] - /// impl Pallet { - /// #[pallet::weight(T::WeightInfo::create())] - /// pub fn create( - /// ``` - /// you can now omit it on the call itself, if the name of the weigh function matches the call: - /// - /// ```ignore - /// #[pallet::call(weight = ::WeightInfo)] - /// impl Pallet { - /// pub fn create( - /// ``` - /// - /// It is possible to use this syntax together with instantiated pallets by using `Config` - /// instead. - /// - /// ### Dev Mode - /// - /// Normally the `dev_mode` sets all weights of calls without a `#[pallet::weight]` annotation - /// to zero. Now when there is a `weight` attribute on the `#[pallet::call]`, then that is used - /// instead of the zero weight. So to say: it works together with `dev_mode`. - RuntimeCall(Option, proc_macro2::Span), - Error(proc_macro2::Span), - Tasks(proc_macro2::Span), - TaskList(proc_macro2::Span), - TaskCondition(proc_macro2::Span), - TaskIndex(proc_macro2::Span), - RuntimeTask(proc_macro2::Span), - RuntimeEvent(proc_macro2::Span), - RuntimeOrigin(proc_macro2::Span), - Inherent(proc_macro2::Span), - Storage(proc_macro2::Span), - GenesisConfig(proc_macro2::Span), - GenesisBuild(proc_macro2::Span), - ValidateUnsigned(proc_macro2::Span), - TypeValue(proc_macro2::Span), - ExtraConstants(proc_macro2::Span), - Composite(proc_macro2::Span), + Config(proc_macro2::Span, bool), + Pallet(proc_macro2::Span), + Hooks(proc_macro2::Span), + /// A `#[pallet::call]` with optional attributes to specialize the behaviour. + /// + /// # Attributes + /// + /// Each attribute `attr` can take the form of `#[pallet::call(attr = …)]` or + /// `#[pallet::call(attr(…))]`. The possible attributes are: + /// + /// ## `weight` + /// + /// Can be used to reduce the repetitive weight annotation in the trivial case. It accepts one + /// argument that is expected to be an implementation of the `WeightInfo` or something that + /// behaves syntactically equivalent. This allows to annotate a `WeightInfo` for all the calls. + /// Now each call does not need to specify its own `#[pallet::weight]` but can instead use the + /// one from the `#[pallet::call]` definition. So instead of having to write it on each call: + /// + /// ```ignore + /// #[pallet::call] + /// impl Pallet { + /// #[pallet::weight(T::WeightInfo::create())] + /// pub fn create( + /// ``` + /// you can now omit it on the call itself, if the name of the weigh function matches the call: + /// + /// ```ignore + /// #[pallet::call(weight = ::WeightInfo)] + /// impl Pallet { + /// pub fn create( + /// ``` + /// + /// It is possible to use this syntax together with instantiated pallets by using `Config` + /// instead. + /// + /// ### Dev Mode + /// + /// Normally the `dev_mode` sets all weights of calls without a `#[pallet::weight]` annotation + /// to zero. Now when there is a `weight` attribute on the `#[pallet::call]`, then that is used + /// instead of the zero weight. So to say: it works together with `dev_mode`. + RuntimeCall(Option, proc_macro2::Span), + Error(proc_macro2::Span), + Tasks(proc_macro2::Span), + TaskList(proc_macro2::Span), + TaskCondition(proc_macro2::Span), + TaskIndex(proc_macro2::Span), + RuntimeTask(proc_macro2::Span), + RuntimeEvent(proc_macro2::Span), + RuntimeOrigin(proc_macro2::Span), + Inherent(proc_macro2::Span), + Storage(proc_macro2::Span), + GenesisConfig(proc_macro2::Span), + GenesisBuild(proc_macro2::Span), + ValidateUnsigned(proc_macro2::Span), + TypeValue(proc_macro2::Span), + ExtraConstants(proc_macro2::Span), + Composite(proc_macro2::Span), } impl PalletAttr { - fn span(&self) -> proc_macro2::Span { - match self { - Self::Config(span, _) => *span, - Self::Pallet(span) => *span, - Self::Hooks(span) => *span, - Self::Tasks(span) => *span, - Self::TaskCondition(span) => *span, - Self::TaskIndex(span) => *span, - Self::TaskList(span) => *span, - Self::Error(span) => *span, - Self::RuntimeTask(span) => *span, - Self::RuntimeCall(_, span) => *span, - Self::RuntimeEvent(span) => *span, - Self::RuntimeOrigin(span) => *span, - Self::Inherent(span) => *span, - Self::Storage(span) => *span, - Self::GenesisConfig(span) => *span, - Self::GenesisBuild(span) => *span, - Self::ValidateUnsigned(span) => *span, - Self::TypeValue(span) => *span, - Self::ExtraConstants(span) => *span, - Self::Composite(span) => *span, - } - } + fn span(&self) -> proc_macro2::Span { + match self { + Self::Config(span, _) => *span, + Self::Pallet(span) => *span, + Self::Hooks(span) => *span, + Self::Tasks(span) => *span, + Self::TaskCondition(span) => *span, + Self::TaskIndex(span) => *span, + Self::TaskList(span) => *span, + Self::Error(span) => *span, + Self::RuntimeTask(span) => *span, + Self::RuntimeCall(_, span) => *span, + Self::RuntimeEvent(span) => *span, + Self::RuntimeOrigin(span) => *span, + Self::Inherent(span) => *span, + Self::Storage(span) => *span, + Self::GenesisConfig(span) => *span, + Self::GenesisBuild(span) => *span, + Self::ValidateUnsigned(span) => *span, + Self::TypeValue(span) => *span, + Self::ExtraConstants(span) => *span, + Self::Composite(span) => *span, + } + } } impl syn::parse::Parse for PalletAttr { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - let content; - syn::bracketed!(content in input); - content.parse::()?; - content.parse::()?; - - let lookahead = content.lookahead1(); - if lookahead.peek(keyword::config) { - let span = content.parse::()?.span(); - let with_default = content.peek(syn::token::Paren); - if with_default { - let inside_config; - let _paren = syn::parenthesized!(inside_config in content); - inside_config.parse::()?; - } - Ok(PalletAttr::Config(span, with_default)) - } else if lookahead.peek(keyword::pallet) { - Ok(PalletAttr::Pallet(content.parse::()?.span())) - } else if lookahead.peek(keyword::hooks) { - Ok(PalletAttr::Hooks(content.parse::()?.span())) - } else if lookahead.peek(keyword::call) { - let span = content.parse::().expect("peeked").span(); - let attr = match content.is_empty() { - true => None, - false => Some(InheritedCallWeightAttr::parse(&content)?), - }; - Ok(PalletAttr::RuntimeCall(attr, span)) - } else if lookahead.peek(keyword::tasks_experimental) { - Ok(PalletAttr::Tasks(content.parse::()?.span())) - } else if lookahead.peek(keyword::task_enum) { - Ok(PalletAttr::RuntimeTask(content.parse::()?.span())) - } else if lookahead.peek(keyword::task_condition) { - Ok(PalletAttr::TaskCondition(content.parse::()?.span())) - } else if lookahead.peek(keyword::task_index) { - Ok(PalletAttr::TaskIndex(content.parse::()?.span())) - } else if lookahead.peek(keyword::task_list) { - Ok(PalletAttr::TaskList(content.parse::()?.span())) - } else if lookahead.peek(keyword::error) { - Ok(PalletAttr::Error(content.parse::()?.span())) - } else if lookahead.peek(keyword::event) { - Ok(PalletAttr::RuntimeEvent(content.parse::()?.span())) - } else if lookahead.peek(keyword::origin) { - Ok(PalletAttr::RuntimeOrigin(content.parse::()?.span())) - } else if lookahead.peek(keyword::inherent) { - Ok(PalletAttr::Inherent(content.parse::()?.span())) - } else if lookahead.peek(keyword::storage) { - Ok(PalletAttr::Storage(content.parse::()?.span())) - } else if lookahead.peek(keyword::genesis_config) { - Ok(PalletAttr::GenesisConfig(content.parse::()?.span())) - } else if lookahead.peek(keyword::genesis_build) { - Ok(PalletAttr::GenesisBuild(content.parse::()?.span())) - } else if lookahead.peek(keyword::validate_unsigned) { - Ok(PalletAttr::ValidateUnsigned(content.parse::()?.span())) - } else if lookahead.peek(keyword::type_value) { - Ok(PalletAttr::TypeValue(content.parse::()?.span())) - } else if lookahead.peek(keyword::extra_constants) { - Ok(PalletAttr::ExtraConstants(content.parse::()?.span())) - } else if lookahead.peek(keyword::composite_enum) { - Ok(PalletAttr::Composite(content.parse::()?.span())) - } else { - Err(lookahead.error()) - } - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + let lookahead = content.lookahead1(); + if lookahead.peek(keyword::config) { + let span = content.parse::()?.span(); + let with_default = content.peek(syn::token::Paren); + if with_default { + let inside_config; + let _paren = syn::parenthesized!(inside_config in content); + inside_config.parse::()?; + } + Ok(PalletAttr::Config(span, with_default)) + } else if lookahead.peek(keyword::pallet) { + Ok(PalletAttr::Pallet( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::hooks) { + Ok(PalletAttr::Hooks(content.parse::()?.span())) + } else if lookahead.peek(keyword::call) { + let span = content.parse::().expect("peeked").span(); + let attr = match content.is_empty() { + true => None, + false => Some(InheritedCallWeightAttr::parse(&content)?), + }; + Ok(PalletAttr::RuntimeCall(attr, span)) + } else if lookahead.peek(keyword::tasks_experimental) { + Ok(PalletAttr::Tasks( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::task_enum) { + Ok(PalletAttr::RuntimeTask( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::task_condition) { + Ok(PalletAttr::TaskCondition( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::task_index) { + Ok(PalletAttr::TaskIndex( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::task_list) { + Ok(PalletAttr::TaskList( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::error) { + Ok(PalletAttr::Error(content.parse::()?.span())) + } else if lookahead.peek(keyword::event) { + Ok(PalletAttr::RuntimeEvent( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::origin) { + Ok(PalletAttr::RuntimeOrigin( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::inherent) { + Ok(PalletAttr::Inherent( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::storage) { + Ok(PalletAttr::Storage( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::genesis_config) { + Ok(PalletAttr::GenesisConfig( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::genesis_build) { + Ok(PalletAttr::GenesisBuild( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::validate_unsigned) { + Ok(PalletAttr::ValidateUnsigned( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::type_value) { + Ok(PalletAttr::TypeValue( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::extra_constants) { + Ok(PalletAttr::ExtraConstants( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::composite_enum) { + Ok(PalletAttr::Composite( + content.parse::()?.span(), + )) + } else { + Err(lookahead.error()) + } + } } /// The optional weight annotation on a `#[pallet::call]` like `#[pallet::call(weight($type))]`. #[derive(Clone)] pub struct InheritedCallWeightAttr { - pub typename: syn::Type, - pub span: proc_macro2::Span, + pub typename: syn::Type, + pub span: proc_macro2::Span, } impl syn::parse::Parse for InheritedCallWeightAttr { - // Parses `(weight($type))` or `(weight = $type)`. - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let content; - syn::parenthesized!(content in input); - content.parse::()?; - let lookahead = content.lookahead1(); - - let buffer = if lookahead.peek(syn::token::Paren) { - let inner; - syn::parenthesized!(inner in content); - inner - } else if lookahead.peek(syn::Token![=]) { - content.parse::().expect("peeked"); - content - } else { - return Err(lookahead.error()) - }; - - Ok(Self { typename: buffer.parse()?, span: input.span() }) - } + // Parses `(weight($type))` or `(weight = $type)`. + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let content; + syn::parenthesized!(content in input); + content.parse::()?; + let lookahead = content.lookahead1(); + + let buffer = if lookahead.peek(syn::token::Paren) { + let inner; + syn::parenthesized!(inner in content); + inner + } else if lookahead.peek(syn::Token![=]) { + content.parse::().expect("peeked"); + content + } else { + return Err(lookahead.error()); + }; + + Ok(Self { + typename: buffer.parse()?, + span: input.span(), + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/origin.rs b/support/procedural-fork/src/pallet/parse/origin.rs index 76e2a8841..2dd84c40d 100644 --- a/support/procedural-fork/src/pallet/parse/origin.rs +++ b/support/procedural-fork/src/pallet/parse/origin.rs @@ -25,48 +25,56 @@ use syn::spanned::Spanned; /// * `struct Origin` /// * `enum Origin` pub struct OriginDef { - /// The index of item in pallet module. - pub index: usize, - pub has_instance: bool, - pub is_generic: bool, - /// A set of usage of instance, must be check for consistency with trait. - pub instances: Vec, + /// The index of item in pallet module. + pub index: usize, + pub has_instance: bool, + pub is_generic: bool, + /// A set of usage of instance, must be check for consistency with trait. + pub instances: Vec, } impl OriginDef { - pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { - let item_span = item.span(); - let (vis, ident, generics) = match &item { - syn::Item::Enum(item) => (&item.vis, &item.ident, &item.generics), - syn::Item::Struct(item) => (&item.vis, &item.ident, &item.generics), - syn::Item::Type(item) => (&item.vis, &item.ident, &item.generics), - _ => { - let msg = "Invalid pallet::origin, expected enum or struct or type"; - return Err(syn::Error::new(item.span(), msg)) - }, - }; + pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { + let item_span = item.span(); + let (vis, ident, generics) = match &item { + syn::Item::Enum(item) => (&item.vis, &item.ident, &item.generics), + syn::Item::Struct(item) => (&item.vis, &item.ident, &item.generics), + syn::Item::Type(item) => (&item.vis, &item.ident, &item.generics), + _ => { + let msg = "Invalid pallet::origin, expected enum or struct or type"; + return Err(syn::Error::new(item.span(), msg)); + } + }; - let has_instance = generics.params.len() == 2; - let is_generic = !generics.params.is_empty(); + let has_instance = generics.params.len() == 2; + let is_generic = !generics.params.is_empty(); - let mut instances = vec![]; - if let Some(u) = helper::check_type_def_optional_gen(generics, item.span())? { - instances.push(u); - } else { - // construct_runtime only allow generic event for instantiable pallet. - instances.push(helper::InstanceUsage { has_instance: false, span: ident.span() }) - } + let mut instances = vec![]; + if let Some(u) = helper::check_type_def_optional_gen(generics, item.span())? { + instances.push(u); + } else { + // construct_runtime only allow generic event for instantiable pallet. + instances.push(helper::InstanceUsage { + has_instance: false, + span: ident.span(), + }) + } - if !matches!(vis, syn::Visibility::Public(_)) { - let msg = "Invalid pallet::origin, Origin must be public"; - return Err(syn::Error::new(item_span, msg)) - } + if !matches!(vis, syn::Visibility::Public(_)) { + let msg = "Invalid pallet::origin, Origin must be public"; + return Err(syn::Error::new(item_span, msg)); + } - if ident != "Origin" { - let msg = "Invalid pallet::origin, ident must `Origin`"; - return Err(syn::Error::new(ident.span(), msg)) - } + if ident != "Origin" { + let msg = "Invalid pallet::origin, ident must `Origin`"; + return Err(syn::Error::new(ident.span(), msg)); + } - Ok(OriginDef { index, has_instance, is_generic, instances }) - } + Ok(OriginDef { + index, + has_instance, + is_generic, + instances, + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/pallet_struct.rs b/support/procedural-fork/src/pallet/parse/pallet_struct.rs index b64576099..320cf01fa 100644 --- a/support/procedural-fork/src/pallet/parse/pallet_struct.rs +++ b/support/procedural-fork/src/pallet/parse/pallet_struct.rs @@ -21,129 +21,137 @@ use syn::spanned::Spanned; /// List of additional token to be used for parsing. mod keyword { - syn::custom_keyword!(pallet); - syn::custom_keyword!(Pallet); - syn::custom_keyword!(without_storage_info); - syn::custom_keyword!(storage_version); + syn::custom_keyword!(pallet); + syn::custom_keyword!(Pallet); + syn::custom_keyword!(without_storage_info); + syn::custom_keyword!(storage_version); } /// Definition of the pallet pallet. pub struct PalletStructDef { - /// The index of item in pallet pallet. - pub index: usize, - /// A set of usage of instance, must be check for consistency with config trait. - pub instances: Vec, - /// The keyword Pallet used (contains span). - pub pallet: keyword::Pallet, - /// The span of the pallet::pallet attribute. - pub attr_span: proc_macro2::Span, - /// Whether to specify the storages max encoded len when implementing `StorageInfoTrait`. - /// Contains the span of the attribute. - pub without_storage_info: Option, - /// The in-code storage version of the pallet. - pub storage_version: Option, + /// The index of item in pallet pallet. + pub index: usize, + /// A set of usage of instance, must be check for consistency with config trait. + pub instances: Vec, + /// The keyword Pallet used (contains span). + pub pallet: keyword::Pallet, + /// The span of the pallet::pallet attribute. + pub attr_span: proc_macro2::Span, + /// Whether to specify the storages max encoded len when implementing `StorageInfoTrait`. + /// Contains the span of the attribute. + pub without_storage_info: Option, + /// The in-code storage version of the pallet. + pub storage_version: Option, } /// Parse for one variant of: /// * `#[pallet::without_storage_info]` /// * `#[pallet::storage_version(STORAGE_VERSION)]` pub enum PalletStructAttr { - WithoutStorageInfoTrait(proc_macro2::Span), - StorageVersion { storage_version: syn::Path, span: proc_macro2::Span }, + WithoutStorageInfoTrait(proc_macro2::Span), + StorageVersion { + storage_version: syn::Path, + span: proc_macro2::Span, + }, } impl PalletStructAttr { - fn span(&self) -> proc_macro2::Span { - match self { - Self::WithoutStorageInfoTrait(span) | Self::StorageVersion { span, .. } => *span, - } - } + fn span(&self) -> proc_macro2::Span { + match self { + Self::WithoutStorageInfoTrait(span) | Self::StorageVersion { span, .. } => *span, + } + } } impl syn::parse::Parse for PalletStructAttr { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - let content; - syn::bracketed!(content in input); - content.parse::()?; - content.parse::()?; - - let lookahead = content.lookahead1(); - if lookahead.peek(keyword::without_storage_info) { - let span = content.parse::()?.span(); - Ok(Self::WithoutStorageInfoTrait(span)) - } else if lookahead.peek(keyword::storage_version) { - let span = content.parse::()?.span(); - - let version_content; - syn::parenthesized!(version_content in content); - let storage_version = version_content.parse::()?; - - Ok(Self::StorageVersion { storage_version, span }) - } else { - Err(lookahead.error()) - } - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + let lookahead = content.lookahead1(); + if lookahead.peek(keyword::without_storage_info) { + let span = content.parse::()?.span(); + Ok(Self::WithoutStorageInfoTrait(span)) + } else if lookahead.peek(keyword::storage_version) { + let span = content.parse::()?.span(); + + let version_content; + syn::parenthesized!(version_content in content); + let storage_version = version_content.parse::()?; + + Ok(Self::StorageVersion { + storage_version, + span, + }) + } else { + Err(lookahead.error()) + } + } } impl PalletStructDef { - pub fn try_from( - attr_span: proc_macro2::Span, - index: usize, - item: &mut syn::Item, - ) -> syn::Result { - let item = if let syn::Item::Struct(item) = item { - item - } else { - let msg = "Invalid pallet::pallet, expected struct definition"; - return Err(syn::Error::new(item.span(), msg)) - }; - - let mut without_storage_info = None; - let mut storage_version_found = None; - - let struct_attrs: Vec = helper::take_item_pallet_attrs(&mut item.attrs)?; - for attr in struct_attrs { - match attr { - PalletStructAttr::WithoutStorageInfoTrait(span) - if without_storage_info.is_none() => - { - without_storage_info = Some(span); - }, - PalletStructAttr::StorageVersion { storage_version, .. } - if storage_version_found.is_none() => - { - storage_version_found = Some(storage_version); - }, - attr => { - let msg = "Unexpected duplicated attribute"; - return Err(syn::Error::new(attr.span(), msg)) - }, - } - } - - let pallet = syn::parse2::(item.ident.to_token_stream())?; - - if !matches!(item.vis, syn::Visibility::Public(_)) { - let msg = "Invalid pallet::pallet, Pallet must be public"; - return Err(syn::Error::new(item.span(), msg)) - } - - if item.generics.where_clause.is_some() { - let msg = "Invalid pallet::pallet, where clause not supported on Pallet declaration"; - return Err(syn::Error::new(item.generics.where_clause.span(), msg)) - } - - let instances = - vec![helper::check_type_def_gen_no_bounds(&item.generics, item.ident.span())?]; - - Ok(Self { - index, - instances, - pallet, - attr_span, - without_storage_info, - storage_version: storage_version_found, - }) - } + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + ) -> syn::Result { + let item = if let syn::Item::Struct(item) = item { + item + } else { + let msg = "Invalid pallet::pallet, expected struct definition"; + return Err(syn::Error::new(item.span(), msg)); + }; + + let mut without_storage_info = None; + let mut storage_version_found = None; + + let struct_attrs: Vec = helper::take_item_pallet_attrs(&mut item.attrs)?; + for attr in struct_attrs { + match attr { + PalletStructAttr::WithoutStorageInfoTrait(span) + if without_storage_info.is_none() => + { + without_storage_info = Some(span); + } + PalletStructAttr::StorageVersion { + storage_version, .. + } if storage_version_found.is_none() => { + storage_version_found = Some(storage_version); + } + attr => { + let msg = "Unexpected duplicated attribute"; + return Err(syn::Error::new(attr.span(), msg)); + } + } + } + + let pallet = syn::parse2::(item.ident.to_token_stream())?; + + if !matches!(item.vis, syn::Visibility::Public(_)) { + let msg = "Invalid pallet::pallet, Pallet must be public"; + return Err(syn::Error::new(item.span(), msg)); + } + + if item.generics.where_clause.is_some() { + let msg = "Invalid pallet::pallet, where clause not supported on Pallet declaration"; + return Err(syn::Error::new(item.generics.where_clause.span(), msg)); + } + + let instances = vec![helper::check_type_def_gen_no_bounds( + &item.generics, + item.ident.span(), + )?]; + + Ok(Self { + index, + instances, + pallet, + attr_span, + without_storage_info, + storage_version: storage_version_found, + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/storage.rs b/support/procedural-fork/src/pallet/parse/storage.rs index 9d96a18b5..64a5e685b 100644 --- a/support/procedural-fork/src/pallet/parse/storage.rs +++ b/support/procedural-fork/src/pallet/parse/storage.rs @@ -23,16 +23,16 @@ use syn::spanned::Spanned; /// List of additional token to be used for parsing. mod keyword { - syn::custom_keyword!(Error); - syn::custom_keyword!(pallet); - syn::custom_keyword!(getter); - syn::custom_keyword!(storage_prefix); - syn::custom_keyword!(unbounded); - syn::custom_keyword!(whitelist_storage); - syn::custom_keyword!(disable_try_decode_storage); - syn::custom_keyword!(OptionQuery); - syn::custom_keyword!(ResultQuery); - syn::custom_keyword!(ValueQuery); + syn::custom_keyword!(Error); + syn::custom_keyword!(pallet); + syn::custom_keyword!(getter); + syn::custom_keyword!(storage_prefix); + syn::custom_keyword!(unbounded); + syn::custom_keyword!(whitelist_storage); + syn::custom_keyword!(disable_try_decode_storage); + syn::custom_keyword!(OptionQuery); + syn::custom_keyword!(ResultQuery); + syn::custom_keyword!(ValueQuery); } /// Parse for one of the following: @@ -42,906 +42,1003 @@ mod keyword { /// * `#[pallet::whitelist_storage] /// * `#[pallet::disable_try_decode_storage]` pub enum PalletStorageAttr { - Getter(syn::Ident, proc_macro2::Span), - StorageName(syn::LitStr, proc_macro2::Span), - Unbounded(proc_macro2::Span), - WhitelistStorage(proc_macro2::Span), - DisableTryDecodeStorage(proc_macro2::Span), + Getter(syn::Ident, proc_macro2::Span), + StorageName(syn::LitStr, proc_macro2::Span), + Unbounded(proc_macro2::Span), + WhitelistStorage(proc_macro2::Span), + DisableTryDecodeStorage(proc_macro2::Span), } impl PalletStorageAttr { - fn attr_span(&self) -> proc_macro2::Span { - match self { - Self::Getter(_, span) | - Self::StorageName(_, span) | - Self::Unbounded(span) | - Self::WhitelistStorage(span) => *span, - Self::DisableTryDecodeStorage(span) => *span, - } - } + fn attr_span(&self) -> proc_macro2::Span { + match self { + Self::Getter(_, span) + | Self::StorageName(_, span) + | Self::Unbounded(span) + | Self::WhitelistStorage(span) => *span, + Self::DisableTryDecodeStorage(span) => *span, + } + } } impl syn::parse::Parse for PalletStorageAttr { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - let attr_span = input.span(); - let content; - syn::bracketed!(content in input); - content.parse::()?; - content.parse::()?; - - let lookahead = content.lookahead1(); - if lookahead.peek(keyword::getter) { - content.parse::()?; - - let generate_content; - syn::parenthesized!(generate_content in content); - generate_content.parse::()?; - Ok(Self::Getter(generate_content.parse::()?, attr_span)) - } else if lookahead.peek(keyword::storage_prefix) { - content.parse::()?; - content.parse::()?; - - let renamed_prefix = content.parse::()?; - // Ensure the renamed prefix is a proper Rust identifier - syn::parse_str::(&renamed_prefix.value()).map_err(|_| { - let msg = format!("`{}` is not a valid identifier", renamed_prefix.value()); - syn::Error::new(renamed_prefix.span(), msg) - })?; - - Ok(Self::StorageName(renamed_prefix, attr_span)) - } else if lookahead.peek(keyword::unbounded) { - content.parse::()?; - - Ok(Self::Unbounded(attr_span)) - } else if lookahead.peek(keyword::whitelist_storage) { - content.parse::()?; - Ok(Self::WhitelistStorage(attr_span)) - } else if lookahead.peek(keyword::disable_try_decode_storage) { - content.parse::()?; - Ok(Self::DisableTryDecodeStorage(attr_span)) - } else { - Err(lookahead.error()) - } - } + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let attr_span = input.span(); + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; + + let lookahead = content.lookahead1(); + if lookahead.peek(keyword::getter) { + content.parse::()?; + + let generate_content; + syn::parenthesized!(generate_content in content); + generate_content.parse::()?; + Ok(Self::Getter( + generate_content.parse::()?, + attr_span, + )) + } else if lookahead.peek(keyword::storage_prefix) { + content.parse::()?; + content.parse::()?; + + let renamed_prefix = content.parse::()?; + // Ensure the renamed prefix is a proper Rust identifier + syn::parse_str::(&renamed_prefix.value()).map_err(|_| { + let msg = format!("`{}` is not a valid identifier", renamed_prefix.value()); + syn::Error::new(renamed_prefix.span(), msg) + })?; + + Ok(Self::StorageName(renamed_prefix, attr_span)) + } else if lookahead.peek(keyword::unbounded) { + content.parse::()?; + + Ok(Self::Unbounded(attr_span)) + } else if lookahead.peek(keyword::whitelist_storage) { + content.parse::()?; + Ok(Self::WhitelistStorage(attr_span)) + } else if lookahead.peek(keyword::disable_try_decode_storage) { + content.parse::()?; + Ok(Self::DisableTryDecodeStorage(attr_span)) + } else { + Err(lookahead.error()) + } + } } struct PalletStorageAttrInfo { - getter: Option, - rename_as: Option, - unbounded: bool, - whitelisted: bool, - try_decode: bool, + getter: Option, + rename_as: Option, + unbounded: bool, + whitelisted: bool, + try_decode: bool, } impl PalletStorageAttrInfo { - fn from_attrs(attrs: Vec) -> syn::Result { - let mut getter = None; - let mut rename_as = None; - let mut unbounded = false; - let mut whitelisted = false; - let mut disable_try_decode_storage = false; - for attr in attrs { - match attr { - PalletStorageAttr::Getter(ident, ..) if getter.is_none() => getter = Some(ident), - PalletStorageAttr::StorageName(name, ..) if rename_as.is_none() => - rename_as = Some(name), - PalletStorageAttr::Unbounded(..) if !unbounded => unbounded = true, - PalletStorageAttr::WhitelistStorage(..) if !whitelisted => whitelisted = true, - PalletStorageAttr::DisableTryDecodeStorage(..) if !disable_try_decode_storage => - disable_try_decode_storage = true, - attr => - return Err(syn::Error::new( - attr.attr_span(), - "Invalid attribute: Duplicate attribute", - )), - } - } - - Ok(PalletStorageAttrInfo { - getter, - rename_as, - unbounded, - whitelisted, - try_decode: !disable_try_decode_storage, - }) - } + fn from_attrs(attrs: Vec) -> syn::Result { + let mut getter = None; + let mut rename_as = None; + let mut unbounded = false; + let mut whitelisted = false; + let mut disable_try_decode_storage = false; + for attr in attrs { + match attr { + PalletStorageAttr::Getter(ident, ..) if getter.is_none() => getter = Some(ident), + PalletStorageAttr::StorageName(name, ..) if rename_as.is_none() => { + rename_as = Some(name) + } + PalletStorageAttr::Unbounded(..) if !unbounded => unbounded = true, + PalletStorageAttr::WhitelistStorage(..) if !whitelisted => whitelisted = true, + PalletStorageAttr::DisableTryDecodeStorage(..) if !disable_try_decode_storage => { + disable_try_decode_storage = true + } + attr => { + return Err(syn::Error::new( + attr.attr_span(), + "Invalid attribute: Duplicate attribute", + )) + } + } + } + + Ok(PalletStorageAttrInfo { + getter, + rename_as, + unbounded, + whitelisted, + try_decode: !disable_try_decode_storage, + }) + } } /// The value and key types used by storages. Needed to expand metadata. pub enum Metadata { - Value { value: syn::Type }, - Map { value: syn::Type, key: syn::Type }, - CountedMap { value: syn::Type, key: syn::Type }, - DoubleMap { value: syn::Type, key1: syn::Type, key2: syn::Type }, - NMap { keys: Vec, keygen: syn::Type, value: syn::Type }, - CountedNMap { keys: Vec, keygen: syn::Type, value: syn::Type }, + Value { + value: syn::Type, + }, + Map { + value: syn::Type, + key: syn::Type, + }, + CountedMap { + value: syn::Type, + key: syn::Type, + }, + DoubleMap { + value: syn::Type, + key1: syn::Type, + key2: syn::Type, + }, + NMap { + keys: Vec, + keygen: syn::Type, + value: syn::Type, + }, + CountedNMap { + keys: Vec, + keygen: syn::Type, + value: syn::Type, + }, } pub enum QueryKind { - OptionQuery, - ResultQuery(syn::Path, syn::Ident), - ValueQuery, + OptionQuery, + ResultQuery(syn::Path, syn::Ident), + ValueQuery, } /// Definition of a storage, storage is a storage type like /// `type MyStorage = StorageValue` /// The keys and values types are parsed in order to get metadata pub struct StorageDef { - /// The index of storage item in pallet module. - pub index: usize, - /// Visibility of the storage type. - pub vis: syn::Visibility, - /// The type ident, to generate the StoragePrefix for. - pub ident: syn::Ident, - /// The keys and value metadata of the storage. - pub metadata: Metadata, - /// The doc associated to the storage. - pub docs: Vec, - /// A set of usage of instance, must be check for consistency with config. - pub instances: Vec, - /// Optional getter to generate. If some then query_kind is ensured to be some as well. - pub getter: Option, - /// Optional expression that evaluates to a type that can be used as StoragePrefix instead of - /// ident. - pub rename_as: Option, - /// Whereas the querytype of the storage is OptionQuery, ResultQuery or ValueQuery. - /// Note that this is best effort as it can't be determined when QueryKind is generic, and - /// result can be false if user do some unexpected type alias. - pub query_kind: Option, - /// Where clause of type definition. - pub where_clause: Option, - /// The span of the pallet::storage attribute. - pub attr_span: proc_macro2::Span, - /// The `cfg` attributes. - pub cfg_attrs: Vec, - /// If generics are named (e.g. `StorageValue`) then this contains all the - /// generics of the storage. - /// If generics are not named, this is none. - pub named_generics: Option, - /// If the value stored in this storage is unbounded. - pub unbounded: bool, - /// Whether or not reads to this storage key will be ignored by benchmarking - pub whitelisted: bool, - /// Whether or not to try to decode the storage key when running try-runtime checks. - pub try_decode: bool, - /// Whether or not a default hasher is allowed to replace `_` - pub use_default_hasher: bool, + /// The index of storage item in pallet module. + pub index: usize, + /// Visibility of the storage type. + pub vis: syn::Visibility, + /// The type ident, to generate the StoragePrefix for. + pub ident: syn::Ident, + /// The keys and value metadata of the storage. + pub metadata: Metadata, + /// The doc associated to the storage. + pub docs: Vec, + /// A set of usage of instance, must be check for consistency with config. + pub instances: Vec, + /// Optional getter to generate. If some then query_kind is ensured to be some as well. + pub getter: Option, + /// Optional expression that evaluates to a type that can be used as StoragePrefix instead of + /// ident. + pub rename_as: Option, + /// Whereas the querytype of the storage is OptionQuery, ResultQuery or ValueQuery. + /// Note that this is best effort as it can't be determined when QueryKind is generic, and + /// result can be false if user do some unexpected type alias. + pub query_kind: Option, + /// Where clause of type definition. + pub where_clause: Option, + /// The span of the pallet::storage attribute. + pub attr_span: proc_macro2::Span, + /// The `cfg` attributes. + pub cfg_attrs: Vec, + /// If generics are named (e.g. `StorageValue`) then this contains all the + /// generics of the storage. + /// If generics are not named, this is none. + pub named_generics: Option, + /// If the value stored in this storage is unbounded. + pub unbounded: bool, + /// Whether or not reads to this storage key will be ignored by benchmarking + pub whitelisted: bool, + /// Whether or not to try to decode the storage key when running try-runtime checks. + pub try_decode: bool, + /// Whether or not a default hasher is allowed to replace `_` + pub use_default_hasher: bool, } /// The parsed generic from the #[derive(Clone)] pub enum StorageGenerics { - DoubleMap { - hasher1: syn::Type, - key1: syn::Type, - hasher2: syn::Type, - key2: syn::Type, - value: syn::Type, - query_kind: Option, - on_empty: Option, - max_values: Option, - }, - Map { - hasher: syn::Type, - key: syn::Type, - value: syn::Type, - query_kind: Option, - on_empty: Option, - max_values: Option, - }, - CountedMap { - hasher: syn::Type, - key: syn::Type, - value: syn::Type, - query_kind: Option, - on_empty: Option, - max_values: Option, - }, - Value { - value: syn::Type, - query_kind: Option, - on_empty: Option, - }, - NMap { - keygen: syn::Type, - value: syn::Type, - query_kind: Option, - on_empty: Option, - max_values: Option, - }, - CountedNMap { - keygen: syn::Type, - value: syn::Type, - query_kind: Option, - on_empty: Option, - max_values: Option, - }, + DoubleMap { + hasher1: syn::Type, + key1: syn::Type, + hasher2: syn::Type, + key2: syn::Type, + value: syn::Type, + query_kind: Option, + on_empty: Option, + max_values: Option, + }, + Map { + hasher: syn::Type, + key: syn::Type, + value: syn::Type, + query_kind: Option, + on_empty: Option, + max_values: Option, + }, + CountedMap { + hasher: syn::Type, + key: syn::Type, + value: syn::Type, + query_kind: Option, + on_empty: Option, + max_values: Option, + }, + Value { + value: syn::Type, + query_kind: Option, + on_empty: Option, + }, + NMap { + keygen: syn::Type, + value: syn::Type, + query_kind: Option, + on_empty: Option, + max_values: Option, + }, + CountedNMap { + keygen: syn::Type, + value: syn::Type, + query_kind: Option, + on_empty: Option, + max_values: Option, + }, } impl StorageGenerics { - /// Return the metadata from the defined generics - fn metadata(&self) -> syn::Result { - let res = match self.clone() { - Self::DoubleMap { value, key1, key2, .. } => Metadata::DoubleMap { value, key1, key2 }, - Self::Map { value, key, .. } => Metadata::Map { value, key }, - Self::CountedMap { value, key, .. } => Metadata::CountedMap { value, key }, - Self::Value { value, .. } => Metadata::Value { value }, - Self::NMap { keygen, value, .. } => - Metadata::NMap { keys: collect_keys(&keygen)?, keygen, value }, - Self::CountedNMap { keygen, value, .. } => - Metadata::CountedNMap { keys: collect_keys(&keygen)?, keygen, value }, - }; - - Ok(res) - } - - /// Return the query kind from the defined generics - fn query_kind(&self) -> Option { - match &self { - Self::DoubleMap { query_kind, .. } | - Self::Map { query_kind, .. } | - Self::CountedMap { query_kind, .. } | - Self::Value { query_kind, .. } | - Self::NMap { query_kind, .. } | - Self::CountedNMap { query_kind, .. } => query_kind.clone(), - } - } + /// Return the metadata from the defined generics + fn metadata(&self) -> syn::Result { + let res = match self.clone() { + Self::DoubleMap { + value, key1, key2, .. + } => Metadata::DoubleMap { value, key1, key2 }, + Self::Map { value, key, .. } => Metadata::Map { value, key }, + Self::CountedMap { value, key, .. } => Metadata::CountedMap { value, key }, + Self::Value { value, .. } => Metadata::Value { value }, + Self::NMap { keygen, value, .. } => Metadata::NMap { + keys: collect_keys(&keygen)?, + keygen, + value, + }, + Self::CountedNMap { keygen, value, .. } => Metadata::CountedNMap { + keys: collect_keys(&keygen)?, + keygen, + value, + }, + }; + + Ok(res) + } + + /// Return the query kind from the defined generics + fn query_kind(&self) -> Option { + match &self { + Self::DoubleMap { query_kind, .. } + | Self::Map { query_kind, .. } + | Self::CountedMap { query_kind, .. } + | Self::Value { query_kind, .. } + | Self::NMap { query_kind, .. } + | Self::CountedNMap { query_kind, .. } => query_kind.clone(), + } + } } enum StorageKind { - Value, - Map, - CountedMap, - DoubleMap, - NMap, - CountedNMap, + Value, + Map, + CountedMap, + DoubleMap, + NMap, + CountedNMap, } /// Check the generics in the `map` contains the generics in `gen` may contains generics in /// `optional_gen`, and doesn't contains any other. fn check_generics( - map: &HashMap, - mandatory_generics: &[&str], - optional_generics: &[&str], - storage_type_name: &str, - args_span: proc_macro2::Span, + map: &HashMap, + mandatory_generics: &[&str], + optional_generics: &[&str], + storage_type_name: &str, + args_span: proc_macro2::Span, ) -> syn::Result<()> { - let mut errors = vec![]; - - let expectation = { - let mut e = format!( - "`{}` expect generics {}and optional generics {}", - storage_type_name, - mandatory_generics - .iter() - .map(|name| format!("`{}`, ", name)) - .collect::(), - &optional_generics.iter().map(|name| format!("`{}`, ", name)).collect::(), - ); - e.pop(); - e.pop(); - e.push('.'); - e - }; - - for (gen_name, gen_binding) in map { - if !mandatory_generics.contains(&gen_name.as_str()) && - !optional_generics.contains(&gen_name.as_str()) - { - let msg = format!( - "Invalid pallet::storage, Unexpected generic `{}` for `{}`. {}", - gen_name, storage_type_name, expectation, - ); - errors.push(syn::Error::new(gen_binding.span(), msg)); - } - } - - for mandatory_generic in mandatory_generics { - if !map.contains_key(&mandatory_generic.to_string()) { - let msg = format!( - "Invalid pallet::storage, cannot find `{}` generic, required for `{}`.", - mandatory_generic, storage_type_name - ); - errors.push(syn::Error::new(args_span, msg)); - } - } - - let mut errors = errors.drain(..); - if let Some(mut error) = errors.next() { - for other_error in errors { - error.combine(other_error); - } - Err(error) - } else { - Ok(()) - } + let mut errors = vec![]; + + let expectation = { + let mut e = format!( + "`{}` expect generics {}and optional generics {}", + storage_type_name, + mandatory_generics + .iter() + .map(|name| format!("`{}`, ", name)) + .collect::(), + &optional_generics + .iter() + .map(|name| format!("`{}`, ", name)) + .collect::(), + ); + e.pop(); + e.pop(); + e.push('.'); + e + }; + + for (gen_name, gen_binding) in map { + if !mandatory_generics.contains(&gen_name.as_str()) + && !optional_generics.contains(&gen_name.as_str()) + { + let msg = format!( + "Invalid pallet::storage, Unexpected generic `{}` for `{}`. {}", + gen_name, storage_type_name, expectation, + ); + errors.push(syn::Error::new(gen_binding.span(), msg)); + } + } + + for mandatory_generic in mandatory_generics { + if !map.contains_key(&mandatory_generic.to_string()) { + let msg = format!( + "Invalid pallet::storage, cannot find `{}` generic, required for `{}`.", + mandatory_generic, storage_type_name + ); + errors.push(syn::Error::new(args_span, msg)); + } + } + + let mut errors = errors.drain(..); + if let Some(mut error) = errors.next() { + for other_error in errors { + error.combine(other_error); + } + Err(error) + } else { + Ok(()) + } } /// Returns `(named generics, metadata, query kind, use_default_hasher)` fn process_named_generics( - storage: &StorageKind, - args_span: proc_macro2::Span, - args: &[syn::AssocType], - dev_mode: bool, + storage: &StorageKind, + args_span: proc_macro2::Span, + args: &[syn::AssocType], + dev_mode: bool, ) -> syn::Result<(Option, Metadata, Option, bool)> { - let mut parsed = HashMap::::new(); - - // Ensure no duplicate. - for arg in args { - if let Some(other) = parsed.get(&arg.ident.to_string()) { - let msg = "Invalid pallet::storage, Duplicated named generic"; - let mut err = syn::Error::new(arg.ident.span(), msg); - err.combine(syn::Error::new(other.ident.span(), msg)); - return Err(err) - } - parsed.insert(arg.ident.to_string(), arg.clone()); - } - - let mut map_mandatory_generics = vec!["Key", "Value"]; - let mut map_optional_generics = vec!["QueryKind", "OnEmpty", "MaxValues"]; - if dev_mode { - map_optional_generics.push("Hasher"); - } else { - map_mandatory_generics.push("Hasher"); - } - - let generics = match storage { - StorageKind::Value => { - check_generics( - &parsed, - &["Value"], - &["QueryKind", "OnEmpty"], - "StorageValue", - args_span, - )?; - - StorageGenerics::Value { - value: parsed - .remove("Value") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), - on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), - } - }, - StorageKind::Map => { - check_generics( - &parsed, - &map_mandatory_generics, - &map_optional_generics, - "StorageMap", - args_span, - )?; - - StorageGenerics::Map { - hasher: parsed - .remove("Hasher") - .map(|binding| binding.ty) - .unwrap_or(syn::parse_quote!(Blake2_128Concat)), - key: parsed - .remove("Key") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - value: parsed - .remove("Value") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), - on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), - max_values: parsed.remove("MaxValues").map(|binding| binding.ty), - } - }, - StorageKind::CountedMap => { - check_generics( - &parsed, - &map_mandatory_generics, - &map_optional_generics, - "CountedStorageMap", - args_span, - )?; - - StorageGenerics::CountedMap { - hasher: parsed - .remove("Hasher") - .map(|binding| binding.ty) - .unwrap_or(syn::Type::Verbatim(quote::quote! { Blake2_128Concat })), - key: parsed - .remove("Key") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - value: parsed - .remove("Value") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), - on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), - max_values: parsed.remove("MaxValues").map(|binding| binding.ty), - } - }, - StorageKind::DoubleMap => { - let mut double_map_mandatory_generics = vec!["Key1", "Key2", "Value"]; - if dev_mode { - map_optional_generics.extend(["Hasher1", "Hasher2"]); - } else { - double_map_mandatory_generics.extend(["Hasher1", "Hasher2"]); - } - - check_generics( - &parsed, - &double_map_mandatory_generics, - &map_optional_generics, - "StorageDoubleMap", - args_span, - )?; - - StorageGenerics::DoubleMap { - hasher1: parsed - .remove("Hasher1") - .map(|binding| binding.ty) - .unwrap_or(syn::parse_quote!(Blake2_128Concat)), - key1: parsed - .remove("Key1") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - hasher2: parsed - .remove("Hasher2") - .map(|binding| binding.ty) - .unwrap_or(syn::parse_quote!(Blake2_128Concat)), - key2: parsed - .remove("Key2") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - value: parsed - .remove("Value") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), - on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), - max_values: parsed.remove("MaxValues").map(|binding| binding.ty), - } - }, - StorageKind::NMap => { - check_generics( - &parsed, - &["Key", "Value"], - &["QueryKind", "OnEmpty", "MaxValues"], - "StorageNMap", - args_span, - )?; - - StorageGenerics::NMap { - keygen: parsed - .remove("Key") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - value: parsed - .remove("Value") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), - on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), - max_values: parsed.remove("MaxValues").map(|binding| binding.ty), - } - }, - StorageKind::CountedNMap => { - check_generics( - &parsed, - &["Key", "Value"], - &["QueryKind", "OnEmpty", "MaxValues"], - "CountedStorageNMap", - args_span, - )?; - - StorageGenerics::CountedNMap { - keygen: parsed - .remove("Key") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - value: parsed - .remove("Value") - .map(|binding| binding.ty) - .expect("checked above as mandatory generic"), - query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), - on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), - max_values: parsed.remove("MaxValues").map(|binding| binding.ty), - } - }, - }; - - let metadata = generics.metadata()?; - let query_kind = generics.query_kind(); - - Ok((Some(generics), metadata, query_kind, false)) + let mut parsed = HashMap::::new(); + + // Ensure no duplicate. + for arg in args { + if let Some(other) = parsed.get(&arg.ident.to_string()) { + let msg = "Invalid pallet::storage, Duplicated named generic"; + let mut err = syn::Error::new(arg.ident.span(), msg); + err.combine(syn::Error::new(other.ident.span(), msg)); + return Err(err); + } + parsed.insert(arg.ident.to_string(), arg.clone()); + } + + let mut map_mandatory_generics = vec!["Key", "Value"]; + let mut map_optional_generics = vec!["QueryKind", "OnEmpty", "MaxValues"]; + if dev_mode { + map_optional_generics.push("Hasher"); + } else { + map_mandatory_generics.push("Hasher"); + } + + let generics = match storage { + StorageKind::Value => { + check_generics( + &parsed, + &["Value"], + &["QueryKind", "OnEmpty"], + "StorageValue", + args_span, + )?; + + StorageGenerics::Value { + value: parsed + .remove("Value") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), + on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), + } + } + StorageKind::Map => { + check_generics( + &parsed, + &map_mandatory_generics, + &map_optional_generics, + "StorageMap", + args_span, + )?; + + StorageGenerics::Map { + hasher: parsed + .remove("Hasher") + .map(|binding| binding.ty) + .unwrap_or(syn::parse_quote!(Blake2_128Concat)), + key: parsed + .remove("Key") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + value: parsed + .remove("Value") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), + on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), + max_values: parsed.remove("MaxValues").map(|binding| binding.ty), + } + } + StorageKind::CountedMap => { + check_generics( + &parsed, + &map_mandatory_generics, + &map_optional_generics, + "CountedStorageMap", + args_span, + )?; + + StorageGenerics::CountedMap { + hasher: parsed + .remove("Hasher") + .map(|binding| binding.ty) + .unwrap_or(syn::Type::Verbatim(quote::quote! { Blake2_128Concat })), + key: parsed + .remove("Key") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + value: parsed + .remove("Value") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), + on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), + max_values: parsed.remove("MaxValues").map(|binding| binding.ty), + } + } + StorageKind::DoubleMap => { + let mut double_map_mandatory_generics = vec!["Key1", "Key2", "Value"]; + if dev_mode { + map_optional_generics.extend(["Hasher1", "Hasher2"]); + } else { + double_map_mandatory_generics.extend(["Hasher1", "Hasher2"]); + } + + check_generics( + &parsed, + &double_map_mandatory_generics, + &map_optional_generics, + "StorageDoubleMap", + args_span, + )?; + + StorageGenerics::DoubleMap { + hasher1: parsed + .remove("Hasher1") + .map(|binding| binding.ty) + .unwrap_or(syn::parse_quote!(Blake2_128Concat)), + key1: parsed + .remove("Key1") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + hasher2: parsed + .remove("Hasher2") + .map(|binding| binding.ty) + .unwrap_or(syn::parse_quote!(Blake2_128Concat)), + key2: parsed + .remove("Key2") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + value: parsed + .remove("Value") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), + on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), + max_values: parsed.remove("MaxValues").map(|binding| binding.ty), + } + } + StorageKind::NMap => { + check_generics( + &parsed, + &["Key", "Value"], + &["QueryKind", "OnEmpty", "MaxValues"], + "StorageNMap", + args_span, + )?; + + StorageGenerics::NMap { + keygen: parsed + .remove("Key") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + value: parsed + .remove("Value") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), + on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), + max_values: parsed.remove("MaxValues").map(|binding| binding.ty), + } + } + StorageKind::CountedNMap => { + check_generics( + &parsed, + &["Key", "Value"], + &["QueryKind", "OnEmpty", "MaxValues"], + "CountedStorageNMap", + args_span, + )?; + + StorageGenerics::CountedNMap { + keygen: parsed + .remove("Key") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + value: parsed + .remove("Value") + .map(|binding| binding.ty) + .expect("checked above as mandatory generic"), + query_kind: parsed.remove("QueryKind").map(|binding| binding.ty), + on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty), + max_values: parsed.remove("MaxValues").map(|binding| binding.ty), + } + } + }; + + let metadata = generics.metadata()?; + let query_kind = generics.query_kind(); + + Ok((Some(generics), metadata, query_kind, false)) } /// Returns `(named generics, metadata, query kind, use_default_hasher)` fn process_unnamed_generics( - storage: &StorageKind, - args_span: proc_macro2::Span, - args: &[syn::Type], - dev_mode: bool, + storage: &StorageKind, + args_span: proc_macro2::Span, + args: &[syn::Type], + dev_mode: bool, ) -> syn::Result<(Option, Metadata, Option, bool)> { - let retrieve_arg = |arg_pos| { - args.get(arg_pos).cloned().ok_or_else(|| { - let msg = format!( - "Invalid pallet::storage, unexpected number of generic argument, \ + let retrieve_arg = |arg_pos| { + args.get(arg_pos).cloned().ok_or_else(|| { + let msg = format!( + "Invalid pallet::storage, unexpected number of generic argument, \ expect at least {} args, found {}.", - arg_pos + 1, - args.len(), - ); - syn::Error::new(args_span, msg) - }) - }; - - let prefix_arg = retrieve_arg(0)?; - syn::parse2::(prefix_arg.to_token_stream()).map_err(|e| { - let msg = "Invalid pallet::storage, for unnamed generic arguments the type \ + arg_pos + 1, + args.len(), + ); + syn::Error::new(args_span, msg) + }) + }; + + let prefix_arg = retrieve_arg(0)?; + syn::parse2::(prefix_arg.to_token_stream()).map_err(|e| { + let msg = "Invalid pallet::storage, for unnamed generic arguments the type \ first generic argument must be `_`, the argument is then replaced by macro."; - let mut err = syn::Error::new(prefix_arg.span(), msg); - err.combine(e); - err - })?; - - let use_default_hasher = |arg_pos| { - let arg = retrieve_arg(arg_pos)?; - if syn::parse2::(arg.to_token_stream()).is_ok() { - if dev_mode { - Ok(true) - } else { - let msg = "`_` can only be used in dev_mode. Please specify an appropriate hasher."; - Err(syn::Error::new(arg.span(), msg)) - } - } else { - Ok(false) - } - }; - - let res = match storage { - StorageKind::Value => - (None, Metadata::Value { value: retrieve_arg(1)? }, retrieve_arg(2).ok(), false), - StorageKind::Map => ( - None, - Metadata::Map { key: retrieve_arg(2)?, value: retrieve_arg(3)? }, - retrieve_arg(4).ok(), - use_default_hasher(1)?, - ), - StorageKind::CountedMap => ( - None, - Metadata::CountedMap { key: retrieve_arg(2)?, value: retrieve_arg(3)? }, - retrieve_arg(4).ok(), - use_default_hasher(1)?, - ), - StorageKind::DoubleMap => ( - None, - Metadata::DoubleMap { - key1: retrieve_arg(2)?, - key2: retrieve_arg(4)?, - value: retrieve_arg(5)?, - }, - retrieve_arg(6).ok(), - use_default_hasher(1)? && use_default_hasher(3)?, - ), - StorageKind::NMap => { - let keygen = retrieve_arg(1)?; - let keys = collect_keys(&keygen)?; - ( - None, - Metadata::NMap { keys, keygen, value: retrieve_arg(2)? }, - retrieve_arg(3).ok(), - false, - ) - }, - StorageKind::CountedNMap => { - let keygen = retrieve_arg(1)?; - let keys = collect_keys(&keygen)?; - ( - None, - Metadata::CountedNMap { keys, keygen, value: retrieve_arg(2)? }, - retrieve_arg(3).ok(), - false, - ) - }, - }; - - Ok(res) + let mut err = syn::Error::new(prefix_arg.span(), msg); + err.combine(e); + err + })?; + + let use_default_hasher = |arg_pos| { + let arg = retrieve_arg(arg_pos)?; + if syn::parse2::(arg.to_token_stream()).is_ok() { + if dev_mode { + Ok(true) + } else { + let msg = "`_` can only be used in dev_mode. Please specify an appropriate hasher."; + Err(syn::Error::new(arg.span(), msg)) + } + } else { + Ok(false) + } + }; + + let res = match storage { + StorageKind::Value => ( + None, + Metadata::Value { + value: retrieve_arg(1)?, + }, + retrieve_arg(2).ok(), + false, + ), + StorageKind::Map => ( + None, + Metadata::Map { + key: retrieve_arg(2)?, + value: retrieve_arg(3)?, + }, + retrieve_arg(4).ok(), + use_default_hasher(1)?, + ), + StorageKind::CountedMap => ( + None, + Metadata::CountedMap { + key: retrieve_arg(2)?, + value: retrieve_arg(3)?, + }, + retrieve_arg(4).ok(), + use_default_hasher(1)?, + ), + StorageKind::DoubleMap => ( + None, + Metadata::DoubleMap { + key1: retrieve_arg(2)?, + key2: retrieve_arg(4)?, + value: retrieve_arg(5)?, + }, + retrieve_arg(6).ok(), + use_default_hasher(1)? && use_default_hasher(3)?, + ), + StorageKind::NMap => { + let keygen = retrieve_arg(1)?; + let keys = collect_keys(&keygen)?; + ( + None, + Metadata::NMap { + keys, + keygen, + value: retrieve_arg(2)?, + }, + retrieve_arg(3).ok(), + false, + ) + } + StorageKind::CountedNMap => { + let keygen = retrieve_arg(1)?; + let keys = collect_keys(&keygen)?; + ( + None, + Metadata::CountedNMap { + keys, + keygen, + value: retrieve_arg(2)?, + }, + retrieve_arg(3).ok(), + false, + ) + } + }; + + Ok(res) } /// Returns `(named generics, metadata, query kind, use_default_hasher)` fn process_generics( - segment: &syn::PathSegment, - dev_mode: bool, + segment: &syn::PathSegment, + dev_mode: bool, ) -> syn::Result<(Option, Metadata, Option, bool)> { - let storage_kind = match &*segment.ident.to_string() { - "StorageValue" => StorageKind::Value, - "StorageMap" => StorageKind::Map, - "CountedStorageMap" => StorageKind::CountedMap, - "StorageDoubleMap" => StorageKind::DoubleMap, - "StorageNMap" => StorageKind::NMap, - "CountedStorageNMap" => StorageKind::CountedNMap, - found => { - let msg = format!( - "Invalid pallet::storage, expected ident: `StorageValue` or \ + let storage_kind = match &*segment.ident.to_string() { + "StorageValue" => StorageKind::Value, + "StorageMap" => StorageKind::Map, + "CountedStorageMap" => StorageKind::CountedMap, + "StorageDoubleMap" => StorageKind::DoubleMap, + "StorageNMap" => StorageKind::NMap, + "CountedStorageNMap" => StorageKind::CountedNMap, + found => { + let msg = format!( + "Invalid pallet::storage, expected ident: `StorageValue` or \ `StorageMap` or `CountedStorageMap` or `StorageDoubleMap` or `StorageNMap` or `CountedStorageNMap` \ in order to expand metadata, found `{}`.", - found, - ); - return Err(syn::Error::new(segment.ident.span(), msg)) - }, - }; - - let args_span = segment.arguments.span(); - - let args = match &segment.arguments { - syn::PathArguments::AngleBracketed(args) if !args.args.is_empty() => args, - _ => { - let msg = "Invalid pallet::storage, invalid number of generic generic arguments, \ + found, + ); + return Err(syn::Error::new(segment.ident.span(), msg)); + } + }; + + let args_span = segment.arguments.span(); + + let args = match &segment.arguments { + syn::PathArguments::AngleBracketed(args) if !args.args.is_empty() => args, + _ => { + let msg = "Invalid pallet::storage, invalid number of generic generic arguments, \ expect more that 0 generic arguments."; - return Err(syn::Error::new(segment.span(), msg)) - }, - }; - - if args.args.iter().all(|gen| matches!(gen, syn::GenericArgument::Type(_))) { - let args = args - .args - .iter() - .map(|gen| match gen { - syn::GenericArgument::Type(gen) => gen.clone(), - _ => unreachable!("It is asserted above that all generics are types"), - }) - .collect::>(); - process_unnamed_generics(&storage_kind, args_span, &args, dev_mode) - } else if args.args.iter().all(|gen| matches!(gen, syn::GenericArgument::AssocType(_))) { - let args = args - .args - .iter() - .map(|gen| match gen { - syn::GenericArgument::AssocType(gen) => gen.clone(), - _ => unreachable!("It is asserted above that all generics are bindings"), - }) - .collect::>(); - process_named_generics(&storage_kind, args_span, &args, dev_mode) - } else { - let msg = "Invalid pallet::storage, invalid generic declaration for storage. Expect only \ + return Err(syn::Error::new(segment.span(), msg)); + } + }; + + if args + .args + .iter() + .all(|gen| matches!(gen, syn::GenericArgument::Type(_))) + { + let args = args + .args + .iter() + .map(|gen| match gen { + syn::GenericArgument::Type(gen) => gen.clone(), + _ => unreachable!("It is asserted above that all generics are types"), + }) + .collect::>(); + process_unnamed_generics(&storage_kind, args_span, &args, dev_mode) + } else if args + .args + .iter() + .all(|gen| matches!(gen, syn::GenericArgument::AssocType(_))) + { + let args = args + .args + .iter() + .map(|gen| match gen { + syn::GenericArgument::AssocType(gen) => gen.clone(), + _ => unreachable!("It is asserted above that all generics are bindings"), + }) + .collect::>(); + process_named_generics(&storage_kind, args_span, &args, dev_mode) + } else { + let msg = "Invalid pallet::storage, invalid generic declaration for storage. Expect only \ type generics or binding generics, e.g. `` or \ ``."; - Err(syn::Error::new(segment.span(), msg)) - } + Err(syn::Error::new(segment.span(), msg)) + } } /// Parse the 2nd type argument to `StorageNMap` and return its keys. fn collect_keys(keygen: &syn::Type) -> syn::Result> { - if let syn::Type::Tuple(tup) = keygen { - tup.elems.iter().map(extract_key).collect::>>() - } else { - Ok(vec![extract_key(keygen)?]) - } + if let syn::Type::Tuple(tup) = keygen { + tup.elems + .iter() + .map(extract_key) + .collect::>>() + } else { + Ok(vec![extract_key(keygen)?]) + } } /// In `Key`, extract K and return it. fn extract_key(ty: &syn::Type) -> syn::Result { - let typ = if let syn::Type::Path(typ) = ty { - typ - } else { - let msg = "Invalid pallet::storage, expected type path"; - return Err(syn::Error::new(ty.span(), msg)) - }; - - let key_struct = typ.path.segments.last().ok_or_else(|| { - let msg = "Invalid pallet::storage, expected type path with at least one segment"; - syn::Error::new(typ.path.span(), msg) - })?; - if key_struct.ident != "Key" && key_struct.ident != "NMapKey" { - let msg = "Invalid pallet::storage, expected Key or NMapKey struct"; - return Err(syn::Error::new(key_struct.ident.span(), msg)) - } - - let ty_params = if let syn::PathArguments::AngleBracketed(args) = &key_struct.arguments { - args - } else { - let msg = "Invalid pallet::storage, expected angle bracketed arguments"; - return Err(syn::Error::new(key_struct.arguments.span(), msg)) - }; - - if ty_params.args.len() != 2 { - let msg = format!( - "Invalid pallet::storage, unexpected number of generic arguments \ + let typ = if let syn::Type::Path(typ) = ty { + typ + } else { + let msg = "Invalid pallet::storage, expected type path"; + return Err(syn::Error::new(ty.span(), msg)); + }; + + let key_struct = typ.path.segments.last().ok_or_else(|| { + let msg = "Invalid pallet::storage, expected type path with at least one segment"; + syn::Error::new(typ.path.span(), msg) + })?; + if key_struct.ident != "Key" && key_struct.ident != "NMapKey" { + let msg = "Invalid pallet::storage, expected Key or NMapKey struct"; + return Err(syn::Error::new(key_struct.ident.span(), msg)); + } + + let ty_params = if let syn::PathArguments::AngleBracketed(args) = &key_struct.arguments { + args + } else { + let msg = "Invalid pallet::storage, expected angle bracketed arguments"; + return Err(syn::Error::new(key_struct.arguments.span(), msg)); + }; + + if ty_params.args.len() != 2 { + let msg = format!( + "Invalid pallet::storage, unexpected number of generic arguments \ for Key struct, expected 2 args, found {}", - ty_params.args.len() - ); - return Err(syn::Error::new(ty_params.span(), msg)) - } - - let key = match &ty_params.args[1] { - syn::GenericArgument::Type(key_ty) => key_ty.clone(), - _ => { - let msg = "Invalid pallet::storage, expected type"; - return Err(syn::Error::new(ty_params.args[1].span(), msg)) - }, - }; - - Ok(key) + ty_params.args.len() + ); + return Err(syn::Error::new(ty_params.span(), msg)); + } + + let key = match &ty_params.args[1] { + syn::GenericArgument::Type(key_ty) => key_ty.clone(), + _ => { + let msg = "Invalid pallet::storage, expected type"; + return Err(syn::Error::new(ty_params.args[1].span(), msg)); + } + }; + + Ok(key) } impl StorageDef { - /// Return the storage prefix for this storage item - pub fn prefix(&self) -> String { - self.rename_as - .as_ref() - .map(syn::LitStr::value) - .unwrap_or_else(|| self.ident.to_string()) - } - - /// Return either the span of the ident or the span of the literal in the - /// #[storage_prefix] attribute - pub fn prefix_span(&self) -> proc_macro2::Span { - self.rename_as - .as_ref() - .map(syn::LitStr::span) - .unwrap_or_else(|| self.ident.span()) - } - - pub fn try_from( - attr_span: proc_macro2::Span, - index: usize, - item: &mut syn::Item, - dev_mode: bool, - ) -> syn::Result { - let item = if let syn::Item::Type(item) = item { - item - } else { - return Err(syn::Error::new(item.span(), "Invalid pallet::storage, expect item type.")) - }; - - let attrs: Vec = helper::take_item_pallet_attrs(&mut item.attrs)?; - let PalletStorageAttrInfo { getter, rename_as, mut unbounded, whitelisted, try_decode } = - PalletStorageAttrInfo::from_attrs(attrs)?; - - // set all storages to be unbounded if dev_mode is enabled - unbounded |= dev_mode; - let cfg_attrs = helper::get_item_cfg_attrs(&item.attrs); - - let instances = vec![helper::check_type_def_gen(&item.generics, item.ident.span())?]; - - let where_clause = item.generics.where_clause.clone(); - let docs = get_doc_literals(&item.attrs); - - let typ = if let syn::Type::Path(typ) = &*item.ty { - typ - } else { - let msg = "Invalid pallet::storage, expected type path"; - return Err(syn::Error::new(item.ty.span(), msg)) - }; - - if typ.path.segments.len() != 1 { - let msg = "Invalid pallet::storage, expected type path with one segment"; - return Err(syn::Error::new(item.ty.span(), msg)) - } - - let (named_generics, metadata, query_kind, use_default_hasher) = - process_generics(&typ.path.segments[0], dev_mode)?; - - let query_kind = query_kind - .map(|query_kind| { - use syn::{ - AngleBracketedGenericArguments, GenericArgument, Path, PathArguments, Type, - TypePath, - }; - - let result_query = match query_kind { - Type::Path(path) - if path - .path - .segments - .last() - .map_or(false, |s| s.ident == "OptionQuery") => - return Ok(Some(QueryKind::OptionQuery)), - Type::Path(TypePath { path: Path { segments, .. }, .. }) - if segments.last().map_or(false, |s| s.ident == "ResultQuery") => - segments - .last() - .expect("segments is checked to have the last value; qed") - .clone(), - Type::Path(path) - if path.path.segments.last().map_or(false, |s| s.ident == "ValueQuery") => - return Ok(Some(QueryKind::ValueQuery)), - _ => return Ok(None), - }; - - let error_type = match result_query.arguments { - PathArguments::AngleBracketed(AngleBracketedGenericArguments { - args, .. - }) => { - if args.len() != 1 { - let msg = format!( - "Invalid pallet::storage, unexpected number of generic arguments \ + /// Return the storage prefix for this storage item + pub fn prefix(&self) -> String { + self.rename_as + .as_ref() + .map(syn::LitStr::value) + .unwrap_or_else(|| self.ident.to_string()) + } + + /// Return either the span of the ident or the span of the literal in the + /// #[storage_prefix] attribute + pub fn prefix_span(&self) -> proc_macro2::Span { + self.rename_as + .as_ref() + .map(syn::LitStr::span) + .unwrap_or_else(|| self.ident.span()) + } + + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + dev_mode: bool, + ) -> syn::Result { + let item = if let syn::Item::Type(item) = item { + item + } else { + return Err(syn::Error::new( + item.span(), + "Invalid pallet::storage, expect item type.", + )); + }; + + let attrs: Vec = helper::take_item_pallet_attrs(&mut item.attrs)?; + let PalletStorageAttrInfo { + getter, + rename_as, + mut unbounded, + whitelisted, + try_decode, + } = PalletStorageAttrInfo::from_attrs(attrs)?; + + // set all storages to be unbounded if dev_mode is enabled + unbounded |= dev_mode; + let cfg_attrs = helper::get_item_cfg_attrs(&item.attrs); + + let instances = vec![helper::check_type_def_gen( + &item.generics, + item.ident.span(), + )?]; + + let where_clause = item.generics.where_clause.clone(); + let docs = get_doc_literals(&item.attrs); + + let typ = if let syn::Type::Path(typ) = &*item.ty { + typ + } else { + let msg = "Invalid pallet::storage, expected type path"; + return Err(syn::Error::new(item.ty.span(), msg)); + }; + + if typ.path.segments.len() != 1 { + let msg = "Invalid pallet::storage, expected type path with one segment"; + return Err(syn::Error::new(item.ty.span(), msg)); + } + + let (named_generics, metadata, query_kind, use_default_hasher) = + process_generics(&typ.path.segments[0], dev_mode)?; + + let query_kind = query_kind + .map(|query_kind| { + use syn::{ + AngleBracketedGenericArguments, GenericArgument, Path, PathArguments, Type, + TypePath, + }; + + let result_query = match query_kind { + Type::Path(path) + if path + .path + .segments + .last() + .map_or(false, |s| s.ident == "OptionQuery") => + { + return Ok(Some(QueryKind::OptionQuery)) + } + Type::Path(TypePath { + path: Path { segments, .. }, + .. + }) if segments.last().map_or(false, |s| s.ident == "ResultQuery") => segments + .last() + .expect("segments is checked to have the last value; qed") + .clone(), + Type::Path(path) + if path + .path + .segments + .last() + .map_or(false, |s| s.ident == "ValueQuery") => + { + return Ok(Some(QueryKind::ValueQuery)) + } + _ => return Ok(None), + }; + + let error_type = match result_query.arguments { + PathArguments::AngleBracketed(AngleBracketedGenericArguments { + args, .. + }) => { + if args.len() != 1 { + let msg = format!( + "Invalid pallet::storage, unexpected number of generic arguments \ for ResultQuery, expected 1 type argument, found {}", - args.len(), - ); - return Err(syn::Error::new(args.span(), msg)) - } - - args[0].clone() - }, - args => { - let msg = format!( - "Invalid pallet::storage, unexpected generic args for ResultQuery, \ + args.len(), + ); + return Err(syn::Error::new(args.span(), msg)); + } + + args[0].clone() + } + args => { + let msg = format!( + "Invalid pallet::storage, unexpected generic args for ResultQuery, \ expected angle-bracketed arguments, found `{}`", - args.to_token_stream().to_string() - ); - return Err(syn::Error::new(args.span(), msg)) - }, - }; - - match error_type { - GenericArgument::Type(Type::Path(TypePath { - path: Path { segments: err_variant, leading_colon }, - .. - })) => { - if err_variant.len() < 2 { - let msg = format!( - "Invalid pallet::storage, unexpected number of path segments for \ + args.to_token_stream().to_string() + ); + return Err(syn::Error::new(args.span(), msg)); + } + }; + + match error_type { + GenericArgument::Type(Type::Path(TypePath { + path: + Path { + segments: err_variant, + leading_colon, + }, + .. + })) => { + if err_variant.len() < 2 { + let msg = format!( + "Invalid pallet::storage, unexpected number of path segments for \ the generics in ResultQuery, expected a path with at least 2 \ segments, found {}", - err_variant.len(), - ); - return Err(syn::Error::new(err_variant.span(), msg)) - } - let mut error = err_variant.clone(); - let err_variant = error - .pop() - .expect("Checked to have at least 2; qed") - .into_value() - .ident; - - // Necessary here to eliminate the last double colon - let last = - error.pop().expect("Checked to have at least 2; qed").into_value(); - error.push_value(last); - - Ok(Some(QueryKind::ResultQuery( - syn::Path { leading_colon, segments: error }, - err_variant, - ))) - }, - gen_arg => { - let msg = format!( + err_variant.len(), + ); + return Err(syn::Error::new(err_variant.span(), msg)); + } + let mut error = err_variant.clone(); + let err_variant = error + .pop() + .expect("Checked to have at least 2; qed") + .into_value() + .ident; + + // Necessary here to eliminate the last double colon + let last = error + .pop() + .expect("Checked to have at least 2; qed") + .into_value(); + error.push_value(last); + + Ok(Some(QueryKind::ResultQuery( + syn::Path { + leading_colon, + segments: error, + }, + err_variant, + ))) + } + gen_arg => { + let msg = format!( "Invalid pallet::storage, unexpected generic argument kind, expected a \ type path to a `PalletError` enum variant, found `{}`", gen_arg.to_token_stream().to_string(), ); - Err(syn::Error::new(gen_arg.span(), msg)) - }, - } - }) - .transpose()? - .unwrap_or(Some(QueryKind::OptionQuery)); - - if let (None, Some(getter)) = (query_kind.as_ref(), getter.as_ref()) { - let msg = "Invalid pallet::storage, cannot generate getter because QueryKind is not \ + Err(syn::Error::new(gen_arg.span(), msg)) + } + } + }) + .transpose()? + .unwrap_or(Some(QueryKind::OptionQuery)); + + if let (None, Some(getter)) = (query_kind.as_ref(), getter.as_ref()) { + let msg = "Invalid pallet::storage, cannot generate getter because QueryKind is not \ identifiable. QueryKind must be `OptionQuery`, `ResultQuery`, `ValueQuery`, or default \ one to be identifiable."; - return Err(syn::Error::new(getter.span(), msg)) - } - - Ok(StorageDef { - attr_span, - index, - vis: item.vis.clone(), - ident: item.ident.clone(), - instances, - metadata, - docs, - getter, - rename_as, - query_kind, - where_clause, - cfg_attrs, - named_generics, - unbounded, - whitelisted, - try_decode, - use_default_hasher, - }) - } + return Err(syn::Error::new(getter.span(), msg)); + } + + Ok(StorageDef { + attr_span, + index, + vis: item.vis.clone(), + ident: item.ident.clone(), + instances, + metadata, + docs, + getter, + rename_as, + query_kind, + where_clause, + cfg_attrs, + named_generics, + unbounded, + whitelisted, + try_decode, + use_default_hasher, + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/tasks.rs b/support/procedural-fork/src/pallet/parse/tasks.rs index 6405bb415..50633fbd0 100644 --- a/support/procedural-fork/src/pallet/parse/tasks.rs +++ b/support/procedural-fork/src/pallet/parse/tasks.rs @@ -30,96 +30,103 @@ use frame_support_procedural_tools::generate_access_from_frame_or_crate; use proc_macro2::TokenStream as TokenStream2; use quote::{quote, ToTokens}; use syn::{ - parse::ParseStream, - parse2, - spanned::Spanned, - token::{Bracket, Paren, PathSep, Pound}, - Attribute, Error, Expr, Ident, ImplItem, ImplItemFn, ItemEnum, ItemImpl, LitInt, Path, - PathArguments, Result, TypePath, + parse::ParseStream, + parse2, + spanned::Spanned, + token::{Bracket, Paren, PathSep, Pound}, + Attribute, Error, Expr, Ident, ImplItem, ImplItemFn, ItemEnum, ItemImpl, LitInt, Path, + PathArguments, Result, TypePath, }; pub mod keywords { - use syn::custom_keyword; + use syn::custom_keyword; - custom_keyword!(tasks_experimental); - custom_keyword!(task_enum); - custom_keyword!(task_list); - custom_keyword!(task_condition); - custom_keyword!(task_index); - custom_keyword!(task_weight); - custom_keyword!(pallet); + custom_keyword!(tasks_experimental); + custom_keyword!(task_enum); + custom_keyword!(task_list); + custom_keyword!(task_condition); + custom_keyword!(task_index); + custom_keyword!(task_weight); + custom_keyword!(pallet); } /// Represents the `#[pallet::tasks_experimental]` attribute and its attached item. Also includes /// metadata about the linked [`TaskEnumDef`] if applicable. #[derive(Clone, Debug)] pub struct TasksDef { - pub tasks_attr: Option, - pub tasks: Vec, - pub item_impl: ItemImpl, - /// Path to `frame_support` - pub scrate: Path, - pub enum_ident: Ident, - pub enum_arguments: PathArguments, + pub tasks_attr: Option, + pub tasks: Vec, + pub item_impl: ItemImpl, + /// Path to `frame_support` + pub scrate: Path, + pub enum_ident: Ident, + pub enum_arguments: PathArguments, } impl syn::parse::Parse for TasksDef { - fn parse(input: ParseStream) -> Result { - let item_impl: ItemImpl = input.parse()?; - let (tasks_attrs, normal_attrs) = partition_tasks_attrs(&item_impl); - let tasks_attr = match tasks_attrs.first() { - Some(attr) => Some(parse2::(attr.to_token_stream())?), - None => None, - }; - if let Some(extra_tasks_attr) = tasks_attrs.get(1) { - return Err(Error::new( - extra_tasks_attr.span(), - "unexpected extra `#[pallet::tasks_experimental]` attribute", - )) - } - let tasks: Vec = if tasks_attr.is_some() { - item_impl - .items - .clone() - .into_iter() - .filter(|impl_item| matches!(impl_item, ImplItem::Fn(_))) - .map(|item| parse2::(item.to_token_stream())) - .collect::>()? - } else { - Vec::new() - }; - let mut task_indices = HashSet::::new(); - for task in tasks.iter() { - let task_index = &task.index_attr.meta.index; - if !task_indices.insert(task_index.clone()) { - return Err(Error::new( - task_index.span(), - format!("duplicate task index `{}`", task_index), - )) - } - } - let mut item_impl = item_impl; - item_impl.attrs = normal_attrs; - - // we require the path on the impl to be a TypePath - let enum_path = parse2::(item_impl.self_ty.to_token_stream())?; - let segments = enum_path.path.segments.iter().collect::>(); - let (Some(last_seg), None) = (segments.get(0), segments.get(1)) else { - return Err(Error::new( - enum_path.span(), - "if specified manually, the task enum must be defined locally in this \ + fn parse(input: ParseStream) -> Result { + let item_impl: ItemImpl = input.parse()?; + let (tasks_attrs, normal_attrs) = partition_tasks_attrs(&item_impl); + let tasks_attr = match tasks_attrs.first() { + Some(attr) => Some(parse2::(attr.to_token_stream())?), + None => None, + }; + if let Some(extra_tasks_attr) = tasks_attrs.get(1) { + return Err(Error::new( + extra_tasks_attr.span(), + "unexpected extra `#[pallet::tasks_experimental]` attribute", + )); + } + let tasks: Vec = if tasks_attr.is_some() { + item_impl + .items + .clone() + .into_iter() + .filter(|impl_item| matches!(impl_item, ImplItem::Fn(_))) + .map(|item| parse2::(item.to_token_stream())) + .collect::>()? + } else { + Vec::new() + }; + let mut task_indices = HashSet::::new(); + for task in tasks.iter() { + let task_index = &task.index_attr.meta.index; + if !task_indices.insert(task_index.clone()) { + return Err(Error::new( + task_index.span(), + format!("duplicate task index `{}`", task_index), + )); + } + } + let mut item_impl = item_impl; + item_impl.attrs = normal_attrs; + + // we require the path on the impl to be a TypePath + let enum_path = parse2::(item_impl.self_ty.to_token_stream())?; + let segments = enum_path.path.segments.iter().collect::>(); + let (Some(last_seg), None) = (segments.get(0), segments.get(1)) else { + return Err(Error::new( + enum_path.span(), + "if specified manually, the task enum must be defined locally in this \ pallet and cannot be a re-export", - )) - }; - let enum_ident = last_seg.ident.clone(); - let enum_arguments = last_seg.arguments.clone(); - - // We do this here because it would be improper to do something fallible like this at - // the expansion phase. Fallible stuff should happen during parsing. - let scrate = generate_access_from_frame_or_crate("frame-support")?; - - Ok(TasksDef { tasks_attr, item_impl, tasks, scrate, enum_ident, enum_arguments }) - } + )); + }; + let enum_ident = last_seg.ident.clone(); + let enum_arguments = last_seg.arguments.clone(); + + // We do this here because it would be improper to do something fallible like this at + // the expansion phase. Fallible stuff should happen during parsing. + let scrate = generate_access_from_frame_or_crate("frame-support")?; + + Ok(TasksDef { + tasks_attr, + item_impl, + tasks, + scrate, + enum_ident, + enum_arguments, + }) + } } /// Parsing for a `#[pallet::tasks_experimental]` attr. @@ -148,821 +155,851 @@ pub type PalletTaskEnumAttr = PalletTaskAttr; /// attached `#[pallet::task_enum]` attribute. #[derive(Clone, Debug)] pub struct TaskEnumDef { - pub attr: Option, - pub item_enum: ItemEnum, - pub scrate: Path, - pub type_use_generics: TokenStream2, + pub attr: Option, + pub item_enum: ItemEnum, + pub scrate: Path, + pub type_use_generics: TokenStream2, } impl syn::parse::Parse for TaskEnumDef { - fn parse(input: ParseStream) -> Result { - let mut item_enum = input.parse::()?; - let attr = extract_pallet_attr(&mut item_enum)?; - let attr = match attr { - Some(attr) => Some(parse2(attr)?), - None => None, - }; + fn parse(input: ParseStream) -> Result { + let mut item_enum = input.parse::()?; + let attr = extract_pallet_attr(&mut item_enum)?; + let attr = match attr { + Some(attr) => Some(parse2(attr)?), + None => None, + }; - // We do this here because it would be improper to do something fallible like this at - // the expansion phase. Fallible stuff should happen during parsing. - let scrate = generate_access_from_frame_or_crate("frame-support")?; + // We do this here because it would be improper to do something fallible like this at + // the expansion phase. Fallible stuff should happen during parsing. + let scrate = generate_access_from_frame_or_crate("frame-support")?; - let type_use_generics = quote!(T); + let type_use_generics = quote!(T); - Ok(TaskEnumDef { attr, item_enum, scrate, type_use_generics }) - } + Ok(TaskEnumDef { + attr, + item_enum, + scrate, + type_use_generics, + }) + } } /// Represents an individual tasks within a [`TasksDef`]. #[derive(Debug, Clone)] pub struct TaskDef { - pub index_attr: TaskIndexAttr, - pub condition_attr: TaskConditionAttr, - pub list_attr: TaskListAttr, - pub weight_attr: TaskWeightAttr, - pub normal_attrs: Vec, - pub item: ImplItemFn, - pub arg_names: Vec, + pub index_attr: TaskIndexAttr, + pub condition_attr: TaskConditionAttr, + pub list_attr: TaskListAttr, + pub weight_attr: TaskWeightAttr, + pub normal_attrs: Vec, + pub item: ImplItemFn, + pub arg_names: Vec, } impl syn::parse::Parse for TaskDef { - fn parse(input: ParseStream) -> Result { - let item = input.parse::()?; - // we only want to activate TaskAttrType parsing errors for tasks-related attributes, - // so we filter them here - let (task_attrs, normal_attrs) = partition_task_attrs(&item); - - let task_attrs: Vec = task_attrs - .into_iter() - .map(|attr| parse2(attr.to_token_stream())) - .collect::>()?; - - let Some(index_attr) = task_attrs - .iter() - .find(|attr| matches!(attr.meta, TaskAttrMeta::TaskIndex(_))) - .cloned() - else { - return Err(Error::new( - item.sig.ident.span(), - "missing `#[pallet::task_index(..)]` attribute", - )) - }; - - let Some(condition_attr) = task_attrs - .iter() - .find(|attr| matches!(attr.meta, TaskAttrMeta::TaskCondition(_))) - .cloned() - else { - return Err(Error::new( - item.sig.ident.span(), - "missing `#[pallet::task_condition(..)]` attribute", - )) - }; - - let Some(list_attr) = task_attrs - .iter() - .find(|attr| matches!(attr.meta, TaskAttrMeta::TaskList(_))) - .cloned() - else { - return Err(Error::new( - item.sig.ident.span(), - "missing `#[pallet::task_list(..)]` attribute", - )) - }; - - let Some(weight_attr) = task_attrs - .iter() - .find(|attr| matches!(attr.meta, TaskAttrMeta::TaskWeight(_))) - .cloned() - else { - return Err(Error::new( - item.sig.ident.span(), - "missing `#[pallet::task_weight(..)]` attribute", - )) - }; - - if let Some(duplicate) = task_attrs - .iter() - .filter(|attr| matches!(attr.meta, TaskAttrMeta::TaskCondition(_))) - .collect::>() - .get(1) - { - return Err(Error::new( - duplicate.span(), - "unexpected extra `#[pallet::task_condition(..)]` attribute", - )) - } - - if let Some(duplicate) = task_attrs - .iter() - .filter(|attr| matches!(attr.meta, TaskAttrMeta::TaskList(_))) - .collect::>() - .get(1) - { - return Err(Error::new( - duplicate.span(), - "unexpected extra `#[pallet::task_list(..)]` attribute", - )) - } - - if let Some(duplicate) = task_attrs - .iter() - .filter(|attr| matches!(attr.meta, TaskAttrMeta::TaskIndex(_))) - .collect::>() - .get(1) - { - return Err(Error::new( - duplicate.span(), - "unexpected extra `#[pallet::task_index(..)]` attribute", - )) - } - - let mut arg_names = vec![]; - for input in item.sig.inputs.iter() { - match input { - syn::FnArg::Typed(pat_type) => match &*pat_type.pat { - syn::Pat::Ident(ident) => arg_names.push(ident.ident.clone()), - _ => return Err(Error::new(input.span(), "unexpected pattern type")), - }, - _ => return Err(Error::new(input.span(), "unexpected function argument type")), - } - } - - let index_attr = index_attr.try_into().expect("we check the type above; QED"); - let condition_attr = condition_attr.try_into().expect("we check the type above; QED"); - let list_attr = list_attr.try_into().expect("we check the type above; QED"); - let weight_attr = weight_attr.try_into().expect("we check the type above; QED"); - - Ok(TaskDef { - index_attr, - condition_attr, - list_attr, - weight_attr, - normal_attrs, - item, - arg_names, - }) - } + fn parse(input: ParseStream) -> Result { + let item = input.parse::()?; + // we only want to activate TaskAttrType parsing errors for tasks-related attributes, + // so we filter them here + let (task_attrs, normal_attrs) = partition_task_attrs(&item); + + let task_attrs: Vec = task_attrs + .into_iter() + .map(|attr| parse2(attr.to_token_stream())) + .collect::>()?; + + let Some(index_attr) = task_attrs + .iter() + .find(|attr| matches!(attr.meta, TaskAttrMeta::TaskIndex(_))) + .cloned() + else { + return Err(Error::new( + item.sig.ident.span(), + "missing `#[pallet::task_index(..)]` attribute", + )); + }; + + let Some(condition_attr) = task_attrs + .iter() + .find(|attr| matches!(attr.meta, TaskAttrMeta::TaskCondition(_))) + .cloned() + else { + return Err(Error::new( + item.sig.ident.span(), + "missing `#[pallet::task_condition(..)]` attribute", + )); + }; + + let Some(list_attr) = task_attrs + .iter() + .find(|attr| matches!(attr.meta, TaskAttrMeta::TaskList(_))) + .cloned() + else { + return Err(Error::new( + item.sig.ident.span(), + "missing `#[pallet::task_list(..)]` attribute", + )); + }; + + let Some(weight_attr) = task_attrs + .iter() + .find(|attr| matches!(attr.meta, TaskAttrMeta::TaskWeight(_))) + .cloned() + else { + return Err(Error::new( + item.sig.ident.span(), + "missing `#[pallet::task_weight(..)]` attribute", + )); + }; + + if let Some(duplicate) = task_attrs + .iter() + .filter(|attr| matches!(attr.meta, TaskAttrMeta::TaskCondition(_))) + .collect::>() + .get(1) + { + return Err(Error::new( + duplicate.span(), + "unexpected extra `#[pallet::task_condition(..)]` attribute", + )); + } + + if let Some(duplicate) = task_attrs + .iter() + .filter(|attr| matches!(attr.meta, TaskAttrMeta::TaskList(_))) + .collect::>() + .get(1) + { + return Err(Error::new( + duplicate.span(), + "unexpected extra `#[pallet::task_list(..)]` attribute", + )); + } + + if let Some(duplicate) = task_attrs + .iter() + .filter(|attr| matches!(attr.meta, TaskAttrMeta::TaskIndex(_))) + .collect::>() + .get(1) + { + return Err(Error::new( + duplicate.span(), + "unexpected extra `#[pallet::task_index(..)]` attribute", + )); + } + + let mut arg_names = vec![]; + for input in item.sig.inputs.iter() { + match input { + syn::FnArg::Typed(pat_type) => match &*pat_type.pat { + syn::Pat::Ident(ident) => arg_names.push(ident.ident.clone()), + _ => return Err(Error::new(input.span(), "unexpected pattern type")), + }, + _ => { + return Err(Error::new( + input.span(), + "unexpected function argument type", + )) + } + } + } + + let index_attr = index_attr.try_into().expect("we check the type above; QED"); + let condition_attr = condition_attr + .try_into() + .expect("we check the type above; QED"); + let list_attr = list_attr.try_into().expect("we check the type above; QED"); + let weight_attr = weight_attr + .try_into() + .expect("we check the type above; QED"); + + Ok(TaskDef { + index_attr, + condition_attr, + list_attr, + weight_attr, + normal_attrs, + item, + arg_names, + }) + } } /// The contents of a [`TasksDef`]-related attribute. #[derive(Parse, Debug, Clone)] pub enum TaskAttrMeta { - #[peek(keywords::task_list, name = "#[pallet::task_list(..)]")] - TaskList(TaskListAttrMeta), - #[peek(keywords::task_index, name = "#[pallet::task_index(..)")] - TaskIndex(TaskIndexAttrMeta), - #[peek(keywords::task_condition, name = "#[pallet::task_condition(..)")] - TaskCondition(TaskConditionAttrMeta), - #[peek(keywords::task_weight, name = "#[pallet::task_weight(..)")] - TaskWeight(TaskWeightAttrMeta), + #[peek(keywords::task_list, name = "#[pallet::task_list(..)]")] + TaskList(TaskListAttrMeta), + #[peek(keywords::task_index, name = "#[pallet::task_index(..)")] + TaskIndex(TaskIndexAttrMeta), + #[peek(keywords::task_condition, name = "#[pallet::task_condition(..)")] + TaskCondition(TaskConditionAttrMeta), + #[peek(keywords::task_weight, name = "#[pallet::task_weight(..)")] + TaskWeight(TaskWeightAttrMeta), } /// The contents of a `#[pallet::task_list]` attribute. #[derive(Parse, Debug, Clone)] pub struct TaskListAttrMeta { - pub task_list: keywords::task_list, - #[paren] - _paren: Paren, - #[inside(_paren)] - pub expr: Expr, + pub task_list: keywords::task_list, + #[paren] + _paren: Paren, + #[inside(_paren)] + pub expr: Expr, } /// The contents of a `#[pallet::task_index]` attribute. #[derive(Parse, Debug, Clone)] pub struct TaskIndexAttrMeta { - pub task_index: keywords::task_index, - #[paren] - _paren: Paren, - #[inside(_paren)] - pub index: LitInt, + pub task_index: keywords::task_index, + #[paren] + _paren: Paren, + #[inside(_paren)] + pub index: LitInt, } /// The contents of a `#[pallet::task_condition]` attribute. #[derive(Parse, Debug, Clone)] pub struct TaskConditionAttrMeta { - pub task_condition: keywords::task_condition, - #[paren] - _paren: Paren, - #[inside(_paren)] - pub expr: Expr, + pub task_condition: keywords::task_condition, + #[paren] + _paren: Paren, + #[inside(_paren)] + pub expr: Expr, } /// The contents of a `#[pallet::task_weight]` attribute. #[derive(Parse, Debug, Clone)] pub struct TaskWeightAttrMeta { - pub task_weight: keywords::task_weight, - #[paren] - _paren: Paren, - #[inside(_paren)] - pub expr: Expr, + pub task_weight: keywords::task_weight, + #[paren] + _paren: Paren, + #[inside(_paren)] + pub expr: Expr, } /// The contents of a `#[pallet::task]` attribute. #[derive(Parse, Debug, Clone)] pub struct PalletTaskAttr { - pub pound: Pound, - #[bracket] - _bracket: Bracket, - #[inside(_bracket)] - pub pallet: keywords::pallet, - #[inside(_bracket)] - pub colons: PathSep, - #[inside(_bracket)] - pub meta: T, + pub pound: Pound, + #[bracket] + _bracket: Bracket, + #[inside(_bracket)] + pub pallet: keywords::pallet, + #[inside(_bracket)] + pub colons: PathSep, + #[inside(_bracket)] + pub meta: T, } impl ToTokens for TaskListAttrMeta { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let task_list = self.task_list; - let expr = &self.expr; - tokens.extend(quote!(#task_list(#expr))); - } + fn to_tokens(&self, tokens: &mut TokenStream2) { + let task_list = self.task_list; + let expr = &self.expr; + tokens.extend(quote!(#task_list(#expr))); + } } impl ToTokens for TaskConditionAttrMeta { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let task_condition = self.task_condition; - let expr = &self.expr; - tokens.extend(quote!(#task_condition(#expr))); - } + fn to_tokens(&self, tokens: &mut TokenStream2) { + let task_condition = self.task_condition; + let expr = &self.expr; + tokens.extend(quote!(#task_condition(#expr))); + } } impl ToTokens for TaskWeightAttrMeta { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let task_weight = self.task_weight; - let expr = &self.expr; - tokens.extend(quote!(#task_weight(#expr))); - } + fn to_tokens(&self, tokens: &mut TokenStream2) { + let task_weight = self.task_weight; + let expr = &self.expr; + tokens.extend(quote!(#task_weight(#expr))); + } } impl ToTokens for TaskIndexAttrMeta { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let task_index = self.task_index; - let index = &self.index; - tokens.extend(quote!(#task_index(#index))) - } + fn to_tokens(&self, tokens: &mut TokenStream2) { + let task_index = self.task_index; + let index = &self.index; + tokens.extend(quote!(#task_index(#index))) + } } impl ToTokens for TaskAttrMeta { - fn to_tokens(&self, tokens: &mut TokenStream2) { - match self { - TaskAttrMeta::TaskList(list) => tokens.extend(list.to_token_stream()), - TaskAttrMeta::TaskIndex(index) => tokens.extend(index.to_token_stream()), - TaskAttrMeta::TaskCondition(condition) => tokens.extend(condition.to_token_stream()), - TaskAttrMeta::TaskWeight(weight) => tokens.extend(weight.to_token_stream()), - } - } + fn to_tokens(&self, tokens: &mut TokenStream2) { + match self { + TaskAttrMeta::TaskList(list) => tokens.extend(list.to_token_stream()), + TaskAttrMeta::TaskIndex(index) => tokens.extend(index.to_token_stream()), + TaskAttrMeta::TaskCondition(condition) => tokens.extend(condition.to_token_stream()), + TaskAttrMeta::TaskWeight(weight) => tokens.extend(weight.to_token_stream()), + } + } } impl ToTokens for PalletTaskAttr { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let pound = self.pound; - let pallet = self.pallet; - let colons = self.colons; - let meta = &self.meta; - tokens.extend(quote!(#pound[#pallet #colons #meta])); - } + fn to_tokens(&self, tokens: &mut TokenStream2) { + let pound = self.pound; + let pallet = self.pallet; + let colons = self.colons; + let meta = &self.meta; + tokens.extend(quote!(#pound[#pallet #colons #meta])); + } } impl TryFrom> for TaskIndexAttr { - type Error = syn::Error; - - fn try_from(value: PalletTaskAttr) -> Result { - let pound = value.pound; - let pallet = value.pallet; - let colons = value.colons; - match value.meta { - TaskAttrMeta::TaskIndex(meta) => parse2(quote!(#pound[#pallet #colons #meta])), - _ => - return Err(Error::new( - value.span(), - format!("`{:?}` cannot be converted to a `TaskIndexAttr`", value.meta), - )), - } - } + type Error = syn::Error; + + fn try_from(value: PalletTaskAttr) -> Result { + let pound = value.pound; + let pallet = value.pallet; + let colons = value.colons; + match value.meta { + TaskAttrMeta::TaskIndex(meta) => parse2(quote!(#pound[#pallet #colons #meta])), + _ => { + return Err(Error::new( + value.span(), + format!( + "`{:?}` cannot be converted to a `TaskIndexAttr`", + value.meta + ), + )) + } + } + } } impl TryFrom> for TaskConditionAttr { - type Error = syn::Error; - - fn try_from(value: PalletTaskAttr) -> Result { - let pound = value.pound; - let pallet = value.pallet; - let colons = value.colons; - match value.meta { - TaskAttrMeta::TaskCondition(meta) => parse2(quote!(#pound[#pallet #colons #meta])), - _ => - return Err(Error::new( - value.span(), - format!("`{:?}` cannot be converted to a `TaskConditionAttr`", value.meta), - )), - } - } + type Error = syn::Error; + + fn try_from(value: PalletTaskAttr) -> Result { + let pound = value.pound; + let pallet = value.pallet; + let colons = value.colons; + match value.meta { + TaskAttrMeta::TaskCondition(meta) => parse2(quote!(#pound[#pallet #colons #meta])), + _ => { + return Err(Error::new( + value.span(), + format!( + "`{:?}` cannot be converted to a `TaskConditionAttr`", + value.meta + ), + )) + } + } + } } impl TryFrom> for TaskWeightAttr { - type Error = syn::Error; - - fn try_from(value: PalletTaskAttr) -> Result { - let pound = value.pound; - let pallet = value.pallet; - let colons = value.colons; - match value.meta { - TaskAttrMeta::TaskWeight(meta) => parse2(quote!(#pound[#pallet #colons #meta])), - _ => - return Err(Error::new( - value.span(), - format!("`{:?}` cannot be converted to a `TaskWeightAttr`", value.meta), - )), - } - } + type Error = syn::Error; + + fn try_from(value: PalletTaskAttr) -> Result { + let pound = value.pound; + let pallet = value.pallet; + let colons = value.colons; + match value.meta { + TaskAttrMeta::TaskWeight(meta) => parse2(quote!(#pound[#pallet #colons #meta])), + _ => { + return Err(Error::new( + value.span(), + format!( + "`{:?}` cannot be converted to a `TaskWeightAttr`", + value.meta + ), + )) + } + } + } } impl TryFrom> for TaskListAttr { - type Error = syn::Error; - - fn try_from(value: PalletTaskAttr) -> Result { - let pound = value.pound; - let pallet = value.pallet; - let colons = value.colons; - match value.meta { - TaskAttrMeta::TaskList(meta) => parse2(quote!(#pound[#pallet #colons #meta])), - _ => - return Err(Error::new( - value.span(), - format!("`{:?}` cannot be converted to a `TaskListAttr`", value.meta), - )), - } - } + type Error = syn::Error; + + fn try_from(value: PalletTaskAttr) -> Result { + let pound = value.pound; + let pallet = value.pallet; + let colons = value.colons; + match value.meta { + TaskAttrMeta::TaskList(meta) => parse2(quote!(#pound[#pallet #colons #meta])), + _ => { + return Err(Error::new( + value.span(), + format!("`{:?}` cannot be converted to a `TaskListAttr`", value.meta), + )) + } + } + } } fn extract_pallet_attr(item_enum: &mut ItemEnum) -> Result> { - let mut duplicate = None; - let mut attr = None; - item_enum.attrs = item_enum - .attrs - .iter() - .filter(|found_attr| { - let segs = found_attr - .path() - .segments - .iter() - .map(|seg| seg.ident.clone()) - .collect::>(); - let (Some(seg1), Some(_), None) = (segs.get(0), segs.get(1), segs.get(2)) else { - return true - }; - if seg1 != "pallet" { - return true - } - if attr.is_some() { - duplicate = Some(found_attr.span()); - } - attr = Some(found_attr.to_token_stream()); - false - }) - .cloned() - .collect(); - if let Some(span) = duplicate { - return Err(Error::new(span, "only one `#[pallet::_]` attribute is supported on this item")) - } - Ok(attr) + let mut duplicate = None; + let mut attr = None; + item_enum.attrs = item_enum + .attrs + .iter() + .filter(|found_attr| { + let segs = found_attr + .path() + .segments + .iter() + .map(|seg| seg.ident.clone()) + .collect::>(); + let (Some(seg1), Some(_), None) = (segs.get(0), segs.get(1), segs.get(2)) else { + return true; + }; + if seg1 != "pallet" { + return true; + } + if attr.is_some() { + duplicate = Some(found_attr.span()); + } + attr = Some(found_attr.to_token_stream()); + false + }) + .cloned() + .collect(); + if let Some(span) = duplicate { + return Err(Error::new( + span, + "only one `#[pallet::_]` attribute is supported on this item", + )); + } + Ok(attr) } fn partition_tasks_attrs(item_impl: &ItemImpl) -> (Vec, Vec) { - item_impl.attrs.clone().into_iter().partition(|attr| { - let mut path_segs = attr.path().segments.iter(); - let (Some(prefix), Some(suffix), None) = - (path_segs.next(), path_segs.next(), path_segs.next()) - else { - return false - }; - prefix.ident == "pallet" && suffix.ident == "tasks_experimental" - }) + item_impl.attrs.clone().into_iter().partition(|attr| { + let mut path_segs = attr.path().segments.iter(); + let (Some(prefix), Some(suffix), None) = + (path_segs.next(), path_segs.next(), path_segs.next()) + else { + return false; + }; + prefix.ident == "pallet" && suffix.ident == "tasks_experimental" + }) } fn partition_task_attrs(item: &ImplItemFn) -> (Vec, Vec) { - item.attrs.clone().into_iter().partition(|attr| { - let mut path_segs = attr.path().segments.iter(); - let (Some(prefix), Some(suffix)) = (path_segs.next(), path_segs.next()) else { - return false - }; - // N.B: the `PartialEq` impl between `Ident` and `&str` is more efficient than - // parsing and makes no stack or heap allocations - prefix.ident == "pallet" && - (suffix.ident == "tasks_experimental" || - suffix.ident == "task_list" || - suffix.ident == "task_condition" || - suffix.ident == "task_weight" || - suffix.ident == "task_index") - }) + item.attrs.clone().into_iter().partition(|attr| { + let mut path_segs = attr.path().segments.iter(); + let (Some(prefix), Some(suffix)) = (path_segs.next(), path_segs.next()) else { + return false; + }; + // N.B: the `PartialEq` impl between `Ident` and `&str` is more efficient than + // parsing and makes no stack or heap allocations + prefix.ident == "pallet" + && (suffix.ident == "tasks_experimental" + || suffix.ident == "task_list" + || suffix.ident == "task_condition" + || suffix.ident == "task_weight" + || suffix.ident == "task_index") + }) } #[test] fn test_parse_task_list_() { - parse2::(quote!(#[pallet::task_list(Something::iter())])).unwrap(); - parse2::(quote!(#[pallet::task_list(Numbers::::iter_keys())])).unwrap(); - parse2::(quote!(#[pallet::task_list(iter())])).unwrap(); - assert_parse_error_matches!( - parse2::(quote!(#[pallet::task_list()])), - "expected an expression" - ); - assert_parse_error_matches!( - parse2::(quote!(#[pallet::task_list])), - "expected parentheses" - ); + parse2::(quote!(#[pallet::task_list(Something::iter())])).unwrap(); + parse2::(quote!(#[pallet::task_list(Numbers::::iter_keys())])).unwrap(); + parse2::(quote!(#[pallet::task_list(iter())])).unwrap(); + assert_parse_error_matches!( + parse2::(quote!(#[pallet::task_list()])), + "expected an expression" + ); + assert_parse_error_matches!( + parse2::(quote!(#[pallet::task_list])), + "expected parentheses" + ); } #[test] fn test_parse_task_index() { - parse2::(quote!(#[pallet::task_index(3)])).unwrap(); - parse2::(quote!(#[pallet::task_index(0)])).unwrap(); - parse2::(quote!(#[pallet::task_index(17)])).unwrap(); - assert_parse_error_matches!( - parse2::(quote!(#[pallet::task_index])), - "expected parentheses" - ); - assert_parse_error_matches!( - parse2::(quote!(#[pallet::task_index("hey")])), - "expected integer literal" - ); - assert_parse_error_matches!( - parse2::(quote!(#[pallet::task_index(0.3)])), - "expected integer literal" - ); + parse2::(quote!(#[pallet::task_index(3)])).unwrap(); + parse2::(quote!(#[pallet::task_index(0)])).unwrap(); + parse2::(quote!(#[pallet::task_index(17)])).unwrap(); + assert_parse_error_matches!( + parse2::(quote!(#[pallet::task_index])), + "expected parentheses" + ); + assert_parse_error_matches!( + parse2::(quote!(#[pallet::task_index("hey")])), + "expected integer literal" + ); + assert_parse_error_matches!( + parse2::(quote!(#[pallet::task_index(0.3)])), + "expected integer literal" + ); } #[test] fn test_parse_task_condition() { - parse2::(quote!(#[pallet::task_condition(|x| x.is_some())])).unwrap(); - parse2::(quote!(#[pallet::task_condition(|_x| some_expr())])).unwrap(); - parse2::(quote!(#[pallet::task_condition(|| some_expr())])).unwrap(); - parse2::(quote!(#[pallet::task_condition(some_expr())])).unwrap(); + parse2::(quote!(#[pallet::task_condition(|x| x.is_some())])).unwrap(); + parse2::(quote!(#[pallet::task_condition(|_x| some_expr())])).unwrap(); + parse2::(quote!(#[pallet::task_condition(|| some_expr())])).unwrap(); + parse2::(quote!(#[pallet::task_condition(some_expr())])).unwrap(); } #[test] fn test_parse_tasks_attr() { - parse2::(quote!(#[pallet::tasks_experimental])).unwrap(); - assert_parse_error_matches!( - parse2::(quote!(#[pallet::taskss])), - "expected `tasks_experimental`" - ); - assert_parse_error_matches!( - parse2::(quote!(#[pallet::tasks_])), - "expected `tasks_experimental`" - ); - assert_parse_error_matches!( - parse2::(quote!(#[pal::tasks])), - "expected `pallet`" - ); - assert_parse_error_matches!( - parse2::(quote!(#[pallet::tasks_experimental()])), - "unexpected token" - ); + parse2::(quote!(#[pallet::tasks_experimental])).unwrap(); + assert_parse_error_matches!( + parse2::(quote!(#[pallet::taskss])), + "expected `tasks_experimental`" + ); + assert_parse_error_matches!( + parse2::(quote!(#[pallet::tasks_])), + "expected `tasks_experimental`" + ); + assert_parse_error_matches!( + parse2::(quote!(#[pal::tasks])), + "expected `pallet`" + ); + assert_parse_error_matches!( + parse2::(quote!(#[pallet::tasks_experimental()])), + "unexpected token" + ); } #[test] fn test_parse_tasks_def_basic() { - simulate_manifest_dir("../../examples/basic", || { - let parsed = parse2::(quote! { - #[pallet::tasks_experimental] - impl, I: 'static> Pallet { - /// Add a pair of numbers into the totals and remove them. - #[pallet::task_list(Numbers::::iter_keys())] - #[pallet::task_condition(|i| Numbers::::contains_key(i))] - #[pallet::task_index(0)] - #[pallet::task_weight(0)] - pub fn add_number_into_total(i: u32) -> DispatchResult { - let v = Numbers::::take(i).ok_or(Error::::NotFound)?; - Total::::mutate(|(total_keys, total_values)| { - *total_keys += i; - *total_values += v; - }); - Ok(()) - } - } - }) - .unwrap(); - assert_eq!(parsed.tasks.len(), 1); - }); + simulate_manifest_dir("../../examples/basic", || { + let parsed = parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + /// Add a pair of numbers into the totals and remove them. + #[pallet::task_list(Numbers::::iter_keys())] + #[pallet::task_condition(|i| Numbers::::contains_key(i))] + #[pallet::task_index(0)] + #[pallet::task_weight(0)] + pub fn add_number_into_total(i: u32) -> DispatchResult { + let v = Numbers::::take(i).ok_or(Error::::NotFound)?; + Total::::mutate(|(total_keys, total_values)| { + *total_keys += i; + *total_values += v; + }); + Ok(()) + } + } + }) + .unwrap(); + assert_eq!(parsed.tasks.len(), 1); + }); } #[test] fn test_parse_tasks_def_basic_increment_decrement() { - simulate_manifest_dir("../../examples/basic", || { - let parsed = parse2::(quote! { - #[pallet::tasks_experimental] - impl, I: 'static> Pallet { - /// Get the value and check if it can be incremented - #[pallet::task_index(0)] - #[pallet::task_condition(|| { - let value = Value::::get().unwrap(); - value < 255 - })] - #[pallet::task_list(Vec::>::new())] - #[pallet::task_weight(0)] - fn increment() -> DispatchResult { - let value = Value::::get().unwrap_or_default(); - if value >= 255 { - Err(Error::::ValueOverflow.into()) - } else { - let new_val = value.checked_add(1).ok_or(Error::::ValueOverflow)?; - Value::::put(new_val); - Pallet::::deposit_event(Event::Incremented { new_val }); - Ok(()) - } - } - - // Get the value and check if it can be decremented - #[pallet::task_index(1)] - #[pallet::task_condition(|| { - let value = Value::::get().unwrap(); - value > 0 - })] - #[pallet::task_list(Vec::>::new())] - #[pallet::task_weight(0)] - fn decrement() -> DispatchResult { - let value = Value::::get().unwrap_or_default(); - if value == 0 { - Err(Error::::ValueUnderflow.into()) - } else { - let new_val = value.checked_sub(1).ok_or(Error::::ValueUnderflow)?; - Value::::put(new_val); - Pallet::::deposit_event(Event::Decremented { new_val }); - Ok(()) - } - } - } - }) - .unwrap(); - assert_eq!(parsed.tasks.len(), 2); - }); + simulate_manifest_dir("../../examples/basic", || { + let parsed = parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + /// Get the value and check if it can be incremented + #[pallet::task_index(0)] + #[pallet::task_condition(|| { + let value = Value::::get().unwrap(); + value < 255 + })] + #[pallet::task_list(Vec::>::new())] + #[pallet::task_weight(0)] + fn increment() -> DispatchResult { + let value = Value::::get().unwrap_or_default(); + if value >= 255 { + Err(Error::::ValueOverflow.into()) + } else { + let new_val = value.checked_add(1).ok_or(Error::::ValueOverflow)?; + Value::::put(new_val); + Pallet::::deposit_event(Event::Incremented { new_val }); + Ok(()) + } + } + + // Get the value and check if it can be decremented + #[pallet::task_index(1)] + #[pallet::task_condition(|| { + let value = Value::::get().unwrap(); + value > 0 + })] + #[pallet::task_list(Vec::>::new())] + #[pallet::task_weight(0)] + fn decrement() -> DispatchResult { + let value = Value::::get().unwrap_or_default(); + if value == 0 { + Err(Error::::ValueUnderflow.into()) + } else { + let new_val = value.checked_sub(1).ok_or(Error::::ValueUnderflow)?; + Value::::put(new_val); + Pallet::::deposit_event(Event::Decremented { new_val }); + Ok(()) + } + } + } + }) + .unwrap(); + assert_eq!(parsed.tasks.len(), 2); + }); } #[test] fn test_parse_tasks_def_duplicate_index() { - simulate_manifest_dir("../../examples/basic", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::tasks_experimental] - impl, I: 'static> Pallet { - #[pallet::task_list(Something::iter())] - #[pallet::task_condition(|i| i % 2 == 0)] - #[pallet::task_index(0)] - #[pallet::task_weight(0)] - pub fn foo(i: u32) -> DispatchResult { - Ok(()) - } - - #[pallet::task_list(Numbers::::iter_keys())] - #[pallet::task_condition(|i| Numbers::::contains_key(i))] - #[pallet::task_index(0)] - #[pallet::task_weight(0)] - pub fn bar(i: u32) -> DispatchResult { - Ok(()) - } - } - }), - "duplicate task index `0`" - ); - }); + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + #[pallet::task_list(Something::iter())] + #[pallet::task_condition(|i| i % 2 == 0)] + #[pallet::task_index(0)] + #[pallet::task_weight(0)] + pub fn foo(i: u32) -> DispatchResult { + Ok(()) + } + + #[pallet::task_list(Numbers::::iter_keys())] + #[pallet::task_condition(|i| Numbers::::contains_key(i))] + #[pallet::task_index(0)] + #[pallet::task_weight(0)] + pub fn bar(i: u32) -> DispatchResult { + Ok(()) + } + } + }), + "duplicate task index `0`" + ); + }); } #[test] fn test_parse_tasks_def_missing_task_list() { - simulate_manifest_dir("../../examples/basic", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::tasks_experimental] - impl, I: 'static> Pallet { - #[pallet::task_condition(|i| i % 2 == 0)] - #[pallet::task_index(0)] - pub fn foo(i: u32) -> DispatchResult { - Ok(()) - } - } - }), - r"missing `#\[pallet::task_list\(\.\.\)\]`" - ); - }); + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + #[pallet::task_condition(|i| i % 2 == 0)] + #[pallet::task_index(0)] + pub fn foo(i: u32) -> DispatchResult { + Ok(()) + } + } + }), + r"missing `#\[pallet::task_list\(\.\.\)\]`" + ); + }); } #[test] fn test_parse_tasks_def_missing_task_condition() { - simulate_manifest_dir("../../examples/basic", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::tasks_experimental] - impl, I: 'static> Pallet { - #[pallet::task_list(Something::iter())] - #[pallet::task_index(0)] - pub fn foo(i: u32) -> DispatchResult { - Ok(()) - } - } - }), - r"missing `#\[pallet::task_condition\(\.\.\)\]`" - ); - }); + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + #[pallet::task_list(Something::iter())] + #[pallet::task_index(0)] + pub fn foo(i: u32) -> DispatchResult { + Ok(()) + } + } + }), + r"missing `#\[pallet::task_condition\(\.\.\)\]`" + ); + }); } #[test] fn test_parse_tasks_def_missing_task_index() { - simulate_manifest_dir("../../examples/basic", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::tasks_experimental] - impl, I: 'static> Pallet { - #[pallet::task_condition(|i| i % 2 == 0)] - #[pallet::task_list(Something::iter())] - pub fn foo(i: u32) -> DispatchResult { - Ok(()) - } - } - }), - r"missing `#\[pallet::task_index\(\.\.\)\]`" - ); - }); + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + #[pallet::task_condition(|i| i % 2 == 0)] + #[pallet::task_list(Something::iter())] + pub fn foo(i: u32) -> DispatchResult { + Ok(()) + } + } + }), + r"missing `#\[pallet::task_index\(\.\.\)\]`" + ); + }); } #[test] fn test_parse_tasks_def_missing_task_weight() { - simulate_manifest_dir("../../examples/basic", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::tasks_experimental] - impl, I: 'static> Pallet { - #[pallet::task_condition(|i| i % 2 == 0)] - #[pallet::task_list(Something::iter())] - #[pallet::task_index(0)] - pub fn foo(i: u32) -> DispatchResult { - Ok(()) - } - } - }), - r"missing `#\[pallet::task_weight\(\.\.\)\]`" - ); - }); + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + #[pallet::task_condition(|i| i % 2 == 0)] + #[pallet::task_list(Something::iter())] + #[pallet::task_index(0)] + pub fn foo(i: u32) -> DispatchResult { + Ok(()) + } + } + }), + r"missing `#\[pallet::task_weight\(\.\.\)\]`" + ); + }); } #[test] fn test_parse_tasks_def_unexpected_extra_task_list_attr() { - simulate_manifest_dir("../../examples/basic", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::tasks_experimental] - impl, I: 'static> Pallet { - #[pallet::task_condition(|i| i % 2 == 0)] - #[pallet::task_index(0)] - #[pallet::task_weight(0)] - #[pallet::task_list(Something::iter())] - #[pallet::task_list(SomethingElse::iter())] - pub fn foo(i: u32) -> DispatchResult { - Ok(()) - } - } - }), - r"unexpected extra `#\[pallet::task_list\(\.\.\)\]`" - ); - }); + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + #[pallet::task_condition(|i| i % 2 == 0)] + #[pallet::task_index(0)] + #[pallet::task_weight(0)] + #[pallet::task_list(Something::iter())] + #[pallet::task_list(SomethingElse::iter())] + pub fn foo(i: u32) -> DispatchResult { + Ok(()) + } + } + }), + r"unexpected extra `#\[pallet::task_list\(\.\.\)\]`" + ); + }); } #[test] fn test_parse_tasks_def_unexpected_extra_task_condition_attr() { - simulate_manifest_dir("../../examples/basic", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::tasks_experimental] - impl, I: 'static> Pallet { - #[pallet::task_condition(|i| i % 2 == 0)] - #[pallet::task_condition(|i| i % 4 == 0)] - #[pallet::task_index(0)] - #[pallet::task_list(Something::iter())] - #[pallet::task_weight(0)] - pub fn foo(i: u32) -> DispatchResult { - Ok(()) - } - } - }), - r"unexpected extra `#\[pallet::task_condition\(\.\.\)\]`" - ); - }); + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + #[pallet::task_condition(|i| i % 2 == 0)] + #[pallet::task_condition(|i| i % 4 == 0)] + #[pallet::task_index(0)] + #[pallet::task_list(Something::iter())] + #[pallet::task_weight(0)] + pub fn foo(i: u32) -> DispatchResult { + Ok(()) + } + } + }), + r"unexpected extra `#\[pallet::task_condition\(\.\.\)\]`" + ); + }); } #[test] fn test_parse_tasks_def_unexpected_extra_task_index_attr() { - simulate_manifest_dir("../../examples/basic", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::tasks_experimental] - impl, I: 'static> Pallet { - #[pallet::task_condition(|i| i % 2 == 0)] - #[pallet::task_index(0)] - #[pallet::task_index(0)] - #[pallet::task_list(Something::iter())] - #[pallet::task_weight(0)] - pub fn foo(i: u32) -> DispatchResult { - Ok(()) - } - } - }), - r"unexpected extra `#\[pallet::task_index\(\.\.\)\]`" - ); - }); + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + impl, I: 'static> Pallet { + #[pallet::task_condition(|i| i % 2 == 0)] + #[pallet::task_index(0)] + #[pallet::task_index(0)] + #[pallet::task_list(Something::iter())] + #[pallet::task_weight(0)] + pub fn foo(i: u32) -> DispatchResult { + Ok(()) + } + } + }), + r"unexpected extra `#\[pallet::task_index\(\.\.\)\]`" + ); + }); } #[test] fn test_parse_tasks_def_extra_tasks_attribute() { - simulate_manifest_dir("../../examples/basic", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::tasks_experimental] - #[pallet::tasks_experimental] - impl, I: 'static> Pallet {} - }), - r"unexpected extra `#\[pallet::tasks_experimental\]` attribute" - ); - }); + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::tasks_experimental] + #[pallet::tasks_experimental] + impl, I: 'static> Pallet {} + }), + r"unexpected extra `#\[pallet::tasks_experimental\]` attribute" + ); + }); } #[test] fn test_parse_task_enum_def_basic() { - simulate_manifest_dir("../../examples/basic", || { - parse2::(quote! { - #[pallet::task_enum] - pub enum Task { - Increment, - Decrement, - } - }) - .unwrap(); - }); + simulate_manifest_dir("../../examples/basic", || { + parse2::(quote! { + #[pallet::task_enum] + pub enum Task { + Increment, + Decrement, + } + }) + .unwrap(); + }); } #[test] fn test_parse_task_enum_def_non_task_name() { - simulate_manifest_dir("../../examples/basic", || { - parse2::(quote! { - #[pallet::task_enum] - pub enum Something { - Foo - } - }) - .unwrap(); - }); + simulate_manifest_dir("../../examples/basic", || { + parse2::(quote! { + #[pallet::task_enum] + pub enum Something { + Foo + } + }) + .unwrap(); + }); } #[test] fn test_parse_task_enum_def_missing_attr_allowed() { - simulate_manifest_dir("../../examples/basic", || { - parse2::(quote! { - pub enum Task { - Increment, - Decrement, - } - }) - .unwrap(); - }); + simulate_manifest_dir("../../examples/basic", || { + parse2::(quote! { + pub enum Task { + Increment, + Decrement, + } + }) + .unwrap(); + }); } #[test] fn test_parse_task_enum_def_missing_attr_alternate_name_allowed() { - simulate_manifest_dir("../../examples/basic", || { - parse2::(quote! { - pub enum Foo { - Red, - } - }) - .unwrap(); - }); + simulate_manifest_dir("../../examples/basic", || { + parse2::(quote! { + pub enum Foo { + Red, + } + }) + .unwrap(); + }); } #[test] fn test_parse_task_enum_def_wrong_attr() { - simulate_manifest_dir("../../examples/basic", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::something] - pub enum Task { - Increment, - Decrement, - } - }), - "expected `task_enum`" - ); - }); + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::something] + pub enum Task { + Increment, + Decrement, + } + }), + "expected `task_enum`" + ); + }); } #[test] fn test_parse_task_enum_def_wrong_item() { - simulate_manifest_dir("../../examples/basic", || { - assert_parse_error_matches!( - parse2::(quote! { - #[pallet::task_enum] - pub struct Something; - }), - "expected `enum`" - ); - }); + simulate_manifest_dir("../../examples/basic", || { + assert_parse_error_matches!( + parse2::(quote! { + #[pallet::task_enum] + pub struct Something; + }), + "expected `enum`" + ); + }); } diff --git a/support/procedural-fork/src/pallet/parse/tests/mod.rs b/support/procedural-fork/src/pallet/parse/tests/mod.rs index a3661f307..fd7dc2dbe 100644 --- a/support/procedural-fork/src/pallet/parse/tests/mod.rs +++ b/support/procedural-fork/src/pallet/parse/tests/mod.rs @@ -20,7 +20,7 @@ use syn::parse_quote; #[doc(hidden)] pub mod __private { - pub use regex; + pub use regex; } /// Allows you to assert that the input expression resolves to an error whose string @@ -63,22 +63,22 @@ pub mod __private { /// enough that it will work with any error with a reasonable [`core::fmt::Display`] impl. #[macro_export] macro_rules! assert_parse_error_matches { - ($expr:expr, $reg:literal) => { - match $expr { - Ok(_) => panic!("Expected an `Error(..)`, but got Ok(..)"), - Err(e) => { - let error_message = e.to_string(); - let re = $crate::pallet::parse::tests::__private::regex::Regex::new($reg) - .expect("Invalid regex pattern"); - assert!( - re.is_match(&error_message), - "Error message \"{}\" does not match the pattern \"{}\"", - error_message, - $reg - ); - }, - } - }; + ($expr:expr, $reg:literal) => { + match $expr { + Ok(_) => panic!("Expected an `Error(..)`, but got Ok(..)"), + Err(e) => { + let error_message = e.to_string(); + let re = $crate::pallet::parse::tests::__private::regex::Regex::new($reg) + .expect("Invalid regex pattern"); + assert!( + re.is_match(&error_message), + "Error message \"{}\" does not match the pattern \"{}\"", + error_message, + $reg + ); + } + } + }; } /// Allows you to assert that an entire pallet parses successfully. A custom syntax is used for @@ -183,82 +183,82 @@ macro_rules! assert_pallet_parse_error { /// This function uses a [`Mutex`] to avoid a race condition created when multiple tests try to /// modify and then restore the `CARGO_MANIFEST_DIR` ENV var in an overlapping way. pub fn simulate_manifest_dir, F: FnOnce() + std::panic::UnwindSafe>( - path: P, - closure: F, + path: P, + closure: F, ) { - use std::{env::*, path::*}; + use std::{env::*, path::*}; - /// Ensures that only one thread can modify/restore the `CARGO_MANIFEST_DIR` ENV var at a time, - /// avoiding a race condition because `cargo test` runs tests in parallel. - /// - /// Although this forces all tests that use [`simulate_manifest_dir`] to run sequentially with - /// respect to each other, this is still several orders of magnitude faster than using UI - /// tests, even if they are run in parallel. - static MANIFEST_DIR_LOCK: Mutex<()> = Mutex::new(()); + /// Ensures that only one thread can modify/restore the `CARGO_MANIFEST_DIR` ENV var at a time, + /// avoiding a race condition because `cargo test` runs tests in parallel. + /// + /// Although this forces all tests that use [`simulate_manifest_dir`] to run sequentially with + /// respect to each other, this is still several orders of magnitude faster than using UI + /// tests, even if they are run in parallel. + static MANIFEST_DIR_LOCK: Mutex<()> = Mutex::new(()); - // avoid race condition when swapping out `CARGO_MANIFEST_DIR` - let guard = MANIFEST_DIR_LOCK.lock().unwrap(); + // avoid race condition when swapping out `CARGO_MANIFEST_DIR` + let guard = MANIFEST_DIR_LOCK.lock().unwrap(); - // obtain the current/original `CARGO_MANIFEST_DIR` - let orig = PathBuf::from( - var("CARGO_MANIFEST_DIR").expect("failed to read ENV var `CARGO_MANIFEST_DIR`"), - ); + // obtain the current/original `CARGO_MANIFEST_DIR` + let orig = PathBuf::from( + var("CARGO_MANIFEST_DIR").expect("failed to read ENV var `CARGO_MANIFEST_DIR`"), + ); - // set `CARGO_MANIFEST_DIR` to the provided path, relative to current working dir - set_var("CARGO_MANIFEST_DIR", orig.join(path.as_ref())); + // set `CARGO_MANIFEST_DIR` to the provided path, relative to current working dir + set_var("CARGO_MANIFEST_DIR", orig.join(path.as_ref())); - // safely run closure catching any panics - let result = panic::catch_unwind(closure); + // safely run closure catching any panics + let result = panic::catch_unwind(closure); - // restore original `CARGO_MANIFEST_DIR` before unwinding - set_var("CARGO_MANIFEST_DIR", &orig); + // restore original `CARGO_MANIFEST_DIR` before unwinding + set_var("CARGO_MANIFEST_DIR", &orig); - // unlock the mutex so we don't poison it if there is a panic - drop(guard); + // unlock the mutex so we don't poison it if there is a panic + drop(guard); - // unwind any panics originally encountered when running closure - result.unwrap(); + // unwind any panics originally encountered when running closure + result.unwrap(); } mod tasks; #[test] fn test_parse_minimal_pallet() { - assert_pallet_parses! { - #[manifest_dir("../../examples/basic")] - #[frame_support::pallet] - pub mod pallet { - #[pallet::config] - pub trait Config: frame_system::Config {} + assert_pallet_parses! { + #[manifest_dir("../../examples/basic")] + #[frame_support::pallet] + pub mod pallet { + #[pallet::config] + pub trait Config: frame_system::Config {} - #[pallet::pallet] - pub struct Pallet(_); - } - }; + #[pallet::pallet] + pub struct Pallet(_); + } + }; } #[test] fn test_parse_pallet_missing_pallet() { - assert_pallet_parse_error! { - #[manifest_dir("../../examples/basic")] - #[error_regex("Missing `\\#\\[pallet::pallet\\]`")] - #[frame_support::pallet] - pub mod pallet { - #[pallet::config] - pub trait Config: frame_system::Config {} - } - } + assert_pallet_parse_error! { + #[manifest_dir("../../examples/basic")] + #[error_regex("Missing `\\#\\[pallet::pallet\\]`")] + #[frame_support::pallet] + pub mod pallet { + #[pallet::config] + pub trait Config: frame_system::Config {} + } + } } #[test] fn test_parse_pallet_missing_config() { - assert_pallet_parse_error! { - #[manifest_dir("../../examples/basic")] - #[error_regex("Missing `\\#\\[pallet::config\\]`")] - #[frame_support::pallet] - pub mod pallet { - #[pallet::pallet] - pub struct Pallet(_); - } - } + assert_pallet_parse_error! { + #[manifest_dir("../../examples/basic")] + #[error_regex("Missing `\\#\\[pallet::config\\]`")] + #[frame_support::pallet] + pub mod pallet { + #[pallet::pallet] + pub struct Pallet(_); + } + } } diff --git a/support/procedural-fork/src/pallet/parse/tests/tasks.rs b/support/procedural-fork/src/pallet/parse/tests/tasks.rs index 9f1436284..6cd4d13bb 100644 --- a/support/procedural-fork/src/pallet/parse/tests/tasks.rs +++ b/support/procedural-fork/src/pallet/parse/tests/tasks.rs @@ -19,222 +19,222 @@ use syn::parse_quote; #[test] fn test_parse_pallet_with_task_enum_missing_impl() { - assert_pallet_parse_error! { - #[manifest_dir("../../examples/basic")] - #[error_regex("Missing `\\#\\[pallet::tasks_experimental\\]` impl")] - #[frame_support::pallet] - pub mod pallet { - #[pallet::task_enum] - pub enum Task { - Something, - } - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(_); - } - } + assert_pallet_parse_error! { + #[manifest_dir("../../examples/basic")] + #[error_regex("Missing `\\#\\[pallet::tasks_experimental\\]` impl")] + #[frame_support::pallet] + pub mod pallet { + #[pallet::task_enum] + pub enum Task { + Something, + } + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + } } #[test] fn test_parse_pallet_with_task_enum_wrong_attribute() { - assert_pallet_parse_error! { - #[manifest_dir("../../examples/basic")] - #[error_regex("expected one of")] - #[frame_support::pallet] - pub mod pallet { - #[pallet::wrong_attribute] - pub enum Task { - Something, - } - - #[pallet::task_list] - impl frame_support::traits::Task for Task - where - T: TypeInfo, - {} - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(_); - } - } + assert_pallet_parse_error! { + #[manifest_dir("../../examples/basic")] + #[error_regex("expected one of")] + #[frame_support::pallet] + pub mod pallet { + #[pallet::wrong_attribute] + pub enum Task { + Something, + } + + #[pallet::task_list] + impl frame_support::traits::Task for Task + where + T: TypeInfo, + {} + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + } } #[test] fn test_parse_pallet_missing_task_enum() { - assert_pallet_parses! { - #[manifest_dir("../../examples/basic")] - #[frame_support::pallet] - pub mod pallet { - #[pallet::tasks_experimental] - #[cfg(test)] // aha, this means it's being eaten - impl frame_support::traits::Task for Task - where - T: TypeInfo, - {} - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(_); - } - }; + assert_pallet_parses! { + #[manifest_dir("../../examples/basic")] + #[frame_support::pallet] + pub mod pallet { + #[pallet::tasks_experimental] + #[cfg(test)] // aha, this means it's being eaten + impl frame_support::traits::Task for Task + where + T: TypeInfo, + {} + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + }; } #[test] fn test_parse_pallet_task_list_in_wrong_place() { - assert_pallet_parse_error! { - #[manifest_dir("../../examples/basic")] - #[error_regex("can only be used on items within an `impl` statement.")] - #[frame_support::pallet] - pub mod pallet { - pub enum MyCustomTaskEnum { - Something, - } - - #[pallet::task_list] - pub fn something() { - println!("hey"); - } - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(_); - } - } + assert_pallet_parse_error! { + #[manifest_dir("../../examples/basic")] + #[error_regex("can only be used on items within an `impl` statement.")] + #[frame_support::pallet] + pub mod pallet { + pub enum MyCustomTaskEnum { + Something, + } + + #[pallet::task_list] + pub fn something() { + println!("hey"); + } + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + } } #[test] fn test_parse_pallet_manual_tasks_impl_without_manual_tasks_enum() { - assert_pallet_parse_error! { - #[manifest_dir("../../examples/basic")] - #[error_regex(".*attribute must be attached to your.*")] - #[frame_support::pallet] - pub mod pallet { - - impl frame_support::traits::Task for Task - where - T: TypeInfo, - { - type Enumeration = sp_std::vec::IntoIter>; - - fn iter() -> Self::Enumeration { - sp_std::vec![Task::increment, Task::decrement].into_iter() - } - } - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(_); - } - } + assert_pallet_parse_error! { + #[manifest_dir("../../examples/basic")] + #[error_regex(".*attribute must be attached to your.*")] + #[frame_support::pallet] + pub mod pallet { + + impl frame_support::traits::Task for Task + where + T: TypeInfo, + { + type Enumeration = sp_std::vec::IntoIter>; + + fn iter() -> Self::Enumeration { + sp_std::vec![Task::increment, Task::decrement].into_iter() + } + } + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + } } #[test] fn test_parse_pallet_manual_task_enum_non_manual_impl() { - assert_pallet_parses! { - #[manifest_dir("../../examples/basic")] - #[frame_support::pallet] - pub mod pallet { - pub enum MyCustomTaskEnum { - Something, - } - - #[pallet::tasks_experimental] - impl frame_support::traits::Task for MyCustomTaskEnum - where - T: TypeInfo, - {} - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(_); - } - }; + assert_pallet_parses! { + #[manifest_dir("../../examples/basic")] + #[frame_support::pallet] + pub mod pallet { + pub enum MyCustomTaskEnum { + Something, + } + + #[pallet::tasks_experimental] + impl frame_support::traits::Task for MyCustomTaskEnum + where + T: TypeInfo, + {} + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + }; } #[test] fn test_parse_pallet_non_manual_task_enum_manual_impl() { - assert_pallet_parses! { - #[manifest_dir("../../examples/basic")] - #[frame_support::pallet] - pub mod pallet { - #[pallet::task_enum] - pub enum MyCustomTaskEnum { - Something, - } - - impl frame_support::traits::Task for MyCustomTaskEnum - where - T: TypeInfo, - {} - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(_); - } - }; + assert_pallet_parses! { + #[manifest_dir("../../examples/basic")] + #[frame_support::pallet] + pub mod pallet { + #[pallet::task_enum] + pub enum MyCustomTaskEnum { + Something, + } + + impl frame_support::traits::Task for MyCustomTaskEnum + where + T: TypeInfo, + {} + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + }; } #[test] fn test_parse_pallet_manual_task_enum_manual_impl() { - assert_pallet_parses! { - #[manifest_dir("../../examples/basic")] - #[frame_support::pallet] - pub mod pallet { - pub enum MyCustomTaskEnum { - Something, - } - - impl frame_support::traits::Task for MyCustomTaskEnum - where - T: TypeInfo, - {} - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(_); - } - }; + assert_pallet_parses! { + #[manifest_dir("../../examples/basic")] + #[frame_support::pallet] + pub mod pallet { + pub enum MyCustomTaskEnum { + Something, + } + + impl frame_support::traits::Task for MyCustomTaskEnum + where + T: TypeInfo, + {} + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + }; } #[test] fn test_parse_pallet_manual_task_enum_mismatch_ident() { - assert_pallet_parses! { - #[manifest_dir("../../examples/basic")] - #[frame_support::pallet] - pub mod pallet { - pub enum WrongIdent { - Something, - } - - #[pallet::tasks_experimental] - impl frame_support::traits::Task for MyCustomTaskEnum - where - T: TypeInfo, - {} - - #[pallet::config] - pub trait Config: frame_system::Config {} - - #[pallet::pallet] - pub struct Pallet(_); - } - }; + assert_pallet_parses! { + #[manifest_dir("../../examples/basic")] + #[frame_support::pallet] + pub mod pallet { + pub enum WrongIdent { + Something, + } + + #[pallet::tasks_experimental] + impl frame_support::traits::Task for MyCustomTaskEnum + where + T: TypeInfo, + {} + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + } + }; } diff --git a/support/procedural-fork/src/pallet/parse/type_value.rs b/support/procedural-fork/src/pallet/parse/type_value.rs index 4d9db30b3..d5c85248f 100644 --- a/support/procedural-fork/src/pallet/parse/type_value.rs +++ b/support/procedural-fork/src/pallet/parse/type_value.rs @@ -20,104 +20,104 @@ use syn::spanned::Spanned; /// Definition of type value. Just a function which is expanded to a struct implementing `Get`. pub struct TypeValueDef { - /// The index of error item in pallet module. - pub index: usize, - /// Visibility of the struct to generate. - pub vis: syn::Visibility, - /// Ident of the struct to generate. - pub ident: syn::Ident, - /// The type return by Get. - pub type_: Box, - /// The block returning the value to get - pub block: Box, - /// If type value is generic over `T` (or `T` and `I` for instantiable pallet) - pub is_generic: bool, - /// A set of usage of instance, must be check for consistency with config. - pub instances: Vec, - /// The where clause of the function. - pub where_clause: Option, - /// The span of the pallet::type_value attribute. - pub attr_span: proc_macro2::Span, - /// Docs on the item. - pub docs: Vec, + /// The index of error item in pallet module. + pub index: usize, + /// Visibility of the struct to generate. + pub vis: syn::Visibility, + /// Ident of the struct to generate. + pub ident: syn::Ident, + /// The type return by Get. + pub type_: Box, + /// The block returning the value to get + pub block: Box, + /// If type value is generic over `T` (or `T` and `I` for instantiable pallet) + pub is_generic: bool, + /// A set of usage of instance, must be check for consistency with config. + pub instances: Vec, + /// The where clause of the function. + pub where_clause: Option, + /// The span of the pallet::type_value attribute. + pub attr_span: proc_macro2::Span, + /// Docs on the item. + pub docs: Vec, } impl TypeValueDef { - pub fn try_from( - attr_span: proc_macro2::Span, - index: usize, - item: &mut syn::Item, - ) -> syn::Result { - let item = if let syn::Item::Fn(item) = item { - item - } else { - let msg = "Invalid pallet::type_value, expected item fn"; - return Err(syn::Error::new(item.span(), msg)) - }; + pub fn try_from( + attr_span: proc_macro2::Span, + index: usize, + item: &mut syn::Item, + ) -> syn::Result { + let item = if let syn::Item::Fn(item) = item { + item + } else { + let msg = "Invalid pallet::type_value, expected item fn"; + return Err(syn::Error::new(item.span(), msg)); + }; - let mut docs = vec![]; - for attr in &item.attrs { - if let syn::Meta::NameValue(meta) = &attr.meta { - if meta.path.get_ident().map_or(false, |ident| ident == "doc") { - docs.push(meta.value.clone()); - continue - } - } + let mut docs = vec![]; + for attr in &item.attrs { + if let syn::Meta::NameValue(meta) = &attr.meta { + if meta.path.get_ident().map_or(false, |ident| ident == "doc") { + docs.push(meta.value.clone()); + continue; + } + } - let msg = "Invalid pallet::type_value, unexpected attribute, only doc attribute are \ + let msg = "Invalid pallet::type_value, unexpected attribute, only doc attribute are \ allowed"; - return Err(syn::Error::new(attr.span(), msg)) - } + return Err(syn::Error::new(attr.span(), msg)); + } - if let Some(span) = item - .sig - .constness - .as_ref() - .map(|t| t.span()) - .or_else(|| item.sig.asyncness.as_ref().map(|t| t.span())) - .or_else(|| item.sig.unsafety.as_ref().map(|t| t.span())) - .or_else(|| item.sig.abi.as_ref().map(|t| t.span())) - .or_else(|| item.sig.variadic.as_ref().map(|t| t.span())) - { - let msg = "Invalid pallet::type_value, unexpected token"; - return Err(syn::Error::new(span, msg)) - } + if let Some(span) = item + .sig + .constness + .as_ref() + .map(|t| t.span()) + .or_else(|| item.sig.asyncness.as_ref().map(|t| t.span())) + .or_else(|| item.sig.unsafety.as_ref().map(|t| t.span())) + .or_else(|| item.sig.abi.as_ref().map(|t| t.span())) + .or_else(|| item.sig.variadic.as_ref().map(|t| t.span())) + { + let msg = "Invalid pallet::type_value, unexpected token"; + return Err(syn::Error::new(span, msg)); + } - if !item.sig.inputs.is_empty() { - let msg = "Invalid pallet::type_value, unexpected argument"; - return Err(syn::Error::new(item.sig.inputs[0].span(), msg)) - } + if !item.sig.inputs.is_empty() { + let msg = "Invalid pallet::type_value, unexpected argument"; + return Err(syn::Error::new(item.sig.inputs[0].span(), msg)); + } - let vis = item.vis.clone(); - let ident = item.sig.ident.clone(); - let block = item.block.clone(); - let type_ = match item.sig.output.clone() { - syn::ReturnType::Type(_, type_) => type_, - syn::ReturnType::Default => { - let msg = "Invalid pallet::type_value, expected return type"; - return Err(syn::Error::new(item.sig.span(), msg)) - }, - }; + let vis = item.vis.clone(); + let ident = item.sig.ident.clone(); + let block = item.block.clone(); + let type_ = match item.sig.output.clone() { + syn::ReturnType::Type(_, type_) => type_, + syn::ReturnType::Default => { + let msg = "Invalid pallet::type_value, expected return type"; + return Err(syn::Error::new(item.sig.span(), msg)); + } + }; - let mut instances = vec![]; - if let Some(usage) = helper::check_type_value_gen(&item.sig.generics, item.sig.span())? { - instances.push(usage); - } + let mut instances = vec![]; + if let Some(usage) = helper::check_type_value_gen(&item.sig.generics, item.sig.span())? { + instances.push(usage); + } - let is_generic = item.sig.generics.type_params().count() > 0; - let where_clause = item.sig.generics.where_clause.clone(); + let is_generic = item.sig.generics.type_params().count() > 0; + let where_clause = item.sig.generics.where_clause.clone(); - Ok(TypeValueDef { - attr_span, - index, - is_generic, - vis, - ident, - block, - type_, - instances, - where_clause, - docs, - }) - } + Ok(TypeValueDef { + attr_span, + index, + is_generic, + vis, + ident, + block, + type_, + instances, + where_clause, + docs, + }) + } } diff --git a/support/procedural-fork/src/pallet/parse/validate_unsigned.rs b/support/procedural-fork/src/pallet/parse/validate_unsigned.rs index 2bf0a1b6c..6e5109a74 100644 --- a/support/procedural-fork/src/pallet/parse/validate_unsigned.rs +++ b/support/procedural-fork/src/pallet/parse/validate_unsigned.rs @@ -20,43 +20,43 @@ use syn::spanned::Spanned; /// The definition of the pallet validate unsigned implementation. pub struct ValidateUnsignedDef { - /// The index of validate unsigned item in pallet module. - pub index: usize, - /// A set of usage of instance, must be check for consistency with config. - pub instances: Vec, + /// The index of validate unsigned item in pallet module. + pub index: usize, + /// A set of usage of instance, must be check for consistency with config. + pub instances: Vec, } impl ValidateUnsignedDef { - pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { - let item = if let syn::Item::Impl(item) = item { - item - } else { - let msg = "Invalid pallet::validate_unsigned, expected item impl"; - return Err(syn::Error::new(item.span(), msg)) - }; - - if item.trait_.is_none() { - let msg = "Invalid pallet::validate_unsigned, expected impl<..> ValidateUnsigned for \ + pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result { + let item = if let syn::Item::Impl(item) = item { + item + } else { + let msg = "Invalid pallet::validate_unsigned, expected item impl"; + return Err(syn::Error::new(item.span(), msg)); + }; + + if item.trait_.is_none() { + let msg = "Invalid pallet::validate_unsigned, expected impl<..> ValidateUnsigned for \ Pallet<..>"; - return Err(syn::Error::new(item.span(), msg)) - } - - if let Some(last) = item.trait_.as_ref().unwrap().1.segments.last() { - if last.ident != "ValidateUnsigned" { - let msg = "Invalid pallet::validate_unsigned, expected trait ValidateUnsigned"; - return Err(syn::Error::new(last.span(), msg)) - } - } else { - let msg = "Invalid pallet::validate_unsigned, expected impl<..> ValidateUnsigned for \ + return Err(syn::Error::new(item.span(), msg)); + } + + if let Some(last) = item.trait_.as_ref().unwrap().1.segments.last() { + if last.ident != "ValidateUnsigned" { + let msg = "Invalid pallet::validate_unsigned, expected trait ValidateUnsigned"; + return Err(syn::Error::new(last.span(), msg)); + } + } else { + let msg = "Invalid pallet::validate_unsigned, expected impl<..> ValidateUnsigned for \ Pallet<..>"; - return Err(syn::Error::new(item.span(), msg)) - } + return Err(syn::Error::new(item.span(), msg)); + } - let instances = vec![ - helper::check_pallet_struct_usage(&item.self_ty)?, - helper::check_impl_gen(&item.generics, item.impl_token.span())?, - ]; + let instances = vec![ + helper::check_pallet_struct_usage(&item.self_ty)?, + helper::check_impl_gen(&item.generics, item.impl_token.span())?, + ]; - Ok(ValidateUnsignedDef { index, instances }) - } + Ok(ValidateUnsignedDef { index, instances }) + } } diff --git a/support/procedural-fork/src/pallet_error.rs b/support/procedural-fork/src/pallet_error.rs index 693a1e982..bdf8330cd 100644 --- a/support/procedural-fork/src/pallet_error.rs +++ b/support/procedural-fork/src/pallet_error.rs @@ -20,159 +20,172 @@ use quote::ToTokens; // Derive `PalletError` pub fn derive_pallet_error(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let syn::DeriveInput { ident: name, generics, data, .. } = match syn::parse(input) { - Ok(input) => input, - Err(e) => return e.to_compile_error().into(), - }; - - let frame_support = match generate_access_from_frame_or_crate("frame-support") { - Ok(c) => c, - Err(e) => return e.into_compile_error().into(), - }; - let frame_support = &frame_support; - let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); - - let max_encoded_size = match data { - syn::Data::Struct(syn::DataStruct { fields, .. }) => match fields { - syn::Fields::Named(syn::FieldsNamed { named: fields, .. }) | - syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed: fields, .. }) => { - let maybe_field_tys = fields - .iter() - .map(|f| generate_field_types(f, &frame_support)) - .collect::>>(); - let field_tys = match maybe_field_tys { - Ok(tys) => tys.into_iter().flatten(), - Err(e) => return e.into_compile_error().into(), - }; - quote::quote! { - 0_usize - #( - .saturating_add(< - #field_tys as #frame_support::traits::PalletError - >::MAX_ENCODED_SIZE) - )* - } - }, - syn::Fields::Unit => quote::quote!(0), - }, - syn::Data::Enum(syn::DataEnum { variants, .. }) => { - let field_tys = variants - .iter() - .map(|variant| generate_variant_field_types(variant, &frame_support)) - .collect::>>, syn::Error>>(); - - let field_tys = match field_tys { - Ok(tys) => tys.into_iter().flatten().collect::>(), - Err(e) => return e.to_compile_error().into(), - }; - - // We start with `1`, because the discriminant of an enum is stored as u8 - if field_tys.is_empty() { - quote::quote!(1) - } else { - let variant_sizes = field_tys.into_iter().map(|variant_field_tys| { - quote::quote! { - 1_usize - #(.saturating_add(< - #variant_field_tys as #frame_support::traits::PalletError - >::MAX_ENCODED_SIZE))* - } - }); - - quote::quote! {{ - let mut size = 1_usize; - let mut tmp = 0_usize; - #( - tmp = #variant_sizes; - size = if tmp > size { tmp } else { size }; - tmp = 0_usize; - )* - size - }} - } - }, - syn::Data::Union(syn::DataUnion { union_token, .. }) => { - let msg = "Cannot derive `PalletError` for union; please implement it directly"; - return syn::Error::new(union_token.span, msg).into_compile_error().into() - }, - }; - - quote::quote!( - const _: () = { - impl #impl_generics #frame_support::traits::PalletError - for #name #ty_generics #where_clause - { - const MAX_ENCODED_SIZE: usize = #max_encoded_size; - } - }; - ) - .into() + let syn::DeriveInput { + ident: name, + generics, + data, + .. + } = match syn::parse(input) { + Ok(input) => input, + Err(e) => return e.to_compile_error().into(), + }; + + let frame_support = match generate_access_from_frame_or_crate("frame-support") { + Ok(c) => c, + Err(e) => return e.into_compile_error().into(), + }; + let frame_support = &frame_support; + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + let max_encoded_size = match data { + syn::Data::Struct(syn::DataStruct { fields, .. }) => match fields { + syn::Fields::Named(syn::FieldsNamed { named: fields, .. }) + | syn::Fields::Unnamed(syn::FieldsUnnamed { + unnamed: fields, .. + }) => { + let maybe_field_tys = fields + .iter() + .map(|f| generate_field_types(f, &frame_support)) + .collect::>>(); + let field_tys = match maybe_field_tys { + Ok(tys) => tys.into_iter().flatten(), + Err(e) => return e.into_compile_error().into(), + }; + quote::quote! { + 0_usize + #( + .saturating_add(< + #field_tys as #frame_support::traits::PalletError + >::MAX_ENCODED_SIZE) + )* + } + } + syn::Fields::Unit => quote::quote!(0), + }, + syn::Data::Enum(syn::DataEnum { variants, .. }) => { + let field_tys = variants + .iter() + .map(|variant| generate_variant_field_types(variant, &frame_support)) + .collect::>>, syn::Error>>(); + + let field_tys = match field_tys { + Ok(tys) => tys.into_iter().flatten().collect::>(), + Err(e) => return e.to_compile_error().into(), + }; + + // We start with `1`, because the discriminant of an enum is stored as u8 + if field_tys.is_empty() { + quote::quote!(1) + } else { + let variant_sizes = field_tys.into_iter().map(|variant_field_tys| { + quote::quote! { + 1_usize + #(.saturating_add(< + #variant_field_tys as #frame_support::traits::PalletError + >::MAX_ENCODED_SIZE))* + } + }); + + quote::quote! {{ + let mut size = 1_usize; + let mut tmp = 0_usize; + #( + tmp = #variant_sizes; + size = if tmp > size { tmp } else { size }; + tmp = 0_usize; + )* + size + }} + } + } + syn::Data::Union(syn::DataUnion { union_token, .. }) => { + let msg = "Cannot derive `PalletError` for union; please implement it directly"; + return syn::Error::new(union_token.span, msg) + .into_compile_error() + .into(); + } + }; + + quote::quote!( + const _: () = { + impl #impl_generics #frame_support::traits::PalletError + for #name #ty_generics #where_clause + { + const MAX_ENCODED_SIZE: usize = #max_encoded_size; + } + }; + ) + .into() } fn generate_field_types( - field: &syn::Field, - scrate: &syn::Path, + field: &syn::Field, + scrate: &syn::Path, ) -> syn::Result> { - let attrs = &field.attrs; - - for attr in attrs { - if attr.path().is_ident("codec") { - let mut res = None; - - attr.parse_nested_meta(|meta| { - if meta.path.is_ident("skip") { - res = Some(None); - } else if meta.path.is_ident("compact") { - let field_ty = &field.ty; - res = Some(Some(quote::quote!(#scrate::__private::codec::Compact<#field_ty>))); - } else if meta.path.is_ident("compact") { - res = Some(Some(meta.value()?.parse()?)); - } - - Ok(()) - })?; - - if let Some(v) = res { - return Ok(v) - } - } - } - - Ok(Some(field.ty.to_token_stream())) + let attrs = &field.attrs; + + for attr in attrs { + if attr.path().is_ident("codec") { + let mut res = None; + + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("skip") { + res = Some(None); + } else if meta.path.is_ident("compact") { + let field_ty = &field.ty; + res = Some(Some( + quote::quote!(#scrate::__private::codec::Compact<#field_ty>), + )); + } else if meta.path.is_ident("compact") { + res = Some(Some(meta.value()?.parse()?)); + } + + Ok(()) + })?; + + if let Some(v) = res { + return Ok(v); + } + } + } + + Ok(Some(field.ty.to_token_stream())) } fn generate_variant_field_types( - variant: &syn::Variant, - scrate: &syn::Path, + variant: &syn::Variant, + scrate: &syn::Path, ) -> syn::Result>> { - let attrs = &variant.attrs; - - for attr in attrs { - if attr.path().is_ident("codec") { - let mut skip = false; - - // We ignore the error intentionally as this isn't `codec(skip)` when - // `parse_nested_meta` fails. - let _ = attr.parse_nested_meta(|meta| { - skip = meta.path.is_ident("skip"); - Ok(()) - }); - - if skip { - return Ok(None) - } - } - } - - match &variant.fields { - syn::Fields::Named(syn::FieldsNamed { named: fields, .. }) | - syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed: fields, .. }) => { - let field_tys = fields - .iter() - .map(|field| generate_field_types(field, scrate)) - .collect::>>()?; - Ok(Some(field_tys.into_iter().flatten().collect())) - }, - syn::Fields::Unit => Ok(None), - } + let attrs = &variant.attrs; + + for attr in attrs { + if attr.path().is_ident("codec") { + let mut skip = false; + + // We ignore the error intentionally as this isn't `codec(skip)` when + // `parse_nested_meta` fails. + let _ = attr.parse_nested_meta(|meta| { + skip = meta.path.is_ident("skip"); + Ok(()) + }); + + if skip { + return Ok(None); + } + } + } + + match &variant.fields { + syn::Fields::Named(syn::FieldsNamed { named: fields, .. }) + | syn::Fields::Unnamed(syn::FieldsUnnamed { + unnamed: fields, .. + }) => { + let field_tys = fields + .iter() + .map(|field| generate_field_types(field, scrate)) + .collect::>>()?; + Ok(Some(field_tys.into_iter().flatten().collect())) + } + syn::Fields::Unit => Ok(None), + } } diff --git a/support/procedural-fork/src/runtime/expand/mod.rs b/support/procedural-fork/src/runtime/expand/mod.rs index 93c88fce9..c26cbccb7 100644 --- a/support/procedural-fork/src/runtime/expand/mod.rs +++ b/support/procedural-fork/src/runtime/expand/mod.rs @@ -17,20 +17,20 @@ use super::parse::runtime_types::RuntimeType; use crate::{ - construct_runtime::{ - check_pallet_number, decl_all_pallets, decl_integrity_test, decl_pallet_runtime_setup, - decl_static_assertions, expand, - }, - runtime::{ - parse::{ - AllPalletsDeclaration, ExplicitAllPalletsDeclaration, ImplicitAllPalletsDeclaration, - }, - Def, - }, + construct_runtime::{ + check_pallet_number, decl_all_pallets, decl_integrity_test, decl_pallet_runtime_setup, + decl_static_assertions, expand, + }, + runtime::{ + parse::{ + AllPalletsDeclaration, ExplicitAllPalletsDeclaration, ImplicitAllPalletsDeclaration, + }, + Def, + }, }; use cfg_expr::Predicate; use frame_support_procedural_tools::{ - generate_access_from_frame_or_crate, generate_crate_access, generate_hidden_includes, + generate_access_from_frame_or_crate, generate_crate_access, generate_hidden_includes, }; use proc_macro2::TokenStream as TokenStream2; use quote::quote; @@ -41,280 +41,300 @@ use syn::{Ident, Result}; const SYSTEM_PALLET_NAME: &str = "System"; pub fn expand(def: Def, legacy_ordering: bool) -> TokenStream2 { - let input = def.input; - - let (check_pallet_number_res, res) = match def.pallets { - AllPalletsDeclaration::Implicit(ref decl) => ( - check_pallet_number(input.clone(), decl.pallet_count), - construct_runtime_implicit_to_explicit(input.into(), decl.clone(), legacy_ordering), - ), - AllPalletsDeclaration::Explicit(ref decl) => ( - check_pallet_number(input, decl.pallets.len()), - construct_runtime_final_expansion( - def.runtime_struct.ident.clone(), - decl.clone(), - def.runtime_types.clone(), - legacy_ordering, - ), - ), - }; - - let res = res.unwrap_or_else(|e| e.to_compile_error()); - - // We want to provide better error messages to the user and thus, handle the error here - // separately. If there is an error, we print the error and still generate all of the code to - // get in overall less errors for the user. - let res = if let Err(error) = check_pallet_number_res { - let error = error.to_compile_error(); - - quote! { - #error - - #res - } - } else { - res - }; - - let res = expander::Expander::new("construct_runtime") - .dry(std::env::var("FRAME_EXPAND").is_err()) - .verbose(true) - .write_to_out_dir(res) - .expect("Does not fail because of IO in OUT_DIR; qed"); - - res.into() + let input = def.input; + + let (check_pallet_number_res, res) = match def.pallets { + AllPalletsDeclaration::Implicit(ref decl) => ( + check_pallet_number(input.clone(), decl.pallet_count), + construct_runtime_implicit_to_explicit(input.into(), decl.clone(), legacy_ordering), + ), + AllPalletsDeclaration::Explicit(ref decl) => ( + check_pallet_number(input, decl.pallets.len()), + construct_runtime_final_expansion( + def.runtime_struct.ident.clone(), + decl.clone(), + def.runtime_types.clone(), + legacy_ordering, + ), + ), + }; + + let res = res.unwrap_or_else(|e| e.to_compile_error()); + + // We want to provide better error messages to the user and thus, handle the error here + // separately. If there is an error, we print the error and still generate all of the code to + // get in overall less errors for the user. + let res = if let Err(error) = check_pallet_number_res { + let error = error.to_compile_error(); + + quote! { + #error + + #res + } + } else { + res + }; + + let res = expander::Expander::new("construct_runtime") + .dry(std::env::var("FRAME_EXPAND").is_err()) + .verbose(true) + .write_to_out_dir(res) + .expect("Does not fail because of IO in OUT_DIR; qed"); + + res.into() } fn construct_runtime_implicit_to_explicit( - input: TokenStream2, - definition: ImplicitAllPalletsDeclaration, - legacy_ordering: bool, + input: TokenStream2, + definition: ImplicitAllPalletsDeclaration, + legacy_ordering: bool, ) -> Result { - let frame_support = generate_access_from_frame_or_crate("frame-support")?; - let attr = if legacy_ordering { quote!((legacy_ordering)) } else { quote!() }; - let mut expansion = quote::quote!( - #[frame_support::runtime #attr] - #input - ); - for pallet in definition.pallet_decls.iter() { - let pallet_path = &pallet.path; - let pallet_name = &pallet.name; - let pallet_instance = pallet.instance.as_ref().map(|instance| quote::quote!(<#instance>)); - expansion = quote::quote!( - #frame_support::__private::tt_call! { - macro = [{ #pallet_path::tt_default_parts_v2 }] - frame_support = [{ #frame_support }] - ~~> #frame_support::match_and_insert! { - target = [{ #expansion }] - pattern = [{ #pallet_name = #pallet_path #pallet_instance }] - } - } - ); - } - - Ok(expansion) + let frame_support = generate_access_from_frame_or_crate("frame-support")?; + let attr = if legacy_ordering { + quote!((legacy_ordering)) + } else { + quote!() + }; + let mut expansion = quote::quote!( + #[frame_support::runtime #attr] + #input + ); + for pallet in definition.pallet_decls.iter() { + let pallet_path = &pallet.path; + let pallet_name = &pallet.name; + let pallet_instance = pallet + .instance + .as_ref() + .map(|instance| quote::quote!(<#instance>)); + expansion = quote::quote!( + #frame_support::__private::tt_call! { + macro = [{ #pallet_path::tt_default_parts_v2 }] + frame_support = [{ #frame_support }] + ~~> #frame_support::match_and_insert! { + target = [{ #expansion }] + pattern = [{ #pallet_name = #pallet_path #pallet_instance }] + } + } + ); + } + + Ok(expansion) } fn construct_runtime_final_expansion( - name: Ident, - definition: ExplicitAllPalletsDeclaration, - runtime_types: Vec, - legacy_ordering: bool, + name: Ident, + definition: ExplicitAllPalletsDeclaration, + runtime_types: Vec, + legacy_ordering: bool, ) -> Result { - let ExplicitAllPalletsDeclaration { mut pallets, name: pallets_name } = definition; - - if !legacy_ordering { - // Ensure that order of hooks is based on the pallet index - pallets.sort_by_key(|p| p.index); - } - - let system_pallet = - pallets.iter().find(|decl| decl.name == SYSTEM_PALLET_NAME).ok_or_else(|| { - syn::Error::new( - pallets_name.span(), - "`System` pallet declaration is missing. \ + let ExplicitAllPalletsDeclaration { + mut pallets, + name: pallets_name, + } = definition; + + if !legacy_ordering { + // Ensure that order of hooks is based on the pallet index + pallets.sort_by_key(|p| p.index); + } + + let system_pallet = pallets + .iter() + .find(|decl| decl.name == SYSTEM_PALLET_NAME) + .ok_or_else(|| { + syn::Error::new( + pallets_name.span(), + "`System` pallet declaration is missing. \ Please add this line: `pub type System = frame_system;`", - ) - })?; - if !system_pallet.cfg_pattern.is_empty() { - return Err(syn::Error::new( - system_pallet.name.span(), - "`System` pallet declaration is feature gated, please remove any `#[cfg]` attributes", - )) - } - - let features = pallets - .iter() - .filter_map(|decl| { - (!decl.cfg_pattern.is_empty()).then(|| { - decl.cfg_pattern.iter().flat_map(|attr| { - attr.predicates().filter_map(|pred| match pred { - Predicate::Feature(feat) => Some(feat), - Predicate::Test => Some("test"), - _ => None, - }) - }) - }) - }) - .flatten() - .collect::>(); - - 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 frame_system = generate_access_from_frame_or_crate("frame-system")?; - let block = quote!(<#name as #frame_system::Config>::Block); - let unchecked_extrinsic = quote!(<#block as #scrate::sp_runtime::traits::Block>::Extrinsic); - - let mut dispatch = None; - let mut outer_event = None; - let mut outer_error = None; - let mut outer_origin = None; - let mut freeze_reason = None; - let mut hold_reason = None; - let mut slash_reason = None; - let mut lock_id = None; - let mut task = None; - - for runtime_type in runtime_types.iter() { - match runtime_type { - RuntimeType::RuntimeCall(_) => { - dispatch = - Some(expand::expand_outer_dispatch(&name, system_pallet, &pallets, &scrate)); - }, - RuntimeType::RuntimeEvent(_) => { - outer_event = Some(expand::expand_outer_enum( - &name, - &pallets, - &scrate, - expand::OuterEnumType::Event, - )?); - }, - RuntimeType::RuntimeError(_) => { - outer_error = Some(expand::expand_outer_enum( - &name, - &pallets, - &scrate, - expand::OuterEnumType::Error, - )?); - }, - RuntimeType::RuntimeOrigin(_) => { - outer_origin = - Some(expand::expand_outer_origin(&name, system_pallet, &pallets, &scrate)?); - }, - RuntimeType::RuntimeFreezeReason(_) => { - freeze_reason = Some(expand::expand_outer_freeze_reason(&pallets, &scrate)); - }, - RuntimeType::RuntimeHoldReason(_) => { - hold_reason = Some(expand::expand_outer_hold_reason(&pallets, &scrate)); - }, - RuntimeType::RuntimeSlashReason(_) => { - slash_reason = Some(expand::expand_outer_slash_reason(&pallets, &scrate)); - }, - RuntimeType::RuntimeLockId(_) => { - lock_id = Some(expand::expand_outer_lock_id(&pallets, &scrate)); - }, - RuntimeType::RuntimeTask(_) => { - task = Some(expand::expand_outer_task(&name, &pallets, &scrate)); - }, - } - } - - let all_pallets = decl_all_pallets(&name, pallets.iter(), &features); - let pallet_to_index = decl_pallet_runtime_setup(&name, &pallets, &scrate); - - let metadata = expand::expand_runtime_metadata( - &name, - &pallets, - &scrate, - &unchecked_extrinsic, - &system_pallet.path, - ); - let outer_config = expand::expand_outer_config(&name, &pallets, &scrate); - let inherent = - expand::expand_outer_inherent(&name, &block, &unchecked_extrinsic, &pallets, &scrate); - let validate_unsigned = expand::expand_outer_validate_unsigned(&name, &pallets, &scrate); - let integrity_test = decl_integrity_test(&scrate); - let static_assertions = decl_static_assertions(&name, &pallets, &scrate); - - let res = quote!( - #scrate_decl - - // Prevent UncheckedExtrinsic to print unused warning. - const _: () = { - #[allow(unused)] - type __hidden_use_of_unchecked_extrinsic = #unchecked_extrinsic; - }; - - #[derive( - Clone, Copy, PartialEq, Eq, #scrate::sp_runtime::RuntimeDebug, - #scrate::__private::scale_info::TypeInfo - )] - pub struct #name; - impl #scrate::sp_runtime::traits::GetRuntimeBlockType for #name { - type RuntimeBlock = #block; - } - - // Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata. - // The function is implemented by calling `impl_runtime_apis!`. - // - // However, the `runtime` may be used without calling `impl_runtime_apis!`. - // Rely on the `Deref` trait to differentiate between a runtime that implements - // APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro runtime). - // - // Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function. - // `InternalConstructRuntime` is implemented by the `runtime` for Runtime references (`& Runtime`), - // while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`). - // - // Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!` - // when both macros are called; and will resolve an empty `runtime_metadata` when only the `runtime` - // is used. - - #[doc(hidden)] - trait InternalConstructRuntime { - #[inline(always)] - fn runtime_metadata(&self) -> #scrate::__private::sp_std::vec::Vec<#scrate::__private::metadata_ir::RuntimeApiMetadataIR> { - Default::default() - } - } - #[doc(hidden)] - impl InternalConstructRuntime for &#name {} - - #outer_event - - #outer_error - - #outer_origin - - #all_pallets - - #pallet_to_index - - #dispatch - - #task - - #metadata - - #outer_config - - #inherent - - #validate_unsigned - - #freeze_reason - - #hold_reason - - #lock_id - - #slash_reason - - #integrity_test - - #static_assertions - ); + ) + })?; + if !system_pallet.cfg_pattern.is_empty() { + return Err(syn::Error::new( + system_pallet.name.span(), + "`System` pallet declaration is feature gated, please remove any `#[cfg]` attributes", + )); + } + + let features = pallets + .iter() + .filter_map(|decl| { + (!decl.cfg_pattern.is_empty()).then(|| { + decl.cfg_pattern.iter().flat_map(|attr| { + attr.predicates().filter_map(|pred| match pred { + Predicate::Feature(feat) => Some(feat), + Predicate::Test => Some("test"), + _ => None, + }) + }) + }) + }) + .flatten() + .collect::>(); + + 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 frame_system = generate_access_from_frame_or_crate("frame-system")?; + let block = quote!(<#name as #frame_system::Config>::Block); + let unchecked_extrinsic = quote!(<#block as #scrate::sp_runtime::traits::Block>::Extrinsic); + + let mut dispatch = None; + let mut outer_event = None; + let mut outer_error = None; + let mut outer_origin = None; + let mut freeze_reason = None; + let mut hold_reason = None; + let mut slash_reason = None; + let mut lock_id = None; + let mut task = None; + + for runtime_type in runtime_types.iter() { + match runtime_type { + RuntimeType::RuntimeCall(_) => { + dispatch = Some(expand::expand_outer_dispatch( + &name, + system_pallet, + &pallets, + &scrate, + )); + } + RuntimeType::RuntimeEvent(_) => { + outer_event = Some(expand::expand_outer_enum( + &name, + &pallets, + &scrate, + expand::OuterEnumType::Event, + )?); + } + RuntimeType::RuntimeError(_) => { + outer_error = Some(expand::expand_outer_enum( + &name, + &pallets, + &scrate, + expand::OuterEnumType::Error, + )?); + } + RuntimeType::RuntimeOrigin(_) => { + outer_origin = Some(expand::expand_outer_origin( + &name, + system_pallet, + &pallets, + &scrate, + )?); + } + RuntimeType::RuntimeFreezeReason(_) => { + freeze_reason = Some(expand::expand_outer_freeze_reason(&pallets, &scrate)); + } + RuntimeType::RuntimeHoldReason(_) => { + hold_reason = Some(expand::expand_outer_hold_reason(&pallets, &scrate)); + } + RuntimeType::RuntimeSlashReason(_) => { + slash_reason = Some(expand::expand_outer_slash_reason(&pallets, &scrate)); + } + RuntimeType::RuntimeLockId(_) => { + lock_id = Some(expand::expand_outer_lock_id(&pallets, &scrate)); + } + RuntimeType::RuntimeTask(_) => { + task = Some(expand::expand_outer_task(&name, &pallets, &scrate)); + } + } + } + + let all_pallets = decl_all_pallets(&name, pallets.iter(), &features); + let pallet_to_index = decl_pallet_runtime_setup(&name, &pallets, &scrate); + + let metadata = expand::expand_runtime_metadata( + &name, + &pallets, + &scrate, + &unchecked_extrinsic, + &system_pallet.path, + ); + let outer_config = expand::expand_outer_config(&name, &pallets, &scrate); + let inherent = + expand::expand_outer_inherent(&name, &block, &unchecked_extrinsic, &pallets, &scrate); + let validate_unsigned = expand::expand_outer_validate_unsigned(&name, &pallets, &scrate); + let integrity_test = decl_integrity_test(&scrate); + let static_assertions = decl_static_assertions(&name, &pallets, &scrate); + + let res = quote!( + #scrate_decl + + // Prevent UncheckedExtrinsic to print unused warning. + const _: () = { + #[allow(unused)] + type __hidden_use_of_unchecked_extrinsic = #unchecked_extrinsic; + }; + + #[derive( + Clone, Copy, PartialEq, Eq, #scrate::sp_runtime::RuntimeDebug, + #scrate::__private::scale_info::TypeInfo + )] + pub struct #name; + impl #scrate::sp_runtime::traits::GetRuntimeBlockType for #name { + type RuntimeBlock = #block; + } + + // Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata. + // The function is implemented by calling `impl_runtime_apis!`. + // + // However, the `runtime` may be used without calling `impl_runtime_apis!`. + // Rely on the `Deref` trait to differentiate between a runtime that implements + // APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro runtime). + // + // Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function. + // `InternalConstructRuntime` is implemented by the `runtime` for Runtime references (`& Runtime`), + // while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`). + // + // Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!` + // when both macros are called; and will resolve an empty `runtime_metadata` when only the `runtime` + // is used. + + #[doc(hidden)] + trait InternalConstructRuntime { + #[inline(always)] + fn runtime_metadata(&self) -> #scrate::__private::sp_std::vec::Vec<#scrate::__private::metadata_ir::RuntimeApiMetadataIR> { + Default::default() + } + } + #[doc(hidden)] + impl InternalConstructRuntime for &#name {} + + #outer_event + + #outer_error + + #outer_origin + + #all_pallets + + #pallet_to_index + + #dispatch + + #task + + #metadata + + #outer_config + + #inherent + + #validate_unsigned + + #freeze_reason + + #hold_reason + + #lock_id + + #slash_reason + + #integrity_test + + #static_assertions + ); - Ok(res) + Ok(res) } diff --git a/support/procedural-fork/src/runtime/mod.rs b/support/procedural-fork/src/runtime/mod.rs index aaae579eb..589acff6c 100644 --- a/support/procedural-fork/src/runtime/mod.rs +++ b/support/procedural-fork/src/runtime/mod.rs @@ -210,27 +210,27 @@ mod expand; mod parse; mod keyword { - syn::custom_keyword!(legacy_ordering); + syn::custom_keyword!(legacy_ordering); } pub fn runtime(attr: TokenStream, tokens: TokenStream) -> TokenStream { - let mut legacy_ordering = false; - if !attr.is_empty() { - if let Ok(_) = syn::parse::(attr.clone()) { - legacy_ordering = true; - } else { - let msg = "Invalid runtime macro call: unexpected attribute. Macro call must be \ + let mut legacy_ordering = false; + if !attr.is_empty() { + if let Ok(_) = syn::parse::(attr.clone()) { + legacy_ordering = true; + } else { + let msg = "Invalid runtime macro call: unexpected attribute. Macro call must be \ bare, such as `#[frame_support::runtime]` or `#[runtime]`, or must specify the \ `legacy_ordering` attribute, such as `#[frame_support::runtime(legacy_ordering)]` or \ #[runtime(legacy_ordering)]."; - let span = proc_macro2::TokenStream::from(attr).span(); - return syn::Error::new(span, msg).to_compile_error().into() - } - } + let span = proc_macro2::TokenStream::from(attr).span(); + return syn::Error::new(span, msg).to_compile_error().into(); + } + } - let item = syn::parse_macro_input!(tokens as syn::ItemMod); - match parse::Def::try_from(item) { - Ok(def) => expand::expand(def, legacy_ordering).into(), - Err(e) => e.to_compile_error().into(), - } + let item = syn::parse_macro_input!(tokens as syn::ItemMod); + match parse::Def::try_from(item) { + Ok(def) => expand::expand(def, legacy_ordering).into(), + Err(e) => e.to_compile_error().into(), + } } diff --git a/support/procedural-fork/src/runtime/parse/helper.rs b/support/procedural-fork/src/runtime/parse/helper.rs index f05395f9b..17e362410 100644 --- a/support/procedural-fork/src/runtime/parse/helper.rs +++ b/support/procedural-fork/src/runtime/parse/helper.rs @@ -19,19 +19,26 @@ use crate::pallet::parse::helper::MutItemAttrs; use quote::ToTokens; pub(crate) fn take_first_item_runtime_attr( - item: &mut impl MutItemAttrs, + item: &mut impl MutItemAttrs, ) -> syn::Result> where - Attr: syn::parse::Parse, + Attr: syn::parse::Parse, { - let attrs = if let Some(attrs) = item.mut_item_attrs() { attrs } else { return Ok(None) }; + let attrs = if let Some(attrs) = item.mut_item_attrs() { + attrs + } else { + return Ok(None); + }; - if let Some(index) = attrs.iter().position(|attr| { - attr.path().segments.first().map_or(false, |segment| segment.ident == "runtime") - }) { - let runtime_attr = attrs.remove(index); - Ok(Some(syn::parse2(runtime_attr.into_token_stream())?)) - } else { - Ok(None) - } + if let Some(index) = attrs.iter().position(|attr| { + attr.path() + .segments + .first() + .map_or(false, |segment| segment.ident == "runtime") + }) { + let runtime_attr = attrs.remove(index); + Ok(Some(syn::parse2(runtime_attr.into_token_stream())?)) + } else { + Ok(None) + } } diff --git a/support/procedural-fork/src/runtime/parse/mod.rs b/support/procedural-fork/src/runtime/parse/mod.rs index 893cb4726..79cf894e8 100644 --- a/support/procedural-fork/src/runtime/parse/mod.rs +++ b/support/procedural-fork/src/runtime/parse/mod.rs @@ -32,235 +32,244 @@ use frame_support_procedural_tools::syn_ext as ext; use runtime_types::RuntimeType; mod keyword { - use syn::custom_keyword; + use syn::custom_keyword; - custom_keyword!(runtime); - custom_keyword!(derive); - custom_keyword!(pallet_index); - custom_keyword!(disable_call); - custom_keyword!(disable_unsigned); + custom_keyword!(runtime); + custom_keyword!(derive); + custom_keyword!(pallet_index); + custom_keyword!(disable_call); + custom_keyword!(disable_unsigned); } enum RuntimeAttr { - Runtime(proc_macro2::Span), - Derive(proc_macro2::Span, Vec), - PalletIndex(proc_macro2::Span, u8), - DisableCall(proc_macro2::Span), - DisableUnsigned(proc_macro2::Span), + Runtime(proc_macro2::Span), + Derive(proc_macro2::Span, Vec), + PalletIndex(proc_macro2::Span, u8), + DisableCall(proc_macro2::Span), + DisableUnsigned(proc_macro2::Span), } impl RuntimeAttr { - fn span(&self) -> proc_macro2::Span { - match self { - Self::Runtime(span) => *span, - Self::Derive(span, _) => *span, - Self::PalletIndex(span, _) => *span, - Self::DisableCall(span) => *span, - Self::DisableUnsigned(span) => *span, - } - } + fn span(&self) -> proc_macro2::Span { + match self { + Self::Runtime(span) => *span, + Self::Derive(span, _) => *span, + Self::PalletIndex(span, _) => *span, + Self::DisableCall(span) => *span, + Self::DisableUnsigned(span) => *span, + } + } } impl syn::parse::Parse for RuntimeAttr { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - input.parse::()?; - let content; - syn::bracketed!(content in input); - content.parse::()?; - content.parse::()?; + fn parse(input: syn::parse::ParseStream) -> syn::Result { + input.parse::()?; + let content; + syn::bracketed!(content in input); + content.parse::()?; + content.parse::()?; - let lookahead = content.lookahead1(); - if lookahead.peek(keyword::runtime) { - Ok(RuntimeAttr::Runtime(content.parse::()?.span())) - } else if lookahead.peek(keyword::derive) { - let _ = content.parse::(); - let derive_content; - syn::parenthesized!(derive_content in content); - let runtime_types = - derive_content.parse::>()?; - let runtime_types = runtime_types.inner.into_iter().collect(); - Ok(RuntimeAttr::Derive(derive_content.span(), runtime_types)) - } else if lookahead.peek(keyword::pallet_index) { - let _ = content.parse::(); - let pallet_index_content; - syn::parenthesized!(pallet_index_content in content); - let pallet_index = pallet_index_content.parse::()?; - if !pallet_index.suffix().is_empty() { - let msg = "Number literal must not have a suffix"; - return Err(syn::Error::new(pallet_index.span(), msg)) - } - Ok(RuntimeAttr::PalletIndex(pallet_index.span(), pallet_index.base10_parse()?)) - } else if lookahead.peek(keyword::disable_call) { - Ok(RuntimeAttr::DisableCall(content.parse::()?.span())) - } else if lookahead.peek(keyword::disable_unsigned) { - Ok(RuntimeAttr::DisableUnsigned(content.parse::()?.span())) - } else { - Err(lookahead.error()) - } - } + let lookahead = content.lookahead1(); + if lookahead.peek(keyword::runtime) { + Ok(RuntimeAttr::Runtime( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::derive) { + let _ = content.parse::(); + let derive_content; + syn::parenthesized!(derive_content in content); + let runtime_types = + derive_content.parse::>()?; + let runtime_types = runtime_types.inner.into_iter().collect(); + Ok(RuntimeAttr::Derive(derive_content.span(), runtime_types)) + } else if lookahead.peek(keyword::pallet_index) { + let _ = content.parse::(); + let pallet_index_content; + syn::parenthesized!(pallet_index_content in content); + let pallet_index = pallet_index_content.parse::()?; + if !pallet_index.suffix().is_empty() { + let msg = "Number literal must not have a suffix"; + return Err(syn::Error::new(pallet_index.span(), msg)); + } + Ok(RuntimeAttr::PalletIndex( + pallet_index.span(), + pallet_index.base10_parse()?, + )) + } else if lookahead.peek(keyword::disable_call) { + Ok(RuntimeAttr::DisableCall( + content.parse::()?.span(), + )) + } else if lookahead.peek(keyword::disable_unsigned) { + Ok(RuntimeAttr::DisableUnsigned( + content.parse::()?.span(), + )) + } else { + Err(lookahead.error()) + } + } } #[derive(Debug, Clone)] pub enum AllPalletsDeclaration { - Implicit(ImplicitAllPalletsDeclaration), - Explicit(ExplicitAllPalletsDeclaration), + Implicit(ImplicitAllPalletsDeclaration), + Explicit(ExplicitAllPalletsDeclaration), } /// Declaration of a runtime with some pallet with implicit declaration of parts. #[derive(Debug, Clone)] pub struct ImplicitAllPalletsDeclaration { - pub name: Ident, - pub pallet_decls: Vec, - pub pallet_count: usize, + pub name: Ident, + pub pallet_decls: Vec, + pub pallet_count: usize, } /// Declaration of a runtime with all pallet having explicit declaration of parts. #[derive(Debug, Clone)] pub struct ExplicitAllPalletsDeclaration { - pub name: Ident, - pub pallets: Vec, + pub name: Ident, + pub pallets: Vec, } pub struct Def { - pub input: TokenStream2, - pub item: syn::ItemMod, - pub runtime_struct: runtime_struct::RuntimeStructDef, - pub pallets: AllPalletsDeclaration, - pub runtime_types: Vec, + pub input: TokenStream2, + pub item: syn::ItemMod, + pub runtime_struct: runtime_struct::RuntimeStructDef, + pub pallets: AllPalletsDeclaration, + pub runtime_types: Vec, } impl Def { - pub fn try_from(mut item: syn::ItemMod) -> syn::Result { - let input: TokenStream2 = item.to_token_stream().into(); - let item_span = item.span(); - let items = &mut item - .content - .as_mut() - .ok_or_else(|| { - let msg = "Invalid runtime definition, expected mod to be inlined."; - syn::Error::new(item_span, msg) - })? - .1; + pub fn try_from(mut item: syn::ItemMod) -> syn::Result { + let input: TokenStream2 = item.to_token_stream().into(); + let item_span = item.span(); + let items = &mut item + .content + .as_mut() + .ok_or_else(|| { + let msg = "Invalid runtime definition, expected mod to be inlined."; + syn::Error::new(item_span, msg) + })? + .1; - let mut runtime_struct = None; - let mut runtime_types = None; + let mut runtime_struct = None; + let mut runtime_types = None; - let mut indices = HashMap::new(); - let mut names = HashMap::new(); + let mut indices = HashMap::new(); + let mut names = HashMap::new(); - let mut pallet_decls = vec![]; - let mut pallets = vec![]; + let mut pallet_decls = vec![]; + let mut pallets = vec![]; - for item in items.iter_mut() { - let mut pallet_item = None; - let mut pallet_index = 0; + for item in items.iter_mut() { + let mut pallet_item = None; + let mut pallet_index = 0; - let mut disable_call = false; - let mut disable_unsigned = false; + let mut disable_call = false; + let mut disable_unsigned = false; - while let Some(runtime_attr) = - helper::take_first_item_runtime_attr::(item)? - { - match runtime_attr { - RuntimeAttr::Runtime(span) if runtime_struct.is_none() => { - let p = runtime_struct::RuntimeStructDef::try_from(span, item)?; - runtime_struct = Some(p); - }, - RuntimeAttr::Derive(_, types) if runtime_types.is_none() => { - runtime_types = Some(types); - }, - RuntimeAttr::PalletIndex(span, index) => { - pallet_index = index; - pallet_item = if let syn::Item::Type(item) = item { - Some(item.clone()) - } else { - let msg = "Invalid runtime::pallet_index, expected type definition"; - return Err(syn::Error::new(span, msg)) - }; - }, - RuntimeAttr::DisableCall(_) => disable_call = true, - RuntimeAttr::DisableUnsigned(_) => disable_unsigned = true, - attr => { - let msg = "Invalid duplicated attribute"; - return Err(syn::Error::new(attr.span(), msg)) - }, - } - } + while let Some(runtime_attr) = + helper::take_first_item_runtime_attr::(item)? + { + match runtime_attr { + RuntimeAttr::Runtime(span) if runtime_struct.is_none() => { + let p = runtime_struct::RuntimeStructDef::try_from(span, item)?; + runtime_struct = Some(p); + } + RuntimeAttr::Derive(_, types) if runtime_types.is_none() => { + runtime_types = Some(types); + } + RuntimeAttr::PalletIndex(span, index) => { + pallet_index = index; + pallet_item = if let syn::Item::Type(item) = item { + Some(item.clone()) + } else { + let msg = "Invalid runtime::pallet_index, expected type definition"; + return Err(syn::Error::new(span, msg)); + }; + } + RuntimeAttr::DisableCall(_) => disable_call = true, + RuntimeAttr::DisableUnsigned(_) => disable_unsigned = true, + attr => { + let msg = "Invalid duplicated attribute"; + return Err(syn::Error::new(attr.span(), msg)); + } + } + } - if let Some(pallet_item) = pallet_item { - match *pallet_item.ty.clone() { - syn::Type::Path(ref path) => { - let pallet_decl = - PalletDeclaration::try_from(item.span(), &pallet_item, path)?; + if let Some(pallet_item) = pallet_item { + match *pallet_item.ty.clone() { + syn::Type::Path(ref path) => { + let pallet_decl = + PalletDeclaration::try_from(item.span(), &pallet_item, path)?; - if let Some(used_pallet) = - names.insert(pallet_decl.name.clone(), pallet_decl.name.span()) - { - let msg = "Two pallets with the same name!"; + if let Some(used_pallet) = + names.insert(pallet_decl.name.clone(), pallet_decl.name.span()) + { + let msg = "Two pallets with the same name!"; - let mut err = syn::Error::new(used_pallet, &msg); - err.combine(syn::Error::new(pallet_decl.name.span(), &msg)); - return Err(err) - } + let mut err = syn::Error::new(used_pallet, &msg); + err.combine(syn::Error::new(pallet_decl.name.span(), &msg)); + return Err(err); + } - pallet_decls.push(pallet_decl); - }, - syn::Type::TraitObject(syn::TypeTraitObject { bounds, .. }) => { - let pallet = Pallet::try_from( - item.span(), - &pallet_item, - pallet_index, - disable_call, - disable_unsigned, - &bounds, - )?; + pallet_decls.push(pallet_decl); + } + syn::Type::TraitObject(syn::TypeTraitObject { bounds, .. }) => { + let pallet = Pallet::try_from( + item.span(), + &pallet_item, + pallet_index, + disable_call, + disable_unsigned, + &bounds, + )?; - if let Some(used_pallet) = indices.insert(pallet.index, pallet.name.clone()) - { - let msg = format!( + if let Some(used_pallet) = indices.insert(pallet.index, pallet.name.clone()) + { + let msg = format!( "Pallet indices are conflicting: Both pallets {} and {} are at index {}", used_pallet, pallet.name, pallet.index, ); - let mut err = syn::Error::new(used_pallet.span(), &msg); - err.combine(syn::Error::new(pallet.name.span(), msg)); - return Err(err) - } + let mut err = syn::Error::new(used_pallet.span(), &msg); + err.combine(syn::Error::new(pallet.name.span(), msg)); + return Err(err); + } - pallets.push(pallet); - }, - _ => continue, - } - } - } + pallets.push(pallet); + } + _ => continue, + } + } + } - let name = item.ident.clone(); - let decl_count = pallet_decls.len(); - let pallets = if decl_count > 0 { - AllPalletsDeclaration::Implicit(ImplicitAllPalletsDeclaration { - name, - pallet_decls, - pallet_count: decl_count.saturating_add(pallets.len()), - }) - } else { - AllPalletsDeclaration::Explicit(ExplicitAllPalletsDeclaration { name, pallets }) - }; + let name = item.ident.clone(); + let decl_count = pallet_decls.len(); + let pallets = if decl_count > 0 { + AllPalletsDeclaration::Implicit(ImplicitAllPalletsDeclaration { + name, + pallet_decls, + pallet_count: decl_count.saturating_add(pallets.len()), + }) + } else { + AllPalletsDeclaration::Explicit(ExplicitAllPalletsDeclaration { name, pallets }) + }; - let def = Def { - input, - item, - runtime_struct: runtime_struct.ok_or_else(|| { - syn::Error::new(item_span, + let def = Def { + input, + item, + runtime_struct: runtime_struct.ok_or_else(|| { + syn::Error::new(item_span, "Missing Runtime. Please add a struct inside the module and annotate it with `#[runtime::runtime]`" ) - })?, - pallets, - runtime_types: runtime_types.ok_or_else(|| { - syn::Error::new(item_span, + })?, + pallets, + runtime_types: runtime_types.ok_or_else(|| { + syn::Error::new(item_span, "Missing Runtime Types. Please annotate the runtime struct with `#[runtime::derive]`" ) - })?, - }; + })?, + }; - Ok(def) - } + Ok(def) + } } diff --git a/support/procedural-fork/src/runtime/parse/pallet.rs b/support/procedural-fork/src/runtime/parse/pallet.rs index d2f1857fb..039e2631b 100644 --- a/support/procedural-fork/src/runtime/parse/pallet.rs +++ b/support/procedural-fork/src/runtime/parse/pallet.rs @@ -20,80 +20,88 @@ use quote::ToTokens; use syn::{punctuated::Punctuated, spanned::Spanned, token, Error, Ident, PathArguments}; impl Pallet { - pub fn try_from( - attr_span: proc_macro2::Span, - item: &syn::ItemType, - pallet_index: u8, - disable_call: bool, - disable_unsigned: bool, - bounds: &Punctuated, - ) -> syn::Result { - let name = item.ident.clone(); + pub fn try_from( + attr_span: proc_macro2::Span, + item: &syn::ItemType, + pallet_index: u8, + disable_call: bool, + disable_unsigned: bool, + bounds: &Punctuated, + ) -> syn::Result { + let name = item.ident.clone(); - let mut pallet_path = None; - let mut pallet_parts = vec![]; + let mut pallet_path = None; + let mut pallet_parts = vec![]; - for (index, bound) in bounds.into_iter().enumerate() { - if let syn::TypeParamBound::Trait(syn::TraitBound { path, .. }) = bound { - if index == 0 { - pallet_path = Some(PalletPath { inner: path.clone() }); - } else { - let pallet_part = syn::parse2::(bound.into_token_stream())?; - pallet_parts.push(pallet_part); - } - } else { - return Err(Error::new( - attr_span, - "Invalid pallet declaration, expected a path or a trait object", - )) - }; - } + for (index, bound) in bounds.into_iter().enumerate() { + if let syn::TypeParamBound::Trait(syn::TraitBound { path, .. }) = bound { + if index == 0 { + pallet_path = Some(PalletPath { + inner: path.clone(), + }); + } else { + let pallet_part = syn::parse2::(bound.into_token_stream())?; + pallet_parts.push(pallet_part); + } + } else { + return Err(Error::new( + attr_span, + "Invalid pallet declaration, expected a path or a trait object", + )); + }; + } - let mut path = pallet_path.ok_or(Error::new( - attr_span, - "Invalid pallet declaration, expected a path or a trait object", - ))?; + let mut path = pallet_path.ok_or(Error::new( + attr_span, + "Invalid pallet declaration, expected a path or a trait object", + ))?; - let mut instance = None; - if let Some(segment) = path.inner.segments.iter_mut().find(|seg| !seg.arguments.is_empty()) - { - if let PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { - args, .. - }) = segment.arguments.clone() - { - if let Some(syn::GenericArgument::Type(syn::Type::Path(arg_path))) = args.first() { - instance = - Some(Ident::new(&arg_path.to_token_stream().to_string(), arg_path.span())); - segment.arguments = PathArguments::None; - } - } - } + let mut instance = None; + if let Some(segment) = path + .inner + .segments + .iter_mut() + .find(|seg| !seg.arguments.is_empty()) + { + if let PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { + args, .. + }) = segment.arguments.clone() + { + if let Some(syn::GenericArgument::Type(syn::Type::Path(arg_path))) = args.first() { + instance = Some(Ident::new( + &arg_path.to_token_stream().to_string(), + arg_path.span(), + )); + segment.arguments = PathArguments::None; + } + } + } - pallet_parts = pallet_parts - .into_iter() - .filter(|part| { - if let (true, &PalletPartKeyword::Call(_)) = (disable_call, &part.keyword) { - false - } else if let (true, &PalletPartKeyword::ValidateUnsigned(_)) = - (disable_unsigned, &part.keyword) - { - false - } else { - true - } - }) - .collect(); + pallet_parts = pallet_parts + .into_iter() + .filter(|part| { + if let (true, &PalletPartKeyword::Call(_)) = (disable_call, &part.keyword) { + false + } else if let (true, &PalletPartKeyword::ValidateUnsigned(_)) = + (disable_unsigned, &part.keyword) + { + false + } else { + true + } + }) + .collect(); - let cfg_pattern = vec![]; + let cfg_pattern = vec![]; - Ok(Pallet { - is_expanded: true, - name, - index: pallet_index, - path, - instance, - cfg_pattern, - pallet_parts, - }) - } + Ok(Pallet { + is_expanded: true, + name, + index: pallet_index, + path, + instance, + cfg_pattern, + pallet_parts, + }) + } } diff --git a/support/procedural-fork/src/runtime/parse/pallet_decl.rs b/support/procedural-fork/src/runtime/parse/pallet_decl.rs index 437a163cf..bb1246606 100644 --- a/support/procedural-fork/src/runtime/parse/pallet_decl.rs +++ b/support/procedural-fork/src/runtime/parse/pallet_decl.rs @@ -21,40 +21,51 @@ use syn::{spanned::Spanned, Attribute, Ident, PathArguments}; /// The declaration of a pallet. #[derive(Debug, Clone)] pub struct PalletDeclaration { - /// The name of the pallet, e.g.`System` in `System: frame_system`. - pub name: Ident, - /// Optional attributes tagged right above a pallet declaration. - pub attrs: Vec, - /// The path of the pallet, e.g. `frame_system` in `System: frame_system`. - pub path: syn::Path, - /// The instance of the pallet, e.g. `Instance1` in `Council: pallet_collective::`. - pub instance: Option, + /// The name of the pallet, e.g.`System` in `System: frame_system`. + pub name: Ident, + /// Optional attributes tagged right above a pallet declaration. + pub attrs: Vec, + /// The path of the pallet, e.g. `frame_system` in `System: frame_system`. + pub path: syn::Path, + /// The instance of the pallet, e.g. `Instance1` in `Council: pallet_collective::`. + pub instance: Option, } impl PalletDeclaration { - pub fn try_from( - _attr_span: proc_macro2::Span, - item: &syn::ItemType, - path: &syn::TypePath, - ) -> syn::Result { - let name = item.ident.clone(); - - let mut path = path.path.clone(); - - let mut instance = None; - if let Some(segment) = path.segments.iter_mut().find(|seg| !seg.arguments.is_empty()) { - if let PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { - args, .. - }) = segment.arguments.clone() - { - if let Some(syn::GenericArgument::Type(syn::Type::Path(arg_path))) = args.first() { - instance = - Some(Ident::new(&arg_path.to_token_stream().to_string(), arg_path.span())); - segment.arguments = PathArguments::None; - } - } - } - - Ok(Self { name, path, instance, attrs: item.attrs.clone() }) - } + pub fn try_from( + _attr_span: proc_macro2::Span, + item: &syn::ItemType, + path: &syn::TypePath, + ) -> syn::Result { + let name = item.ident.clone(); + + let mut path = path.path.clone(); + + let mut instance = None; + if let Some(segment) = path + .segments + .iter_mut() + .find(|seg| !seg.arguments.is_empty()) + { + if let PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { + args, .. + }) = segment.arguments.clone() + { + if let Some(syn::GenericArgument::Type(syn::Type::Path(arg_path))) = args.first() { + instance = Some(Ident::new( + &arg_path.to_token_stream().to_string(), + arg_path.span(), + )); + segment.arguments = PathArguments::None; + } + } + } + + Ok(Self { + name, + path, + instance, + attrs: item.attrs.clone(), + }) + } } diff --git a/support/procedural-fork/src/runtime/parse/runtime_struct.rs b/support/procedural-fork/src/runtime/parse/runtime_struct.rs index 8fa746ee8..7ddbdcfeb 100644 --- a/support/procedural-fork/src/runtime/parse/runtime_struct.rs +++ b/support/procedural-fork/src/runtime/parse/runtime_struct.rs @@ -17,19 +17,22 @@ use syn::spanned::Spanned; pub struct RuntimeStructDef { - pub ident: syn::Ident, - pub attr_span: proc_macro2::Span, + pub ident: syn::Ident, + pub attr_span: proc_macro2::Span, } impl RuntimeStructDef { - pub fn try_from(attr_span: proc_macro2::Span, item: &mut syn::Item) -> syn::Result { - let item = if let syn::Item::Struct(item) = item { - item - } else { - let msg = "Invalid runtime::runtime, expected struct definition"; - return Err(syn::Error::new(item.span(), msg)) - }; + pub fn try_from(attr_span: proc_macro2::Span, item: &mut syn::Item) -> syn::Result { + let item = if let syn::Item::Struct(item) = item { + item + } else { + let msg = "Invalid runtime::runtime, expected struct definition"; + return Err(syn::Error::new(item.span(), msg)); + }; - Ok(Self { ident: item.ident.clone(), attr_span }) - } + Ok(Self { + ident: item.ident.clone(), + attr_span, + }) + } } diff --git a/support/procedural-fork/src/runtime/parse/runtime_types.rs b/support/procedural-fork/src/runtime/parse/runtime_types.rs index a4480e2a1..4d8c8358c 100644 --- a/support/procedural-fork/src/runtime/parse/runtime_types.rs +++ b/support/procedural-fork/src/runtime/parse/runtime_types.rs @@ -16,61 +16,61 @@ // limitations under the License. use syn::{ - parse::{Parse, ParseStream}, - Result, + parse::{Parse, ParseStream}, + Result, }; mod keyword { - use syn::custom_keyword; + use syn::custom_keyword; - custom_keyword!(RuntimeCall); - custom_keyword!(RuntimeEvent); - custom_keyword!(RuntimeError); - custom_keyword!(RuntimeOrigin); - custom_keyword!(RuntimeFreezeReason); - custom_keyword!(RuntimeHoldReason); - custom_keyword!(RuntimeSlashReason); - custom_keyword!(RuntimeLockId); - custom_keyword!(RuntimeTask); + custom_keyword!(RuntimeCall); + custom_keyword!(RuntimeEvent); + custom_keyword!(RuntimeError); + custom_keyword!(RuntimeOrigin); + custom_keyword!(RuntimeFreezeReason); + custom_keyword!(RuntimeHoldReason); + custom_keyword!(RuntimeSlashReason); + custom_keyword!(RuntimeLockId); + custom_keyword!(RuntimeTask); } #[derive(Debug, Clone, PartialEq)] pub enum RuntimeType { - RuntimeCall(keyword::RuntimeCall), - RuntimeEvent(keyword::RuntimeEvent), - RuntimeError(keyword::RuntimeError), - RuntimeOrigin(keyword::RuntimeOrigin), - RuntimeFreezeReason(keyword::RuntimeFreezeReason), - RuntimeHoldReason(keyword::RuntimeHoldReason), - RuntimeSlashReason(keyword::RuntimeSlashReason), - RuntimeLockId(keyword::RuntimeLockId), - RuntimeTask(keyword::RuntimeTask), + RuntimeCall(keyword::RuntimeCall), + RuntimeEvent(keyword::RuntimeEvent), + RuntimeError(keyword::RuntimeError), + RuntimeOrigin(keyword::RuntimeOrigin), + RuntimeFreezeReason(keyword::RuntimeFreezeReason), + RuntimeHoldReason(keyword::RuntimeHoldReason), + RuntimeSlashReason(keyword::RuntimeSlashReason), + RuntimeLockId(keyword::RuntimeLockId), + RuntimeTask(keyword::RuntimeTask), } impl Parse for RuntimeType { - fn parse(input: ParseStream) -> Result { - let lookahead = input.lookahead1(); + fn parse(input: ParseStream) -> Result { + let lookahead = input.lookahead1(); - if lookahead.peek(keyword::RuntimeCall) { - Ok(Self::RuntimeCall(input.parse()?)) - } else if lookahead.peek(keyword::RuntimeEvent) { - Ok(Self::RuntimeEvent(input.parse()?)) - } else if lookahead.peek(keyword::RuntimeError) { - Ok(Self::RuntimeError(input.parse()?)) - } else if lookahead.peek(keyword::RuntimeOrigin) { - Ok(Self::RuntimeOrigin(input.parse()?)) - } else if lookahead.peek(keyword::RuntimeFreezeReason) { - Ok(Self::RuntimeFreezeReason(input.parse()?)) - } else if lookahead.peek(keyword::RuntimeHoldReason) { - Ok(Self::RuntimeHoldReason(input.parse()?)) - } else if lookahead.peek(keyword::RuntimeSlashReason) { - Ok(Self::RuntimeSlashReason(input.parse()?)) - } else if lookahead.peek(keyword::RuntimeLockId) { - Ok(Self::RuntimeLockId(input.parse()?)) - } else if lookahead.peek(keyword::RuntimeTask) { - Ok(Self::RuntimeTask(input.parse()?)) - } else { - Err(lookahead.error()) - } - } + if lookahead.peek(keyword::RuntimeCall) { + Ok(Self::RuntimeCall(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeEvent) { + Ok(Self::RuntimeEvent(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeError) { + Ok(Self::RuntimeError(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeOrigin) { + Ok(Self::RuntimeOrigin(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeFreezeReason) { + Ok(Self::RuntimeFreezeReason(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeHoldReason) { + Ok(Self::RuntimeHoldReason(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeSlashReason) { + Ok(Self::RuntimeSlashReason(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeLockId) { + Ok(Self::RuntimeLockId(input.parse()?)) + } else if lookahead.peek(keyword::RuntimeTask) { + Ok(Self::RuntimeTask(input.parse()?)) + } else { + Err(lookahead.error()) + } + } } diff --git a/support/procedural-fork/src/storage_alias.rs b/support/procedural-fork/src/storage_alias.rs index 06f62768f..7099239f9 100644 --- a/support/procedural-fork/src/storage_alias.rs +++ b/support/procedural-fork/src/storage_alias.rs @@ -22,655 +22,688 @@ use frame_support_procedural_tools::generate_access_from_frame_or_crate; use proc_macro2::{Span, TokenStream}; use quote::{quote, ToTokens}; use syn::{ - parenthesized, - parse::{Parse, ParseStream}, - punctuated::Punctuated, - spanned::Spanned, - token, - visit::Visit, - Attribute, Error, Ident, Result, Token, Type, TypeParam, Visibility, WhereClause, + parenthesized, + parse::{Parse, ParseStream}, + punctuated::Punctuated, + spanned::Spanned, + token, + visit::Visit, + Attribute, Error, Ident, Result, Token, Type, TypeParam, Visibility, WhereClause, }; /// Extension trait for [`Type`]. trait TypeExt { - fn get_ident(&self) -> Option<&Ident>; - fn contains_ident(&self, ident: &Ident) -> bool; + fn get_ident(&self) -> Option<&Ident>; + fn contains_ident(&self, ident: &Ident) -> bool; } impl TypeExt for Type { - fn get_ident(&self) -> Option<&Ident> { - match self { - Type::Path(p) => match &p.qself { - Some(qself) => qself.ty.get_ident(), - None => p.path.get_ident(), - }, - _ => None, - } - } - - fn contains_ident(&self, ident: &Ident) -> bool { - struct ContainsIdent<'a> { - ident: &'a Ident, - found: bool, - } - impl<'a, 'ast> Visit<'ast> for ContainsIdent<'a> { - fn visit_ident(&mut self, i: &'ast Ident) { - if i == self.ident { - self.found = true; - } - } - } - - let mut visitor = ContainsIdent { ident, found: false }; - syn::visit::visit_type(&mut visitor, self); - visitor.found - } + fn get_ident(&self) -> Option<&Ident> { + match self { + Type::Path(p) => match &p.qself { + Some(qself) => qself.ty.get_ident(), + None => p.path.get_ident(), + }, + _ => None, + } + } + + fn contains_ident(&self, ident: &Ident) -> bool { + struct ContainsIdent<'a> { + ident: &'a Ident, + found: bool, + } + impl<'a, 'ast> Visit<'ast> for ContainsIdent<'a> { + fn visit_ident(&mut self, i: &'ast Ident) { + if i == self.ident { + self.found = true; + } + } + } + + let mut visitor = ContainsIdent { + ident, + found: false, + }; + syn::visit::visit_type(&mut visitor, self); + visitor.found + } } /// Represents generics which only support [`TypeParam`] separated by commas. struct SimpleGenerics { - lt_token: Token![<], - params: Punctuated, - gt_token: Token![>], + lt_token: Token![<], + params: Punctuated, + gt_token: Token![>], } impl SimpleGenerics { - /// Returns the generics for types declarations etc. - fn type_generics(&self) -> impl Iterator { - self.params.iter().map(|p| &p.ident) - } - - /// Returns the generics for the `impl` block. - fn impl_generics(&self) -> impl Iterator { - self.params.iter() - } + /// Returns the generics for types declarations etc. + fn type_generics(&self) -> impl Iterator { + self.params.iter().map(|p| &p.ident) + } + + /// Returns the generics for the `impl` block. + fn impl_generics(&self) -> impl Iterator { + self.params.iter() + } } impl Parse for SimpleGenerics { - fn parse(input: ParseStream<'_>) -> Result { - Ok(Self { - lt_token: input.parse()?, - params: Punctuated::parse_separated_nonempty(input)?, - gt_token: input.parse()?, - }) - } + fn parse(input: ParseStream<'_>) -> Result { + Ok(Self { + lt_token: input.parse()?, + params: Punctuated::parse_separated_nonempty(input)?, + gt_token: input.parse()?, + }) + } } impl ToTokens for SimpleGenerics { - fn to_tokens(&self, tokens: &mut TokenStream) { - self.lt_token.to_tokens(tokens); - self.params.to_tokens(tokens); - self.gt_token.to_tokens(tokens); - } + fn to_tokens(&self, tokens: &mut TokenStream) { + self.lt_token.to_tokens(tokens); + self.params.to_tokens(tokens); + self.gt_token.to_tokens(tokens); + } } mod storage_types { - syn::custom_keyword!(StorageValue); - syn::custom_keyword!(StorageMap); - syn::custom_keyword!(CountedStorageMap); - syn::custom_keyword!(StorageDoubleMap); - syn::custom_keyword!(StorageNMap); + syn::custom_keyword!(StorageValue); + syn::custom_keyword!(StorageMap); + syn::custom_keyword!(CountedStorageMap); + syn::custom_keyword!(StorageDoubleMap); + syn::custom_keyword!(StorageNMap); } /// The types of prefixes the storage alias macro supports. mod prefix_types { - // Use the verbatim/unmodified input name as the prefix. - syn::custom_keyword!(verbatim); - // The input type is a pallet and its pallet name should be used as the prefix. - syn::custom_keyword!(pallet_name); - // The input type implements `Get<'static str>` and this `str` should be used as the prefix. - syn::custom_keyword!(dynamic); + // Use the verbatim/unmodified input name as the prefix. + syn::custom_keyword!(verbatim); + // The input type is a pallet and its pallet name should be used as the prefix. + syn::custom_keyword!(pallet_name); + // The input type implements `Get<'static str>` and this `str` should be used as the prefix. + syn::custom_keyword!(dynamic); } /// The supported storage types enum StorageType { - Value { - _kw: storage_types::StorageValue, - _lt_token: Token![<], - prefix: Type, - _value_comma: Token![,], - value_ty: Type, - query_type: Option<(Token![,], Type)>, - _trailing_comma: Option, - _gt_token: Token![>], - }, - Map { - _kw: storage_types::StorageMap, - _lt_token: Token![<], - prefix: Type, - _hasher_comma: Token![,], - hasher_ty: Type, - _key_comma: Token![,], - key_ty: Type, - _value_comma: Token![,], - value_ty: Type, - query_type: Option<(Token![,], Type)>, - _trailing_comma: Option, - _gt_token: Token![>], - }, - CountedMap { - _kw: storage_types::CountedStorageMap, - _lt_token: Token![<], - prefix: Type, - _hasher_comma: Token![,], - hasher_ty: Type, - _key_comma: Token![,], - key_ty: Type, - _value_comma: Token![,], - value_ty: Type, - query_type: Option<(Token![,], Type)>, - _trailing_comma: Option, - _gt_token: Token![>], - }, - DoubleMap { - _kw: storage_types::StorageDoubleMap, - _lt_token: Token![<], - prefix: Type, - _hasher1_comma: Token![,], - hasher1_ty: Type, - _key1_comma: Token![,], - key1_ty: Type, - _hasher2_comma: Token![,], - hasher2_ty: Type, - _key2_comma: Token![,], - key2_ty: Type, - _value_comma: Token![,], - value_ty: Type, - query_type: Option<(Token![,], Type)>, - _trailing_comma: Option, - _gt_token: Token![>], - }, - NMap { - _kw: storage_types::StorageNMap, - _lt_token: Token![<], - prefix: Type, - _paren_comma: Token![,], - _paren_token: token::Paren, - key_types: Punctuated, - _value_comma: Token![,], - value_ty: Type, - query_type: Option<(Token![,], Type)>, - _trailing_comma: Option, - _gt_token: Token![>], - }, + Value { + _kw: storage_types::StorageValue, + _lt_token: Token![<], + prefix: Type, + _value_comma: Token![,], + value_ty: Type, + query_type: Option<(Token![,], Type)>, + _trailing_comma: Option, + _gt_token: Token![>], + }, + Map { + _kw: storage_types::StorageMap, + _lt_token: Token![<], + prefix: Type, + _hasher_comma: Token![,], + hasher_ty: Type, + _key_comma: Token![,], + key_ty: Type, + _value_comma: Token![,], + value_ty: Type, + query_type: Option<(Token![,], Type)>, + _trailing_comma: Option, + _gt_token: Token![>], + }, + CountedMap { + _kw: storage_types::CountedStorageMap, + _lt_token: Token![<], + prefix: Type, + _hasher_comma: Token![,], + hasher_ty: Type, + _key_comma: Token![,], + key_ty: Type, + _value_comma: Token![,], + value_ty: Type, + query_type: Option<(Token![,], Type)>, + _trailing_comma: Option, + _gt_token: Token![>], + }, + DoubleMap { + _kw: storage_types::StorageDoubleMap, + _lt_token: Token![<], + prefix: Type, + _hasher1_comma: Token![,], + hasher1_ty: Type, + _key1_comma: Token![,], + key1_ty: Type, + _hasher2_comma: Token![,], + hasher2_ty: Type, + _key2_comma: Token![,], + key2_ty: Type, + _value_comma: Token![,], + value_ty: Type, + query_type: Option<(Token![,], Type)>, + _trailing_comma: Option, + _gt_token: Token![>], + }, + NMap { + _kw: storage_types::StorageNMap, + _lt_token: Token![<], + prefix: Type, + _paren_comma: Token![,], + _paren_token: token::Paren, + key_types: Punctuated, + _value_comma: Token![,], + value_ty: Type, + query_type: Option<(Token![,], Type)>, + _trailing_comma: Option, + _gt_token: Token![>], + }, } impl StorageType { - /// Generate the actual type declaration. - fn generate_type_declaration( - &self, - crate_: &syn::Path, - storage_instance: &StorageInstance, - storage_name: &Ident, - storage_generics: Option<&SimpleGenerics>, - visibility: &Visibility, - attributes: &[Attribute], - ) -> TokenStream { - let storage_instance_generics = &storage_instance.generics; - let storage_instance = &storage_instance.name; - let attributes = attributes.iter(); - let storage_generics = storage_generics.map(|g| { - let generics = g.type_generics(); - - quote!( < #( #generics ),* > ) - }); - - match self { - Self::Value { value_ty, query_type, .. } => { - let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); - - quote! { - #( #attributes )* - #visibility type #storage_name #storage_generics = #crate_::storage::types::StorageValue< - #storage_instance #storage_instance_generics, - #value_ty - #query_type - >; - } - }, - Self::CountedMap { value_ty, query_type, hasher_ty, key_ty, .. } | - Self::Map { value_ty, query_type, hasher_ty, key_ty, .. } => { - let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); - let map_type = Ident::new( - match self { - Self::Map { .. } => "StorageMap", - _ => "CountedStorageMap", - }, - Span::call_site(), - ); - - quote! { - #( #attributes )* - #visibility type #storage_name #storage_generics = #crate_::storage::types::#map_type< - #storage_instance #storage_instance_generics, - #hasher_ty, - #key_ty, - #value_ty - #query_type - >; - } - }, - Self::DoubleMap { - value_ty, - query_type, - hasher1_ty, - key1_ty, - hasher2_ty, - key2_ty, - .. - } => { - let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); - - quote! { - #( #attributes )* - #visibility type #storage_name #storage_generics = #crate_::storage::types::StorageDoubleMap< - #storage_instance #storage_instance_generics, - #hasher1_ty, - #key1_ty, - #hasher2_ty, - #key2_ty, - #value_ty - #query_type - >; - } - }, - Self::NMap { value_ty, query_type, key_types, .. } => { - let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); - let key_types = key_types.iter(); - - quote! { - #( #attributes )* - #visibility type #storage_name #storage_generics = #crate_::storage::types::StorageNMap< - #storage_instance #storage_instance_generics, - ( #( #key_types ),* ), - #value_ty - #query_type - >; - } - }, - } - } - - /// The prefix for this storage type. - fn prefix(&self) -> &Type { - match self { - Self::Value { prefix, .. } | - Self::Map { prefix, .. } | - Self::CountedMap { prefix, .. } | - Self::NMap { prefix, .. } | - Self::DoubleMap { prefix, .. } => prefix, - } - } + /// Generate the actual type declaration. + fn generate_type_declaration( + &self, + crate_: &syn::Path, + storage_instance: &StorageInstance, + storage_name: &Ident, + storage_generics: Option<&SimpleGenerics>, + visibility: &Visibility, + attributes: &[Attribute], + ) -> TokenStream { + let storage_instance_generics = &storage_instance.generics; + let storage_instance = &storage_instance.name; + let attributes = attributes.iter(); + let storage_generics = storage_generics.map(|g| { + let generics = g.type_generics(); + + quote!( < #( #generics ),* > ) + }); + + match self { + Self::Value { + value_ty, + query_type, + .. + } => { + let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); + + quote! { + #( #attributes )* + #visibility type #storage_name #storage_generics = #crate_::storage::types::StorageValue< + #storage_instance #storage_instance_generics, + #value_ty + #query_type + >; + } + } + Self::CountedMap { + value_ty, + query_type, + hasher_ty, + key_ty, + .. + } + | Self::Map { + value_ty, + query_type, + hasher_ty, + key_ty, + .. + } => { + let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); + let map_type = Ident::new( + match self { + Self::Map { .. } => "StorageMap", + _ => "CountedStorageMap", + }, + Span::call_site(), + ); + + quote! { + #( #attributes )* + #visibility type #storage_name #storage_generics = #crate_::storage::types::#map_type< + #storage_instance #storage_instance_generics, + #hasher_ty, + #key_ty, + #value_ty + #query_type + >; + } + } + Self::DoubleMap { + value_ty, + query_type, + hasher1_ty, + key1_ty, + hasher2_ty, + key2_ty, + .. + } => { + let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); + + quote! { + #( #attributes )* + #visibility type #storage_name #storage_generics = #crate_::storage::types::StorageDoubleMap< + #storage_instance #storage_instance_generics, + #hasher1_ty, + #key1_ty, + #hasher2_ty, + #key2_ty, + #value_ty + #query_type + >; + } + } + Self::NMap { + value_ty, + query_type, + key_types, + .. + } => { + let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t)); + let key_types = key_types.iter(); + + quote! { + #( #attributes )* + #visibility type #storage_name #storage_generics = #crate_::storage::types::StorageNMap< + #storage_instance #storage_instance_generics, + ( #( #key_types ),* ), + #value_ty + #query_type + >; + } + } + } + } + + /// The prefix for this storage type. + fn prefix(&self) -> &Type { + match self { + Self::Value { prefix, .. } + | Self::Map { prefix, .. } + | Self::CountedMap { prefix, .. } + | Self::NMap { prefix, .. } + | Self::DoubleMap { prefix, .. } => prefix, + } + } } impl Parse for StorageType { - fn parse(input: ParseStream<'_>) -> Result { - let lookahead = input.lookahead1(); - - let parse_query_type = |input: ParseStream<'_>| -> Result> { - if input.peek(Token![,]) && !input.peek2(Token![>]) { - Ok(Some((input.parse()?, input.parse()?))) - } else { - Ok(None) - } - }; - - if lookahead.peek(storage_types::StorageValue) { - Ok(Self::Value { - _kw: input.parse()?, - _lt_token: input.parse()?, - prefix: input.parse()?, - _value_comma: input.parse()?, - value_ty: input.parse()?, - query_type: parse_query_type(input)?, - _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, - _gt_token: input.parse()?, - }) - } else if lookahead.peek(storage_types::StorageMap) { - Ok(Self::Map { - _kw: input.parse()?, - _lt_token: input.parse()?, - prefix: input.parse()?, - _hasher_comma: input.parse()?, - hasher_ty: input.parse()?, - _key_comma: input.parse()?, - key_ty: input.parse()?, - _value_comma: input.parse()?, - value_ty: input.parse()?, - query_type: parse_query_type(input)?, - _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, - _gt_token: input.parse()?, - }) - } else if lookahead.peek(storage_types::CountedStorageMap) { - Ok(Self::CountedMap { - _kw: input.parse()?, - _lt_token: input.parse()?, - prefix: input.parse()?, - _hasher_comma: input.parse()?, - hasher_ty: input.parse()?, - _key_comma: input.parse()?, - key_ty: input.parse()?, - _value_comma: input.parse()?, - value_ty: input.parse()?, - query_type: parse_query_type(input)?, - _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, - _gt_token: input.parse()?, - }) - } else if lookahead.peek(storage_types::StorageDoubleMap) { - Ok(Self::DoubleMap { - _kw: input.parse()?, - _lt_token: input.parse()?, - prefix: input.parse()?, - _hasher1_comma: input.parse()?, - hasher1_ty: input.parse()?, - _key1_comma: input.parse()?, - key1_ty: input.parse()?, - _hasher2_comma: input.parse()?, - hasher2_ty: input.parse()?, - _key2_comma: input.parse()?, - key2_ty: input.parse()?, - _value_comma: input.parse()?, - value_ty: input.parse()?, - query_type: parse_query_type(input)?, - _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, - _gt_token: input.parse()?, - }) - } else if lookahead.peek(storage_types::StorageNMap) { - let content; - Ok(Self::NMap { - _kw: input.parse()?, - _lt_token: input.parse()?, - prefix: input.parse()?, - _paren_comma: input.parse()?, - _paren_token: parenthesized!(content in input), - key_types: Punctuated::parse_terminated(&content)?, - _value_comma: input.parse()?, - value_ty: input.parse()?, - query_type: parse_query_type(input)?, - _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, - _gt_token: input.parse()?, - }) - } else { - Err(lookahead.error()) - } - } + fn parse(input: ParseStream<'_>) -> Result { + let lookahead = input.lookahead1(); + + let parse_query_type = |input: ParseStream<'_>| -> Result> { + if input.peek(Token![,]) && !input.peek2(Token![>]) { + Ok(Some((input.parse()?, input.parse()?))) + } else { + Ok(None) + } + }; + + if lookahead.peek(storage_types::StorageValue) { + Ok(Self::Value { + _kw: input.parse()?, + _lt_token: input.parse()?, + prefix: input.parse()?, + _value_comma: input.parse()?, + value_ty: input.parse()?, + query_type: parse_query_type(input)?, + _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, + _gt_token: input.parse()?, + }) + } else if lookahead.peek(storage_types::StorageMap) { + Ok(Self::Map { + _kw: input.parse()?, + _lt_token: input.parse()?, + prefix: input.parse()?, + _hasher_comma: input.parse()?, + hasher_ty: input.parse()?, + _key_comma: input.parse()?, + key_ty: input.parse()?, + _value_comma: input.parse()?, + value_ty: input.parse()?, + query_type: parse_query_type(input)?, + _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, + _gt_token: input.parse()?, + }) + } else if lookahead.peek(storage_types::CountedStorageMap) { + Ok(Self::CountedMap { + _kw: input.parse()?, + _lt_token: input.parse()?, + prefix: input.parse()?, + _hasher_comma: input.parse()?, + hasher_ty: input.parse()?, + _key_comma: input.parse()?, + key_ty: input.parse()?, + _value_comma: input.parse()?, + value_ty: input.parse()?, + query_type: parse_query_type(input)?, + _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, + _gt_token: input.parse()?, + }) + } else if lookahead.peek(storage_types::StorageDoubleMap) { + Ok(Self::DoubleMap { + _kw: input.parse()?, + _lt_token: input.parse()?, + prefix: input.parse()?, + _hasher1_comma: input.parse()?, + hasher1_ty: input.parse()?, + _key1_comma: input.parse()?, + key1_ty: input.parse()?, + _hasher2_comma: input.parse()?, + hasher2_ty: input.parse()?, + _key2_comma: input.parse()?, + key2_ty: input.parse()?, + _value_comma: input.parse()?, + value_ty: input.parse()?, + query_type: parse_query_type(input)?, + _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, + _gt_token: input.parse()?, + }) + } else if lookahead.peek(storage_types::StorageNMap) { + let content; + Ok(Self::NMap { + _kw: input.parse()?, + _lt_token: input.parse()?, + prefix: input.parse()?, + _paren_comma: input.parse()?, + _paren_token: parenthesized!(content in input), + key_types: Punctuated::parse_terminated(&content)?, + _value_comma: input.parse()?, + value_ty: input.parse()?, + query_type: parse_query_type(input)?, + _trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?, + _gt_token: input.parse()?, + }) + } else { + Err(lookahead.error()) + } + } } /// The input expected by this macro. struct Input { - attributes: Vec, - visibility: Visibility, - _type: Token![type], - storage_name: Ident, - storage_generics: Option, - where_clause: Option, - _equal: Token![=], - storage_type: StorageType, - _semicolon: Token![;], + attributes: Vec, + visibility: Visibility, + _type: Token![type], + storage_name: Ident, + storage_generics: Option, + where_clause: Option, + _equal: Token![=], + storage_type: StorageType, + _semicolon: Token![;], } impl Parse for Input { - fn parse(input: ParseStream<'_>) -> Result { - let attributes = input.call(Attribute::parse_outer)?; - let visibility = input.parse()?; - let _type = input.parse()?; - let storage_name = input.parse()?; - - let lookahead = input.lookahead1(); - let storage_generics = if lookahead.peek(Token![<]) { - Some(input.parse()?) - } else if lookahead.peek(Token![=]) { - None - } else { - return Err(lookahead.error()) - }; - - let lookahead = input.lookahead1(); - let where_clause = if lookahead.peek(Token![where]) { - Some(input.parse()?) - } else if lookahead.peek(Token![=]) { - None - } else { - return Err(lookahead.error()) - }; - - let _equal = input.parse()?; - - let storage_type = input.parse()?; - - let _semicolon = input.parse()?; - - Ok(Self { - attributes, - visibility, - _type, - storage_name, - storage_generics, - _equal, - storage_type, - where_clause, - _semicolon, - }) - } + fn parse(input: ParseStream<'_>) -> Result { + let attributes = input.call(Attribute::parse_outer)?; + let visibility = input.parse()?; + let _type = input.parse()?; + let storage_name = input.parse()?; + + let lookahead = input.lookahead1(); + let storage_generics = if lookahead.peek(Token![<]) { + Some(input.parse()?) + } else if lookahead.peek(Token![=]) { + None + } else { + return Err(lookahead.error()); + }; + + let lookahead = input.lookahead1(); + let where_clause = if lookahead.peek(Token![where]) { + Some(input.parse()?) + } else if lookahead.peek(Token![=]) { + None + } else { + return Err(lookahead.error()); + }; + + let _equal = input.parse()?; + + let storage_type = input.parse()?; + + let _semicolon = input.parse()?; + + Ok(Self { + attributes, + visibility, + _type, + storage_name, + storage_generics, + _equal, + storage_type, + where_clause, + _semicolon, + }) + } } /// Defines which type of prefix the storage alias is using. #[derive(Clone, Copy)] enum PrefixType { - /// An appropriate prefix will be determined automatically. - /// - /// If generics are passed, this is assumed to be a pallet and the pallet name should be used. - /// Otherwise use the verbatim passed name as prefix. - Compatibility, - /// The provided ident/name will be used as the prefix. - Verbatim, - /// The provided type will be used to determine the prefix. This type must - /// implement `PalletInfoAccess` which specifies the proper name. This - /// name is then used as the prefix. - PalletName, - /// Uses the provided type implementing `Get<'static str>` to determine the prefix. - Dynamic, + /// An appropriate prefix will be determined automatically. + /// + /// If generics are passed, this is assumed to be a pallet and the pallet name should be used. + /// Otherwise use the verbatim passed name as prefix. + Compatibility, + /// The provided ident/name will be used as the prefix. + Verbatim, + /// The provided type will be used to determine the prefix. This type must + /// implement `PalletInfoAccess` which specifies the proper name. This + /// name is then used as the prefix. + PalletName, + /// Uses the provided type implementing `Get<'static str>` to determine the prefix. + Dynamic, } /// Implementation of the `storage_alias` attribute macro. pub fn storage_alias(attributes: TokenStream, input: TokenStream) -> Result { - let input = syn::parse2::(input)?; - let crate_ = generate_access_from_frame_or_crate("frame-support")?; - - let prefix_type = if attributes.is_empty() { - PrefixType::Compatibility - } else if syn::parse2::(attributes.clone()).is_ok() { - PrefixType::Verbatim - } else if syn::parse2::(attributes.clone()).is_ok() { - PrefixType::PalletName - } else if syn::parse2::(attributes.clone()).is_ok() { - PrefixType::Dynamic - } else { - return Err(Error::new(attributes.span(), "Unknown attributes")) - }; - - let storage_instance = generate_storage_instance( - &crate_, - &input.storage_name, - input.storage_generics.as_ref(), - input.where_clause.as_ref(), - input.storage_type.prefix(), - &input.visibility, - matches!(input.storage_type, StorageType::CountedMap { .. }), - prefix_type, - )?; - - let definition = input.storage_type.generate_type_declaration( - &crate_, - &storage_instance, - &input.storage_name, - input.storage_generics.as_ref(), - &input.visibility, - &input.attributes, - ); - - let storage_instance_code = storage_instance.code; - - Ok(quote! { - #storage_instance_code - - #definition - }) + let input = syn::parse2::(input)?; + let crate_ = generate_access_from_frame_or_crate("frame-support")?; + + let prefix_type = if attributes.is_empty() { + PrefixType::Compatibility + } else if syn::parse2::(attributes.clone()).is_ok() { + PrefixType::Verbatim + } else if syn::parse2::(attributes.clone()).is_ok() { + PrefixType::PalletName + } else if syn::parse2::(attributes.clone()).is_ok() { + PrefixType::Dynamic + } else { + return Err(Error::new(attributes.span(), "Unknown attributes")); + }; + + let storage_instance = generate_storage_instance( + &crate_, + &input.storage_name, + input.storage_generics.as_ref(), + input.where_clause.as_ref(), + input.storage_type.prefix(), + &input.visibility, + matches!(input.storage_type, StorageType::CountedMap { .. }), + prefix_type, + )?; + + let definition = input.storage_type.generate_type_declaration( + &crate_, + &storage_instance, + &input.storage_name, + input.storage_generics.as_ref(), + &input.visibility, + &input.attributes, + ); + + let storage_instance_code = storage_instance.code; + + Ok(quote! { + #storage_instance_code + + #definition + }) } /// The storage instance to use for the storage alias. struct StorageInstance { - name: Ident, - generics: TokenStream, - code: TokenStream, + name: Ident, + generics: TokenStream, + code: TokenStream, } /// Generate the [`StorageInstance`] for the storage alias. fn generate_storage_instance( - crate_: &syn::Path, - storage_name: &Ident, - storage_generics: Option<&SimpleGenerics>, - storage_where_clause: Option<&WhereClause>, - prefix: &Type, - visibility: &Visibility, - is_counted_map: bool, - prefix_type: PrefixType, + crate_: &syn::Path, + storage_name: &Ident, + storage_generics: Option<&SimpleGenerics>, + storage_where_clause: Option<&WhereClause>, + prefix: &Type, + visibility: &Visibility, + is_counted_map: bool, + prefix_type: PrefixType, ) -> Result { - if let Type::Infer(_) = prefix { - return Err(Error::new(prefix.span(), "`_` is not allowed as prefix by `storage_alias`.")) - } - - let impl_generics_used_by_prefix = storage_generics - .as_ref() - .map(|g| { - g.impl_generics() - .filter(|g| prefix.contains_ident(&g.ident)) - .collect::>() - }) - .unwrap_or_default(); - - let (pallet_prefix, impl_generics, type_generics) = match prefix_type { - PrefixType::Compatibility => - if !impl_generics_used_by_prefix.is_empty() { - let type_generics = impl_generics_used_by_prefix.iter().map(|g| &g.ident); - let impl_generics = impl_generics_used_by_prefix.iter(); - - ( - quote! { - < #prefix as #crate_::traits::PalletInfoAccess>::name() - }, - quote!( #( #impl_generics ),* ), - quote!( #( #type_generics ),* ), - ) - } else if let Some(prefix) = prefix.get_ident() { - let prefix_str = prefix.to_string(); - - (quote!(#prefix_str), quote!(), quote!()) - } else { - return Err(Error::new_spanned( - prefix, - "If there are no generics, the prefix is only allowed to be an identifier.", - )) - }, - PrefixType::Verbatim => { - let prefix_str = match prefix.get_ident() { - Some(p) => p.to_string(), - None => - return Err(Error::new_spanned( - prefix, - "Prefix type `verbatim` requires that the prefix is an ident.", - )), - }; - - (quote!(#prefix_str), quote!(), quote!()) - }, - PrefixType::PalletName => { - let type_generics = impl_generics_used_by_prefix.iter().map(|g| &g.ident); - let impl_generics = impl_generics_used_by_prefix.iter(); - - ( - quote! { - <#prefix as #crate_::traits::PalletInfoAccess>::name() - }, - quote!( #( #impl_generics ),* ), - quote!( #( #type_generics ),* ), - ) - }, - PrefixType::Dynamic => { - let type_generics = impl_generics_used_by_prefix.iter().map(|g| &g.ident); - let impl_generics = impl_generics_used_by_prefix.iter(); - - ( - quote! { - <#prefix as #crate_::traits::Get<_>>::get() - }, - quote!( #( #impl_generics ),* ), - quote!( #( #type_generics ),* ), - ) - }, - }; - - let where_clause = storage_where_clause.map(|w| quote!(#w)).unwrap_or_default(); - - let name_str = format!("{}_Storage_Instance", storage_name); - let name = Ident::new(&name_str, Span::call_site()); - let storage_name_str = storage_name.to_string(); - - let counter_code = is_counted_map.then(|| { - let counter_name = Ident::new(&counter_prefix(&name_str), Span::call_site()); - let counter_storage_name_str = counter_prefix(&storage_name_str); - let storage_prefix_hash = helper::two128_str(&counter_storage_name_str); - - quote! { - #visibility struct #counter_name< #impl_generics >( - ::core::marker::PhantomData<(#type_generics)> - ) #where_clause; - - impl<#impl_generics> #crate_::traits::StorageInstance - for #counter_name< #type_generics > #where_clause - { - fn pallet_prefix() -> &'static str { - #pallet_prefix - } - - const STORAGE_PREFIX: &'static str = #counter_storage_name_str; - fn storage_prefix_hash() -> [u8; 16] { - #storage_prefix_hash - } - } - - impl<#impl_generics> #crate_::storage::types::CountedStorageMapInstance - for #name< #type_generics > #where_clause - { - type CounterPrefix = #counter_name < #type_generics >; - } - } - }); - - let storage_prefix_hash = helper::two128_str(&storage_name_str); - - // Implement `StorageInstance` trait. - let code = quote! { - #[allow(non_camel_case_types)] - #visibility struct #name< #impl_generics >( - ::core::marker::PhantomData<(#type_generics)> - ) #where_clause; - - impl<#impl_generics> #crate_::traits::StorageInstance - for #name< #type_generics > #where_clause - { - fn pallet_prefix() -> &'static str { - #pallet_prefix - } - - const STORAGE_PREFIX: &'static str = #storage_name_str; - fn storage_prefix_hash() -> [u8; 16] { - #storage_prefix_hash - } - } - - #counter_code - }; - - Ok(StorageInstance { name, code, generics: quote!( < #type_generics > ) }) + if let Type::Infer(_) = prefix { + return Err(Error::new( + prefix.span(), + "`_` is not allowed as prefix by `storage_alias`.", + )); + } + + let impl_generics_used_by_prefix = storage_generics + .as_ref() + .map(|g| { + g.impl_generics() + .filter(|g| prefix.contains_ident(&g.ident)) + .collect::>() + }) + .unwrap_or_default(); + + let (pallet_prefix, impl_generics, type_generics) = match prefix_type { + PrefixType::Compatibility => { + if !impl_generics_used_by_prefix.is_empty() { + let type_generics = impl_generics_used_by_prefix.iter().map(|g| &g.ident); + let impl_generics = impl_generics_used_by_prefix.iter(); + + ( + quote! { + < #prefix as #crate_::traits::PalletInfoAccess>::name() + }, + quote!( #( #impl_generics ),* ), + quote!( #( #type_generics ),* ), + ) + } else if let Some(prefix) = prefix.get_ident() { + let prefix_str = prefix.to_string(); + + (quote!(#prefix_str), quote!(), quote!()) + } else { + return Err(Error::new_spanned( + prefix, + "If there are no generics, the prefix is only allowed to be an identifier.", + )); + } + } + PrefixType::Verbatim => { + let prefix_str = match prefix.get_ident() { + Some(p) => p.to_string(), + None => { + return Err(Error::new_spanned( + prefix, + "Prefix type `verbatim` requires that the prefix is an ident.", + )) + } + }; + + (quote!(#prefix_str), quote!(), quote!()) + } + PrefixType::PalletName => { + let type_generics = impl_generics_used_by_prefix.iter().map(|g| &g.ident); + let impl_generics = impl_generics_used_by_prefix.iter(); + + ( + quote! { + <#prefix as #crate_::traits::PalletInfoAccess>::name() + }, + quote!( #( #impl_generics ),* ), + quote!( #( #type_generics ),* ), + ) + } + PrefixType::Dynamic => { + let type_generics = impl_generics_used_by_prefix.iter().map(|g| &g.ident); + let impl_generics = impl_generics_used_by_prefix.iter(); + + ( + quote! { + <#prefix as #crate_::traits::Get<_>>::get() + }, + quote!( #( #impl_generics ),* ), + quote!( #( #type_generics ),* ), + ) + } + }; + + let where_clause = storage_where_clause.map(|w| quote!(#w)).unwrap_or_default(); + + let name_str = format!("{}_Storage_Instance", storage_name); + let name = Ident::new(&name_str, Span::call_site()); + let storage_name_str = storage_name.to_string(); + + let counter_code = is_counted_map.then(|| { + let counter_name = Ident::new(&counter_prefix(&name_str), Span::call_site()); + let counter_storage_name_str = counter_prefix(&storage_name_str); + let storage_prefix_hash = helper::two128_str(&counter_storage_name_str); + + quote! { + #visibility struct #counter_name< #impl_generics >( + ::core::marker::PhantomData<(#type_generics)> + ) #where_clause; + + impl<#impl_generics> #crate_::traits::StorageInstance + for #counter_name< #type_generics > #where_clause + { + fn pallet_prefix() -> &'static str { + #pallet_prefix + } + + const STORAGE_PREFIX: &'static str = #counter_storage_name_str; + fn storage_prefix_hash() -> [u8; 16] { + #storage_prefix_hash + } + } + + impl<#impl_generics> #crate_::storage::types::CountedStorageMapInstance + for #name< #type_generics > #where_clause + { + type CounterPrefix = #counter_name < #type_generics >; + } + } + }); + + let storage_prefix_hash = helper::two128_str(&storage_name_str); + + // Implement `StorageInstance` trait. + let code = quote! { + #[allow(non_camel_case_types)] + #visibility struct #name< #impl_generics >( + ::core::marker::PhantomData<(#type_generics)> + ) #where_clause; + + impl<#impl_generics> #crate_::traits::StorageInstance + for #name< #type_generics > #where_clause + { + fn pallet_prefix() -> &'static str { + #pallet_prefix + } + + const STORAGE_PREFIX: &'static str = #storage_name_str; + fn storage_prefix_hash() -> [u8; 16] { + #storage_prefix_hash + } + } + + #counter_code + }; + + Ok(StorageInstance { + name, + code, + generics: quote!( < #type_generics > ), + }) } diff --git a/support/procedural-fork/src/transactional.rs b/support/procedural-fork/src/transactional.rs index e9d4f84b7..73a841d9b 100644 --- a/support/procedural-fork/src/transactional.rs +++ b/support/procedural-fork/src/transactional.rs @@ -21,40 +21,50 @@ use quote::quote; use syn::{ItemFn, Result}; pub fn transactional(_attr: TokenStream, input: TokenStream) -> Result { - let ItemFn { attrs, vis, sig, block } = syn::parse(input)?; - - let crate_ = generate_access_from_frame_or_crate("frame-support")?; - let output = quote! { - #(#attrs)* - #vis #sig { - use #crate_::storage::{with_transaction, TransactionOutcome}; - with_transaction(|| { - let r = (|| { #block })(); - if r.is_ok() { - TransactionOutcome::Commit(r) - } else { - TransactionOutcome::Rollback(r) - } - }) - } - }; - - Ok(output.into()) + let ItemFn { + attrs, + vis, + sig, + block, + } = syn::parse(input)?; + + let crate_ = generate_access_from_frame_or_crate("frame-support")?; + let output = quote! { + #(#attrs)* + #vis #sig { + use #crate_::storage::{with_transaction, TransactionOutcome}; + with_transaction(|| { + let r = (|| { #block })(); + if r.is_ok() { + TransactionOutcome::Commit(r) + } else { + TransactionOutcome::Rollback(r) + } + }) + } + }; + + Ok(output.into()) } pub fn require_transactional(_attr: TokenStream, input: TokenStream) -> Result { - let ItemFn { attrs, vis, sig, block } = syn::parse(input)?; - - let crate_ = generate_access_from_frame_or_crate("frame-support")?; - let output = quote! { - #(#attrs)* - #vis #sig { - if !#crate_::storage::transactional::is_transactional() { - return Err(#crate_::sp_runtime::TransactionalError::NoLayer.into()); - } - #block - } - }; - - Ok(output.into()) + let ItemFn { + attrs, + vis, + sig, + block, + } = syn::parse(input)?; + + let crate_ = generate_access_from_frame_or_crate("frame-support")?; + let output = quote! { + #(#attrs)* + #vis #sig { + if !#crate_::storage::transactional::is_transactional() { + return Err(#crate_::sp_runtime::TransactionalError::NoLayer.into()); + } + #block + } + }; + + Ok(output.into()) } diff --git a/support/procedural-fork/src/tt_macro.rs b/support/procedural-fork/src/tt_macro.rs index d37127421..3f280013f 100644 --- a/support/procedural-fork/src/tt_macro.rs +++ b/support/procedural-fork/src/tt_macro.rs @@ -22,29 +22,29 @@ use proc_macro2::{Ident, TokenStream}; use quote::format_ident; struct CreateTtReturnMacroDef { - name: Ident, - args: Vec<(Ident, TokenStream)>, + name: Ident, + args: Vec<(Ident, TokenStream)>, } impl syn::parse::Parse for CreateTtReturnMacroDef { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let name = input.parse()?; - let _ = input.parse::()?; + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let name = input.parse()?; + let _ = input.parse::()?; - let mut args = Vec::new(); - while !input.is_empty() { - let mut value; - let key: Ident = input.parse()?; - let _ = input.parse::()?; - let _: syn::token::Bracket = syn::bracketed!(value in input); - let _: syn::token::Brace = syn::braced!(value in value); - let value: TokenStream = value.parse()?; + let mut args = Vec::new(); + while !input.is_empty() { + let mut value; + let key: Ident = input.parse()?; + let _ = input.parse::()?; + let _: syn::token::Bracket = syn::bracketed!(value in input); + let _: syn::token::Brace = syn::braced!(value in value); + let value: TokenStream = value.parse()?; - args.push((key, value)) - } + args.push((key, value)) + } - Ok(Self { name, args }) - } + Ok(Self { name, args }) + } } /// A proc macro that accepts a name and any number of key-value pairs, to be used to create a @@ -74,32 +74,32 @@ impl syn::parse::Parse for CreateTtReturnMacroDef { /// } /// ``` pub fn create_tt_return_macro(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let CreateTtReturnMacroDef { name, args } = - syn::parse_macro_input!(input as CreateTtReturnMacroDef); + let CreateTtReturnMacroDef { name, args } = + syn::parse_macro_input!(input as CreateTtReturnMacroDef); - let (keys, values): (Vec<_>, Vec<_>) = args.into_iter().unzip(); - let count = COUNTER.with(|counter| counter.borrow_mut().inc()); - let unique_name = format_ident!("{}_{}", name, count); + let (keys, values): (Vec<_>, Vec<_>) = args.into_iter().unzip(); + let count = COUNTER.with(|counter| counter.borrow_mut().inc()); + let unique_name = format_ident!("{}_{}", name, count); - let decl_macro = quote::quote! { - #[macro_export] - #[doc(hidden)] - macro_rules! #unique_name { - { - $caller:tt - $(your_tt_return = [{ $my_tt_macro:path }])? - } => { - $my_tt_return! { - $caller - #( - #keys = [{ #values }] - )* - } - } - } + let decl_macro = quote::quote! { + #[macro_export] + #[doc(hidden)] + macro_rules! #unique_name { + { + $caller:tt + $(your_tt_return = [{ $my_tt_macro:path }])? + } => { + $my_tt_return! { + $caller + #( + #keys = [{ #values }] + )* + } + } + } - pub use #unique_name as #name; - }; + pub use #unique_name as #name; + }; - decl_macro.into() + decl_macro.into() } From 060787a91785847705b38a901b817f05ce762e19 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 19 Sep 2024 14:37:13 -0400 Subject: [PATCH 48/53] cargo fmt --- .../procedural-fork/src/no_bound/default.rs | 244 +++++++++--------- 1 file changed, 123 insertions(+), 121 deletions(-) diff --git a/support/procedural-fork/src/no_bound/default.rs b/support/procedural-fork/src/no_bound/default.rs index 1c0d90531..3f896da35 100644 --- a/support/procedural-fork/src/no_bound/default.rs +++ b/support/procedural-fork/src/no_bound/default.rs @@ -27,132 +27,134 @@ pub fn derive_default_no_bound(input: proc_macro::TokenStream) -> proc_macro::To let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - let impl_ = - match input.data { - Data::Struct(struct_) => match struct_.fields { - Fields::Named(named) => { - let fields = named.named.iter().map(|field| &field.ident).map(|ident| { - quote_spanned! {ident.span() => - #ident: ::core::default::Default::default() - } - }); + let impl_ = match input.data { + Data::Struct(struct_) => match struct_.fields { + Fields::Named(named) => { + let fields = named.named.iter().map(|field| &field.ident).map(|ident| { + quote_spanned! {ident.span() => + #ident: ::core::default::Default::default() + } + }); + + quote!(Self { #( #fields, )* }) + } + Fields::Unnamed(unnamed) => { + let fields = unnamed.unnamed.iter().map(|field| { + quote_spanned! {field.span()=> + ::core::default::Default::default() + } + }); + + quote!(Self( #( #fields, )* )) + } + Fields::Unit => { + quote!(Self) + } + }, + Data::Enum(enum_) => { + if enum_.variants.is_empty() { + return syn::Error::new_spanned(name, "cannot derive Default for an empty enum") + .to_compile_error() + .into(); + } - quote!(Self { #( #fields, )* }) - } - Fields::Unnamed(unnamed) => { - let fields = unnamed.unnamed.iter().map(|field| { - quote_spanned! {field.span()=> - ::core::default::Default::default() + // all #[default] attrs with the variant they're on; i.e. a var + let default_variants = enum_ + .variants + .into_iter() + .filter(|variant| { + variant + .attrs + .iter() + .any(|attr| attr.path().is_ident("default")) + }) + .collect::>(); + + match &*default_variants { + [] => return syn::Error::new( + name.clone().span(), + "no default declared, make a variant default by placing `#[default]` above it", + ) + .into_compile_error() + .into(), + // only one variant with the #[default] attribute set + [default_variant] => { + let variant_attrs = default_variant + .attrs + .iter() + .filter(|a| a.path().is_ident("default")) + .collect::>(); + + // check that there is only one #[default] attribute on the variant + if let [first_attr, second_attr, additional_attrs @ ..] = &*variant_attrs { + let mut err = + syn::Error::new(Span::call_site(), "multiple `#[default]` attributes"); + + err.combine(syn::Error::new_spanned( + first_attr, + "`#[default]` used here", + )); + + err.extend([second_attr].into_iter().chain(additional_attrs).map( + |variant| { + syn::Error::new_spanned(variant, "`#[default]` used again here") + }, + )); + + return err.into_compile_error().into(); + } + + let variant_ident = &default_variant.ident; + + let fully_qualified_variant_path = quote!(Self::#variant_ident); + + match &default_variant.fields { + Fields::Named(named) => { + let fields = + named.named.iter().map(|field| &field.ident).map(|ident| { + quote_spanned! {ident.span()=> + #ident: ::core::default::Default::default() + } + }); + + quote!(#fully_qualified_variant_path { #( #fields, )* }) } - }); - - quote!(Self( #( #fields, )* )) - } - Fields::Unit => { - quote!(Self) - } - }, - Data::Enum(enum_) => { - if enum_.variants.is_empty() { - return syn::Error::new_spanned(name, "cannot derive Default for an empty enum") - .to_compile_error() - .into(); + Fields::Unnamed(unnamed) => { + let fields = unnamed.unnamed.iter().map(|field| { + quote_spanned! {field.span()=> + ::core::default::Default::default() + } + }); + + quote!(#fully_qualified_variant_path( #( #fields, )* )) + } + Fields::Unit => fully_qualified_variant_path, + } } + [first, additional @ ..] => { + let mut err = syn::Error::new(Span::call_site(), "multiple declared defaults"); - // all #[default] attrs with the variant they're on; i.e. a var - let default_variants = enum_ - .variants - .into_iter() - .filter(|variant| { - variant - .attrs - .iter() - .any(|attr| attr.path().is_ident("default")) - }) - .collect::>(); - - match &*default_variants { - [] => return syn::Error::new( - name.clone().span(), - "no default declared, make a variant default by placing `#[default]` above it", - ) - .into_compile_error() - .into(), - // only one variant with the #[default] attribute set - [default_variant] => { - let variant_attrs = default_variant - .attrs - .iter() - .filter(|a| a.path().is_ident("default")) - .collect::>(); - - // check that there is only one #[default] attribute on the variant - if let [first_attr, second_attr, additional_attrs @ ..] = &*variant_attrs { - let mut err = - syn::Error::new(Span::call_site(), "multiple `#[default]` attributes"); - - err.combine(syn::Error::new_spanned(first_attr, "`#[default]` used here")); - - err.extend([second_attr].into_iter().chain(additional_attrs).map( - |variant| { - syn::Error::new_spanned(variant, "`#[default]` used again here") - }, - )); - - return err.into_compile_error().into() - } - - let variant_ident = &default_variant.ident; - - let fully_qualified_variant_path = quote!(Self::#variant_ident); - - match &default_variant.fields { - Fields::Named(named) => { - let fields = - named.named.iter().map(|field| &field.ident).map(|ident| { - quote_spanned! {ident.span()=> - #ident: ::core::default::Default::default() - } - }); - - quote!(#fully_qualified_variant_path { #( #fields, )* }) - }, - Fields::Unnamed(unnamed) => { - let fields = unnamed.unnamed.iter().map(|field| { - quote_spanned! {field.span()=> - ::core::default::Default::default() - } - }); - - quote!(#fully_qualified_variant_path( #( #fields, )* )) - }, - Fields::Unit => fully_qualified_variant_path, - } - }, - [first, additional @ ..] => { - let mut err = syn::Error::new(Span::call_site(), "multiple declared defaults"); - - err.combine(syn::Error::new_spanned(first, "first default")); - - err.extend( - additional - .into_iter() - .map(|variant| syn::Error::new_spanned(variant, "additional default")), - ); - - return err.into_compile_error().into() - }, - } - } - Data::Union(union_) => { - return syn::Error::new_spanned( - union_.union_token, - "Union type not supported by `derive(DefaultNoBound)`", - ) - .to_compile_error() - .into() + err.combine(syn::Error::new_spanned(first, "first default")); + + err.extend( + additional + .into_iter() + .map(|variant| syn::Error::new_spanned(variant, "additional default")), + ); + + return err.into_compile_error().into(); + } } - }; + } + Data::Union(union_) => { + return syn::Error::new_spanned( + union_.union_token, + "Union type not supported by `derive(DefaultNoBound)`", + ) + .to_compile_error() + .into() + } + }; quote!( const _: () = { From be0312cbf2f2106a1c10c9349508eb942f5b9bbf Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Thu, 19 Sep 2024 14:37:53 -0400 Subject: [PATCH 49/53] fix newline --- Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a9788a222..a5b7f2f61 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,8 @@ members = [ "runtime", "support/tools", "support/macros", - "support/linting", "support/procedural-fork", + "support/linting", + "support/procedural-fork", ] resolver = "2" From c368ce34c794249a20985a8cd1cd13de938871c4 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 23 Sep 2024 14:15:38 -0400 Subject: [PATCH 50/53] exclude procedural-fork tests --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index a5b7f2f61..4d77745a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ members = [ "support/linting", "support/procedural-fork", ] +exclude = ["support/procedural-fork"] resolver = "2" [workspace.lints.clippy] From 056413898bffea4073385fd9d68909d01ba366c8 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 23 Sep 2024 15:13:33 -0400 Subject: [PATCH 51/53] ignore procedural-fork tests via a cfg gate --- support/procedural-fork/src/lib.rs | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/support/procedural-fork/src/lib.rs b/support/procedural-fork/src/lib.rs index cef3891a0..c7f1472b2 100644 --- a/support/procedural-fork/src/lib.rs +++ b/support/procedural-fork/src/lib.rs @@ -15,40 +15,60 @@ extern crate proc_macro; +#[cfg(not(test))] mod benchmark; +#[cfg(not(test))] mod construct_runtime; +#[cfg(not(test))] mod crate_version; +#[cfg(not(test))] mod derive_impl; +#[cfg(not(test))] mod dummy_part_checker; +#[cfg(not(test))] mod dynamic_params; +#[cfg(not(test))] mod key_prefix; +#[cfg(not(test))] mod match_and_insert; +#[cfg(not(test))] mod no_bound; +#[cfg(not(test))] mod pallet; +#[cfg(not(test))] mod pallet_error; +#[cfg(not(test))] mod runtime; +#[cfg(not(test))] mod storage_alias; +#[cfg(not(test))] mod transactional; +#[cfg(not(test))] mod tt_macro; - +#[cfg(not(test))] use std::{cell::RefCell, str::FromStr}; +#[cfg(not(test))] pub(crate) const INHERENT_INSTANCE_NAME: &str = "__InherentHiddenInstance"; /// The number of module instances supported by the runtime, starting at index 1, /// and up to `NUMBER_OF_INSTANCE`. +#[cfg(not(test))] pub(crate) const NUMBER_OF_INSTANCE: u8 = 16; thread_local! { /// A global counter, can be used to generate a relatively unique identifier. + #[cfg(not(test))] static COUNTER: RefCell = const { RefCell::new(Counter(0)) }; } /// Counter to generate a relatively unique identifier for macros. This is necessary because /// declarative macros gets hoisted to the crate root, which shares the namespace with other pallets /// containing the very same macros. +#[cfg(not(test))] struct Counter(u64); +#[cfg(not(test))] impl Counter { fn inc(&mut self) -> u64 { let ret = self.0; @@ -60,6 +80,7 @@ impl Counter { /// Get the value from the given environment variable set by cargo. /// /// The value is parsed into the requested destination type. +#[cfg(not(test))] fn get_cargo_env_var(version_env: &str) -> std::result::Result { let version = std::env::var(version_env) .unwrap_or_else(|_| panic!("`{}` is always set by cargo; qed", version_env)); @@ -69,10 +90,12 @@ fn get_cargo_env_var(version_env: &str) -> std::result::Result String { format!("CounterFor{}", prefix) } +#[cfg(not(test))] pub mod exports { pub mod benchmark { pub use crate::benchmark::*; From 4dd15c9a15ba4a403a5cf721b86712f7c5196cea Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 23 Sep 2024 15:15:12 -0400 Subject: [PATCH 52/53] fix lint allow --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4d77745a8..faa8a783f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,7 +43,6 @@ indexing-slicing = "deny" arithmetic-side-effects = "deny" type_complexity = "allow" unwrap-used = "deny" -manual_inspect = "allow" [workspace.dependencies] cargo-husky = { version = "1", default-features = false } From c42864164721f80865a419489d61ba38f247d8af Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 23 Sep 2024 15:25:18 -0400 Subject: [PATCH 53/53] revert lint change --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index faa8a783f..4d77745a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ indexing-slicing = "deny" arithmetic-side-effects = "deny" type_complexity = "allow" unwrap-used = "deny" +manual_inspect = "allow" [workspace.dependencies] cargo-husky = { version = "1", default-features = false }