Allow usage of path in construct_runtime! (#8801)

* Allow usage of path in construct_runtime!

* Fix whitespace

* Fix whitespace

* Make expand_runtime_metadata accept slice instead of Iterator

* Include Call and Event in construct_runtime for testing

* Migrate impl_outer_event to proc macro

* Fix integrity_test_works

* Update UI test expectations

* Factor in module path while generating enum variant or fn names

* Use ParseStream::lookahead for more helpful error messages

* Remove generating outer_event_metadata

* Ensure pallets with different paths but same last path segment can coexist

* Remove unnecessary generated function

* Migrate decl_outer_config to proc macro

* Add default_filter test for expand_outer_origin

* Allow crate, self and super keywords to appear in pallet path

* Add UI test for specifying empty pallet paths in construct_runtime
This commit is contained in:
Keith Yeung
2021-06-01 02:23:41 -07:00
committed by GitHub
parent f85492dcbf
commit 3b1c2f55b2
11 changed files with 1179 additions and 201 deletions
@@ -0,0 +1,123 @@
// This file is part of Substrate.
// Copyright (C) 2021 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::{format_ident, quote};
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 build_storage_calls = TokenStream::new();
for decl in pallet_decls {
if let Some(pallet_entry) = decl.find_part("Config") {
let config = format_ident!("{}Config", decl.name);
let mod_name = decl.pallet.mod_name();
let field_name = if let Some(inst) = decl.instance.as_ref() {
format_ident!("{}_{}", mod_name, inst)
} else {
mod_name
};
let part_is_generic = !pallet_entry.generics.params.is_empty();
types.extend(expand_config_types(runtime, decl, &config, part_is_generic));
fields.extend(quote!(pub #field_name: #config,));
build_storage_calls.extend(expand_config_build_storage_call(scrate, runtime, decl, &field_name));
}
}
quote!{
#types
#[cfg(any(feature = "std", test))]
use #scrate::serde as __genesis_config_serde_import__;
#[cfg(any(feature = "std", test))]
#[derive(#scrate::serde::Serialize, #scrate::serde::Deserialize, Default)]
#[serde(rename_all = "camelCase")]
#[serde(deny_unknown_fields)]
#[serde(crate = "__genesis_config_serde_import__")]
#[allow(non_snake_case)]
pub struct GenesisConfig {
#fields
}
#[cfg(any(feature = "std", test))]
impl #scrate::sp_runtime::BuildStorage for GenesisConfig {
fn assimilate_storage(
&self,
storage: &mut #scrate::sp_runtime::Storage,
) -> std::result::Result<(), String> {
#build_storage_calls
#scrate::BasicExternalities::execute_with_storage(storage, || {
<AllPalletsWithSystem as #scrate::traits::OnGenesis>::on_genesis();
});
Ok(())
}
}
}
}
fn expand_config_types(
runtime: &Ident,
decl: &Pallet,
config: &Ident,
part_is_generic: bool,
) -> TokenStream {
let path = &decl.pallet;
match (decl.instance.as_ref(), part_is_generic) {
(Some(inst), true) => quote!{
#[cfg(any(feature = "std", test))]
pub type #config = #path::GenesisConfig<#runtime, #path::#inst>;
},
(None, true) => quote!{
#[cfg(any(feature = "std", test))]
pub type #config = #path::GenesisConfig<#runtime>;
},
(_, false) => quote!{
#[cfg(any(feature = "std", test))]
pub type #config = #path::GenesisConfig;
},
}
}
fn expand_config_build_storage_call(
scrate: &TokenStream,
runtime: &Ident,
decl: &Pallet,
field_name: &Ident,
) -> TokenStream {
let path = &decl.pallet;
let instance = if let Some(inst) = decl.instance.as_ref() {
quote!(#path::#inst)
} else {
quote!(#path::__InherentHiddenInstance)
};
quote!{
#scrate::sp_runtime::BuildModuleGenesisStorage::
<#runtime, #instance>::build_module_genesis_storage(&self.#field_name, storage)?;
}
}
@@ -0,0 +1,146 @@
// This file is part of Substrate.
// Copyright (C) 2021 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, parse::PalletPath};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{Generics, Ident};
pub fn expand_outer_event(
runtime: &Ident,
pallet_decls: &[Pallet],
scrate: &TokenStream,
) -> syn::Result<TokenStream> {
let mut event_variants = TokenStream::new();
let mut event_conversions = TokenStream::new();
let mut events_metadata = TokenStream::new();
for pallet_decl in pallet_decls {
if let Some(pallet_entry) = pallet_decl.find_part("Event") {
let path = &pallet_decl.pallet;
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 `Event` cannot \
be constructed: pallet `{}` must have generic `Event`",
pallet_decl.name,
);
return Err(syn::Error::new(pallet_decl.name.span(), msg));
}
let part_is_generic = !generics.params.is_empty();
let pallet_event = match (instance, 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),
};
event_variants.extend(expand_event_variant(runtime, path, index, instance, generics));
event_conversions.extend(expand_event_conversion(scrate, path, instance, &pallet_event));
events_metadata.extend(expand_event_metadata(scrate, path, &pallet_event));
}
}
Ok(quote!{
#[derive(
Clone, PartialEq, Eq,
#scrate::codec::Encode,
#scrate::codec::Decode,
#scrate::RuntimeDebug,
)]
#[allow(non_camel_case_types)]
pub enum Event {
#event_variants
}
#event_conversions
})
}
fn expand_event_variant(
runtime: &Ident,
path: &PalletPath,
index: u8,
instance: Option<&Ident>,
generics: &Generics,
) -> TokenStream {
let part_is_generic = !generics.params.is_empty();
let mod_name = &path.mod_name();
match (instance, part_is_generic) {
(Some(inst), true) => {
let variant = format_ident!("{}_{}", mod_name, inst);
quote!(#[codec(index = #index)] #variant(#path::Event<#runtime, #path::#inst>),)
}
(Some(inst), false) => {
let variant = format_ident!("{}_{}", mod_name, inst);
quote!(#[codec(index = #index)] #variant(#path::Event<#path::#inst>),)
}
(None, true) => {
quote!(#[codec(index = #index)] #mod_name(#path::Event<#runtime>),)
}
(None, false) => {
quote!(#[codec(index = #index)] #mod_name(#path::Event),)
}
}
}
fn expand_event_conversion(
scrate: &TokenStream,
path: &PalletPath,
instance: Option<&Ident>,
pallet_event: &TokenStream,
) -> TokenStream {
let mod_name = path.mod_name();
let variant = if let Some(inst) = instance {
format_ident!("{}_{}", mod_name, inst)
} else {
mod_name
};
quote!{
impl From<#pallet_event> for Event {
fn from(x: #pallet_event) -> Self {
Event::#variant(x)
}
}
impl #scrate::sp_std::convert::TryInto<#pallet_event> for Event {
type Error = ();
fn try_into(self) -> #scrate::sp_std::result::Result<#pallet_event, Self::Error> {
match self {
Self::#variant(evt) => Ok(evt),
_ => Err(()),
}
}
}
}
}
fn expand_event_metadata(
scrate: &TokenStream,
path: &PalletPath,
pallet_event: &TokenStream,
) -> TokenStream {
let mod_name = path.mod_name();
quote!{(stringify!(#mod_name), #scrate::event::FnEncode(#pallet_event::metadata)),}
}
@@ -0,0 +1,190 @@
// This file is part of Substrate.
// Copyright (C) 2021 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 crate::construct_runtime::Pallet;
use syn::{Ident, TypePath};
use quote::quote;
pub fn expand_runtime_metadata(
runtime: &Ident,
pallet_declarations: &[Pallet],
scrate: &TokenStream,
extrinsic: &TypePath,
) -> TokenStream {
let modules = 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, scrate, decl);
let calls = expand_pallet_metadata_calls(&filtered_names, runtime, scrate, decl);
let event = expand_pallet_metadata_events(&filtered_names, runtime, scrate, decl);
let constants = expand_pallet_metadata_constants(runtime, scrate, decl);
let errors = expand_pallet_metadata_errors(runtime, scrate, decl);
quote!{
#scrate::metadata::ModuleMetadata {
name: #scrate::metadata::DecodeDifferent::Encode(stringify!(#name)),
index: #index,
storage: #storage,
calls: #calls,
event: #event,
constants: #constants,
errors: #errors,
}
}
})
.collect::<Vec<_>>();
quote!{
impl #runtime {
pub fn metadata() -> #scrate::metadata::RuntimeMetadataPrefixed {
#scrate::metadata::RuntimeMetadataLastVersion {
modules: #scrate::metadata::DecodeDifferent::Encode(&[ #(#modules),* ]),
extrinsic: #scrate::metadata::ExtrinsicMetadata {
version: <#extrinsic as #scrate::sp_runtime::traits::ExtrinsicMetadata>::VERSION,
signed_extensions: <
<
#extrinsic as #scrate::sp_runtime::traits::ExtrinsicMetadata
>::SignedExtensions as #scrate::sp_runtime::traits::SignedExtension
>::identifier()
.into_iter()
.map(#scrate::metadata::DecodeDifferent::Encode)
.collect(),
},
}.into()
}
}
}
}
fn expand_pallet_metadata_storage(
filtered_names: &[&'static str],
runtime: &Ident,
scrate: &TokenStream,
decl: &Pallet,
) -> TokenStream {
if filtered_names.contains(&"Storage") {
let instance = decl.instance.as_ref().into_iter();
let path = &decl.pallet;
quote!{
Some(#scrate::metadata::DecodeDifferent::Encode(
#scrate::metadata::FnEncode(
#path::Pallet::<#runtime #(, #path::#instance)*>::storage_metadata
)
))
}
} else {
quote!(None)
}
}
fn expand_pallet_metadata_calls(
filtered_names: &[&'static str],
runtime: &Ident,
scrate: &TokenStream,
decl: &Pallet,
) -> TokenStream {
if filtered_names.contains(&"Call") {
let instance = decl.instance.as_ref().into_iter();
let path = &decl.pallet;
quote!{
Some(#scrate::metadata::DecodeDifferent::Encode(
#scrate::metadata::FnEncode(
#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.pallet;
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::metadata::DecodeDifferent::Encode(
#scrate::metadata::FnEncode(#pallet_event::metadata)
))
}
} else {
quote!(None)
}
}
fn expand_pallet_metadata_constants(
runtime: &Ident,
scrate: &TokenStream,
decl: &Pallet,
) -> TokenStream {
let path = &decl.pallet;
let instance = decl.instance.as_ref().into_iter();
quote!{
#scrate::metadata::DecodeDifferent::Encode(
#scrate::metadata::FnEncode(
#path::Pallet::<#runtime #(, #path::#instance)*>::module_constants_metadata
)
)
}
}
fn expand_pallet_metadata_errors(
runtime: &Ident,
scrate: &TokenStream,
decl: &Pallet,
) -> TokenStream {
let path = &decl.pallet;
let instance = decl.instance.as_ref().into_iter();
quote!{
#scrate::metadata::DecodeDifferent::Encode(
#scrate::metadata::FnEncode(
<#path::Pallet::<#runtime #(, #path::#instance)*> as #scrate::metadata::ModuleErrorMetadata>::metadata
)
)
}
}
@@ -0,0 +1,26 @@
// This file is part of Substrate.
// Copyright (C) 2021 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 config;
mod event;
mod metadata;
mod origin;
pub use config::expand_outer_config;
pub use event::expand_outer_event;
pub use metadata::expand_runtime_metadata;
pub use origin::expand_outer_origin;
@@ -0,0 +1,341 @@
// This file is part of Substrate.
// Copyright (C) 2021 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, SYSTEM_PALLET_NAME};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{token, Ident, Generics};
pub fn expand_outer_origin(
runtime: &Ident,
pallets: &[Pallet],
pallets_token: token::Brace,
scrate: &TokenStream,
) -> syn::Result<TokenStream> {
let system_pallet = pallets.iter()
.find(|decl| decl.name == SYSTEM_PALLET_NAME)
.ok_or_else(|| syn::Error::new(
pallets_token.span,
"`System` pallet declaration is missing. \
Please add this line: `System: frame_system::{Pallet, Call, Storage, Config, Event<T>},`",
))?;
let mut caller_variants = TokenStream::new();
let mut pallet_conversions = TokenStream::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 path = &pallet_decl.pallet;
let instance = pallet_decl.instance.as_ref();
let index = pallet_decl.index;
let generics = &pallet_entry.generics;
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`",
pallet_decl.name
);
return Err(syn::Error::new(pallet_decl.name.span(), msg));
}
caller_variants.extend(
expand_origin_caller_variant(runtime, path, index, instance, generics),
);
pallet_conversions.extend(
expand_origin_pallet_conversions(scrate, runtime, path, instance, generics),
);
}
}
let system_path = &system_pallet.pallet;
let system_index = system_pallet.index;
Ok(quote!{
// WARNING: All instance must hold the filter `frame_system::Config::BaseCallFilter`, except
// when caller is system Root. One can use `OriginTrait::reset_filter` to do so.
#[derive(Clone)]
pub struct Origin {
caller: OriginCaller,
filter: #scrate::sp_std::rc::Rc<Box<dyn Fn(&<#runtime as #system_path::Config>::Call) -> bool>>,
}
#[cfg(not(feature = "std"))]
impl #scrate::sp_std::fmt::Debug for Origin {
fn fmt(
&self,
fmt: &mut #scrate::sp_std::fmt::Formatter,
) -> #scrate::sp_std::result::Result<(), #scrate::sp_std::fmt::Error> {
fmt.write_str("<wasm:stripped>")
}
}
#[cfg(feature = "std")]
impl #scrate::sp_std::fmt::Debug for Origin {
fn fmt(
&self,
fmt: &mut #scrate::sp_std::fmt::Formatter,
) -> #scrate::sp_std::result::Result<(), #scrate::sp_std::fmt::Error> {
fmt.debug_struct("Origin")
.field("caller", &self.caller)
.field("filter", &"[function ptr]")
.finish()
}
}
impl #scrate::traits::OriginTrait for Origin {
type Call = <#runtime as #system_path::Config>::Call;
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::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::Filter<<#runtime as #system_path::Config>::Call>
>::filter;
self.filter = #scrate::sp_std::rc::Rc::new(Box::new(filter));
}
fn set_caller_from(&mut self, other: impl Into<Self>) {
self.caller = other.into().caller;
}
fn filter_call(&self, call: &Self::Call) -> bool {
(self.filter)(call)
}
fn caller(&self) -> &Self::PalletsOrigin {
&self.caller
}
fn try_with_caller<R>(
mut self,
f: impl FnOnce(Self::PalletsOrigin) -> Result<R, Self::PalletsOrigin>,
) -> Result<R, Self> {
match f(self.caller) {
Ok(r) => Ok(r),
Err(caller) => { self.caller = caller; Err(self) }
}
}
/// Create with system none origin and `frame-system::Config::BaseCallFilter`.
fn none() -> Self {
#system_path::RawOrigin::None.into()
}
/// Create with system root origin and no filter.
fn root() -> Self {
#system_path::RawOrigin::Root.into()
}
/// Create with system signed origin and `frame-system::Config::BaseCallFilter`.
fn signed(by: <#runtime as #system_path::Config>::AccountId) -> Self {
#system_path::RawOrigin::Signed(by).into()
}
}
#[derive(Clone, PartialEq, Eq, #scrate::RuntimeDebug, #scrate::codec::Encode, #scrate::codec::Decode)]
#[allow(non_camel_case_types)]
pub enum OriginCaller {
#[codec(index = #system_index)]
system(#system_path::Origin<#runtime>),
#caller_variants
#[allow(dead_code)]
Void(#scrate::Void)
}
// For backwards compatibility and ease of accessing these functions.
#[allow(dead_code)]
impl Origin {
/// Create with system none origin and `frame-system::Config::BaseCallFilter`.
pub fn none() -> Self {
<Origin as #scrate::traits::OriginTrait>::none()
}
/// Create with system root origin and no filter.
pub fn root() -> Self {
<Origin as #scrate::traits::OriginTrait>::root()
}
/// Create with system signed origin and `frame-system::Config::BaseCallFilter`.
pub fn signed(by: <#runtime as #system_path::Config>::AccountId) -> Self {
<Origin as #scrate::traits::OriginTrait>::signed(by)
}
}
impl From<#system_path::Origin<#runtime>> for OriginCaller {
fn from(x: #system_path::Origin<#runtime>) -> Self {
OriginCaller::system(x)
}
}
impl #scrate::sp_std::convert::TryFrom<OriginCaller> for #system_path::Origin<#runtime> {
type Error = OriginCaller;
fn try_from(x: OriginCaller)
-> #scrate::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 Origin {
/// Convert to runtime origin:
/// * root origin is built with no filter
/// * others use `frame-system::Config::BaseCallFilter`
fn from(x: #system_path::Origin<#runtime>) -> Self {
let o: OriginCaller = x.into();
o.into()
}
}
impl From<OriginCaller> for Origin {
fn from(x: OriginCaller) -> Self {
let mut o = Origin {
caller: x,
filter: #scrate::sp_std::rc::Rc::new(Box::new(|_| true)),
};
// Root has no filter
if !matches!(o.caller, OriginCaller::system(#system_path::Origin::<#runtime>::Root)) {
#scrate::traits::OriginTrait::reset_filter(&mut o);
}
o
}
}
impl Into<#scrate::sp_std::result::Result<#system_path::Origin<#runtime>, Origin>> for Origin {
/// NOTE: converting to pallet origin loses the origin filter information.
fn into(self) -> #scrate::sp_std::result::Result<#system_path::Origin<#runtime>, Self> {
if let OriginCaller::system(l) = self.caller {
Ok(l)
} else {
Err(self)
}
}
}
impl From<Option<<#runtime as #system_path::Config>::AccountId>> for Origin {
/// Convert to runtime origin with caller being system signed or none and use filter
/// `frame-system::Config::BaseCallFilter`.
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,
path: &PalletPath,
index: u8,
instance: Option<&Ident>,
generics: &Generics,
) -> TokenStream {
let part_is_generic = !generics.params.is_empty();
let mod_name = &path.mod_name();
match (instance, part_is_generic) {
(Some(inst), true) => {
let variant = format_ident!("{}_{}", mod_name, inst);
quote!(#[codec(index = #index)] #variant(#path::Origin<#runtime, #path::#inst>),)
}
(Some(inst), false) => {
let variant = format_ident!("{}_{}", mod_name, inst);
quote!(#[codec(index = #index)] #variant(#path::Origin<#path::#inst>),)
}
(None, true) => {
quote!(#[codec(index = #index)] #mod_name(#path::Origin<#runtime>),)
}
(None, false) => {
quote!(#[codec(index = #index)] #mod_name(#path::Origin),)
}
}
}
fn expand_origin_pallet_conversions(
scrate: &TokenStream,
runtime: &Ident,
path: &PalletPath,
instance: Option<&Ident>,
generics: &Generics,
) -> TokenStream {
let mod_name = path.mod_name();
let variant = if let Some(inst) = instance {
format_ident!("{}_{}", mod_name, inst)
} else {
mod_name
};
let part_is_generic = !generics.params.is_empty();
let pallet_origin = match (instance, part_is_generic) {
(Some(inst), true) => quote!(#path::Origin<#runtime, #path::#inst>),
(Some(inst), false) => quote!(#path::Origin<#path::#inst>),
(None, true) => quote!(#path::Origin<#runtime>),
(None, false) => quote!(#path::Origin),
};
quote!{
impl From<#pallet_origin> for OriginCaller {
fn from(x: #pallet_origin) -> Self {
OriginCaller::#variant(x)
}
}
impl From<#pallet_origin> for Origin {
/// Convert to runtime origin using `frame-system::Config::BaseCallFilter`.
fn from(x: #pallet_origin) -> Self {
let x: OriginCaller = x.into();
x.into()
}
}
impl Into<#scrate::sp_std::result::Result<#pallet_origin, Origin>> for Origin {
/// NOTE: converting to pallet origin loses the origin filter information.
fn into(self) -> #scrate::sp_std::result::Result<#pallet_origin, Self> {
if let OriginCaller::#variant(l) = self.caller {
Ok(l)
} else {
Err(self)
}
}
}
impl #scrate::sp_std::convert::TryFrom<OriginCaller> for #pallet_origin {
type Error = OriginCaller;
fn try_from(
x: OriginCaller,
) -> #scrate::sp_std::result::Result<#pallet_origin, OriginCaller> {
if let OriginCaller::#variant(l) = x {
Ok(l)
} else {
Err(x)
}
}
}
}
}
@@ -15,15 +15,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
mod expand;
mod parse;
use frame_support_procedural_tools::syn_ext as ext;
use frame_support_procedural_tools::{generate_crate_access, generate_hidden_includes};
use parse::{PalletDeclaration, RuntimeDefinition, WhereSection, PalletPart};
use parse::{PalletDeclaration, PalletPart, PalletPath, RuntimeDefinition, WhereSection};
use proc_macro::TokenStream;
use proc_macro2::{TokenStream as TokenStream2};
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::{Ident, Result, TypePath};
use syn::{Ident, Result};
use std::collections::HashMap;
/// The fixed name of the system pallet.
@@ -34,7 +35,7 @@ const SYSTEM_PALLET_NAME: &str = "System";
pub struct Pallet {
pub name: Ident,
pub index: u8,
pub pallet: Ident,
pub pallet: PalletPath,
pub instance: Option<Ident>,
pub pallet_parts: Vec<PalletPart>,
}
@@ -134,38 +135,19 @@ fn construct_runtime_parsed(definition: RuntimeDefinition) -> Result<TokenStream
let pallets = complete_pallets(pallets.into_iter())?;
let system_pallet = pallets.iter()
.find(|decl| decl.name == SYSTEM_PALLET_NAME)
.ok_or_else(|| syn::Error::new(
pallets_token.span,
"`System` pallet declaration is missing. \
Please add this line: `System: frame_system::{Pallet, Call, Storage, Config, Event<T>},`",
))?;
let hidden_crate_name = "construct_runtime";
let scrate = generate_crate_access(&hidden_crate_name, "frame-support");
let scrate_decl = generate_hidden_includes(&hidden_crate_name, "frame-support");
let all_but_system_pallets = pallets.iter().filter(|pallet| pallet.name != SYSTEM_PALLET_NAME);
let outer_event = expand::expand_outer_event(&name, &pallets, &scrate)?;
let outer_event = decl_outer_event(
&name,
pallets.iter(),
&scrate,
)?;
let outer_origin = decl_outer_origin(
&name,
all_but_system_pallets,
&system_pallet,
&scrate,
)?;
let outer_origin = expand::expand_outer_origin(&name, &pallets, pallets_token, &scrate)?;
let all_pallets = decl_all_pallets(&name, pallets.iter());
let pallet_to_index = decl_pallet_runtime_setup(&pallets, &scrate);
let dispatch = decl_outer_dispatch(&name, pallets.iter(), &scrate);
let metadata = decl_runtime_metadata(&name, pallets.iter(), &scrate, &unchecked_extrinsic);
let outer_config = decl_outer_config(&name, pallets.iter(), &scrate);
let metadata = expand::expand_runtime_metadata(&name, &pallets, &scrate, &unchecked_extrinsic);
let outer_config = expand::expand_outer_config(&name, &pallets, &scrate);
let inherent = decl_outer_inherent(
&name,
&block,
@@ -262,85 +244,6 @@ fn decl_outer_inherent<'a>(
)
}
fn decl_outer_config<'a>(
runtime: &'a Ident,
pallet_declarations: impl Iterator<Item = &'a Pallet>,
scrate: &'a TokenStream2,
) -> TokenStream2 {
let pallets_tokens = pallet_declarations
.filter_map(|pallet_declaration| {
pallet_declaration.find_part("Config").map(|part| {
let transformed_generics: Vec<_> = part
.generics
.params
.iter()
.map(|param| quote!(<#param>))
.collect();
(pallet_declaration, transformed_generics)
})
})
.map(|(pallet_declaration, generics)| {
let pallet = &pallet_declaration.pallet;
let name = Ident::new(
&format!("{}Config", pallet_declaration.name),
pallet_declaration.name.span(),
);
let instance = pallet_declaration.instance.as_ref().into_iter();
quote!(
#name =>
#pallet #(#instance)* #(#generics)*,
)
});
quote!(
#scrate::impl_outer_config! {
pub struct GenesisConfig for #runtime where AllPalletsWithSystem = AllPalletsWithSystem {
#(#pallets_tokens)*
}
}
)
}
fn decl_runtime_metadata<'a>(
runtime: &'a Ident,
pallet_declarations: impl Iterator<Item = &'a Pallet>,
scrate: &'a TokenStream2,
extrinsic: &TypePath,
) -> TokenStream2 {
let pallets_tokens = pallet_declarations
.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.ident())
.collect();
(pallet_declaration, filtered_names)
})
})
.map(|(pallet_declaration, filtered_names)| {
let pallet = &pallet_declaration.pallet;
let name = &pallet_declaration.name;
let instance = pallet_declaration
.instance
.as_ref()
.map(|name| quote!(<#name>))
.into_iter();
let index = pallet_declaration.index;
quote!(
#pallet::Pallet #(#instance)* as #name { index #index } with #(#filtered_names)*,
)
});
quote!(
#scrate::impl_runtime_metadata!{
for #runtime with pallets where Extrinsic = #extrinsic
#(#pallets_tokens)*
}
)
}
fn decl_outer_dispatch<'a>(
runtime: &'a Ident,
pallet_declarations: impl Iterator<Item = &'a Pallet>,
@@ -349,7 +252,7 @@ fn decl_outer_dispatch<'a>(
let pallets_tokens = pallet_declarations
.filter(|pallet_declaration| pallet_declaration.exists_part("Call"))
.map(|pallet_declaration| {
let pallet = &pallet_declaration.pallet;
let pallet = &pallet_declaration.pallet.inner.segments.last().unwrap();
let name = &pallet_declaration.name;
let index = pallet_declaration.index;
quote!(#[codec(index = #index)] #pallet::#name)
@@ -364,82 +267,6 @@ fn decl_outer_dispatch<'a>(
)
}
fn decl_outer_origin<'a>(
runtime_name: &'a Ident,
pallets_except_system: impl Iterator<Item = &'a Pallet>,
system_pallet: &'a Pallet,
scrate: &'a TokenStream2,
) -> syn::Result<TokenStream2> {
let mut pallets_tokens = TokenStream2::new();
for pallet_declaration in pallets_except_system {
if let Some(pallet_entry) = pallet_declaration.find_part("Origin") {
let pallet = &pallet_declaration.pallet;
let instance = pallet_declaration.instance.as_ref();
let generics = &pallet_entry.generics;
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`",
pallet_declaration.name
);
return Err(syn::Error::new(pallet_declaration.name.span(), msg));
}
let index = pallet_declaration.index;
let tokens = quote!(#[codec(index = #index)] #pallet #instance #generics,);
pallets_tokens.extend(tokens);
}
}
let system_name = &system_pallet.pallet;
let system_index = system_pallet.index;
Ok(quote!(
#scrate::impl_outer_origin! {
pub enum Origin for #runtime_name where
system = #system_name,
system_index = #system_index
{
#pallets_tokens
}
}
))
}
fn decl_outer_event<'a>(
runtime_name: &'a Ident,
pallet_declarations: impl Iterator<Item = &'a Pallet>,
scrate: &'a TokenStream2,
) -> syn::Result<TokenStream2> {
let mut pallets_tokens = TokenStream2::new();
for pallet_declaration in pallet_declarations {
if let Some(pallet_entry) = pallet_declaration.find_part("Event") {
let pallet = &pallet_declaration.pallet;
let instance = pallet_declaration.instance.as_ref();
let generics = &pallet_entry.generics;
if instance.is_some() && generics.params.is_empty() {
let msg = format!(
"Instantiable pallet with no generic `Event` cannot \
be constructed: pallet `{}` must have generic `Event`",
pallet_declaration.name,
);
return Err(syn::Error::new(pallet_declaration.name.span(), msg));
}
let index = pallet_declaration.index;
let tokens = quote!(#[codec(index = #index)] #pallet #instance #generics,);
pallets_tokens.extend(tokens);
}
}
Ok(quote!(
#scrate::impl_outer_event! {
pub enum Event for #runtime_name {
#pallets_tokens
}
}
))
}
fn decl_all_pallets<'a>(
runtime: &'a Ident,
pallet_declarations: impl Iterator<Item = &'a Pallet>,
@@ -16,12 +16,14 @@
// limitations under the License.
use frame_support_procedural_tools::syn_ext as ext;
use proc_macro2::Span;
use proc_macro2::{Span, TokenStream};
use std::collections::HashSet;
use syn::{
ext::IdentExt,
parse::{Parse, ParseStream},
punctuated::Punctuated,
spanned::Spanned,
token, Error, Ident, Result, Token,
token, Error, Ident, Path, PathArguments, PathSegment, Result, Token,
};
mod keyword {
@@ -154,7 +156,7 @@ pub struct PalletDeclaration {
pub name: Ident,
/// Optional fixed index (e.g. `MyPallet ... = 3,`)
pub index: Option<u8>,
pub pallet: Ident,
pub pallet: PalletPath,
pub instance: Option<Ident>,
pub pallet_parts: Vec<PalletPart>,
}
@@ -164,17 +166,16 @@ impl Parse for PalletDeclaration {
let name = input.parse()?;
let _: Token![:] = input.parse()?;
let pallet = input.parse()?;
let instance = if input.peek(Token![::]) && input.peek3(Token![<]) {
let _: Token![::] = input.parse()?;
let instance = if input.peek(Token![<]) {
let _: Token![<] = input.parse()?;
let res = Some(input.parse()?);
let _: Token![>] = input.parse()?;
let _: Token![::] = input.parse()?;
res
} else {
None
};
let _: Token![::] = input.parse()?;
let pallet_parts = parse_pallet_parts(input)?;
let index = if input.peek(Token![=]) {
@@ -198,6 +199,84 @@ impl Parse for PalletDeclaration {
}
}
/// 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 identifers; angle-bracketed or parenthesized segments will
/// result in a parsing error (except when specifying instances)
#[derive(Debug, Clone)]
pub struct PalletPath {
pub inner: Path,
}
impl Parse for PalletPath {
fn parse(input: ParseStream) -> Result<Self> {
let mut lookahead = input.lookahead1();
let mut segments = Punctuated::new();
if lookahead.peek(Token![crate])
|| lookahead.peek(Token![self])
|| lookahead.peek(Token![super])
|| lookahead.peek(Ident)
{
let ident = input.call(Ident::parse_any)?;
segments.push(PathSegment { ident, arguments: PathArguments::None });
let _: Token![::] = input.parse()?;
lookahead = input.lookahead1();
} else {
return Err(lookahead.error());
}
while lookahead.peek(Ident) {
let ident = input.parse()?;
segments.push(PathSegment { ident, arguments: PathArguments::None });
let _: Token![::] = input.parse()?;
lookahead = input.lookahead1();
}
if !lookahead.peek(token::Brace) && !lookahead.peek(Token![<]) {
return Err(lookahead.error());
}
Ok(Self {
inner: Path {
leading_colon: None,
segments,
}
})
}
}
impl PalletPath {
/// Return the snake-cased module name for this path.
pub fn mod_name(&self) -> Ident {
let mut iter = self.inner.segments.iter();
let mut mod_name = match &iter.next().expect("Path should always have 1 segment; qed").ident {
ident if ident == "self" || ident == "super" || ident == "crate" => {
// Skip `crate`, `self` and `super` quasi-keywords when creating the module name
iter.next()
.expect("There must be a path segment pointing to a pallet following \
`crate`, `self` or `super`; qed")
.ident
.clone()
}
ident => ident.clone(),
};
for segment in iter {
mod_name = quote::format_ident!("{}_{}", mod_name, segment.ident);
}
mod_name
}
}
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 }`
@@ -271,11 +350,6 @@ impl PalletPartKeyword {
}
}
/// Returns the name as `Ident`.
fn ident(&self) -> Ident {
Ident::new(self.name(), self.span())
}
/// 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())
@@ -341,11 +415,6 @@ impl PalletPart {
pub fn name(&self) -> &'static str {
self.keyword.name()
}
/// The name of this pallet part as `Ident`.
pub fn ident(&self) -> Ident {
self.keyword.ident()
}
}
fn remove_kind(