Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Add pallet attribute macro to declare pallets #6877

Merged
47 commits merged into from
Dec 24, 2020
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
b67c866
rename system Config to system Trait.
gui1117 Oct 27, 2020
336d3c9
make construct_runtime handle Pallet and Module
gui1117 Oct 27, 2020
942c161
introduce pallet attribute macro
gui1117 Oct 27, 2020
1e1e8a3
allow to print some upgrade helper from decl_storage
gui1117 Oct 29, 2020
0d9bb3e
Improved error msg, typo.
gui1117 Nov 19, 2020
b48cfc6
Improved error msg, typo.
gui1117 Nov 19, 2020
cc165a3
Improved error message on unexpected attributes + ui test
gui1117 Nov 19, 2020
460ccf9
add test for transactional
gui1117 Nov 19, 2020
5ced0a8
various typo
gui1117 Nov 19, 2020
688f044
some tips when spans are lost
gui1117 Nov 23, 2020
c5ef21f
allow pallet to depend on other pallet instances
gui1117 Nov 23, 2020
240a2a7
make event type metadata consistent with call and constant
gui1117 Nov 23, 2020
1229c33
error messages
gui1117 Nov 23, 2020
30000d0
Merge remote-tracking branch 'origin/master' into gui-macro-attribute
gui1117 Nov 23, 2020
c2148b0
ignore doc example
gui1117 Nov 23, 2020
e5485c8
fix pallet upgrade template
gui1117 Nov 23, 2020
1ff085e
Merge remote-tracking branch 'origin/master' into gui-macro-attribute
gui1117 Nov 25, 2020
d5b745f
fixup
gui1117 Nov 25, 2020
8e8ee8d
fix doc
gui1117 Nov 25, 2020
3ea926c
fix indentation
gui1117 Nov 25, 2020
bd99a8d
Apply suggestions code formatting
gui1117 Nov 30, 2020
0adbb30
some renames + fix compilation
gui1117 Nov 30, 2020
c96e1cf
remove unsupported genesis config type alias
gui1117 Nov 30, 2020
04a54d6
Merge remote-tracking branch 'origin/master' into gui-macro-attribute
gui1117 Nov 30, 2020
78789d6
merge fixup
gui1117 Nov 30, 2020
96845d8
fix ui tests
gui1117 Dec 2, 2020
1762c47
additional doc
gui1117 Dec 2, 2020
a03a4b4
Merge remote-tracking branch 'origin/master' into gui-macro-attribute
gui1117 Dec 2, 2020
4cb71d8
Merge remote-tracking branch 'origin/master' into gui-macro-attribute
gui1117 Dec 3, 2020
cf7ae7d
implement StorageInstance with new syntax
gui1117 Dec 3, 2020
88e6995
fix line width
gui1117 Dec 3, 2020
d1ec690
Merge remote-tracking branch 'origin/master' into gui-macro-attribute
gui1117 Dec 3, 2020
3a29683
fix doc: because pallet doc goes below reexport doc
gui1117 Dec 4, 2020
9430b34
Merge remote-tracking branch 'origin/master' into HEAD
gui1117 Dec 9, 2020
888d121
Update frame/support/procedural/src/pallet/parse/event.rs
gui1117 Dec 9, 2020
6fa68a9
Merge remote-tracking branch 'origin/master' into gui-macro-attribute
gui1117 Dec 9, 2020
0c6f916
Merge remote-tracking branch 'origin/master' into gui-macro-attribute
gui1117 Dec 14, 2020
52a654f
Update frame/system/src/lib.rs
gui1117 Dec 23, 2020
03c7fbf
Update frame/support/test/tests/pallet_ui.rs
gui1117 Dec 23, 2020
3f23d3c
improve doc as suggested
gui1117 Dec 23, 2020
d9d48c7
revert construct_runtime Pallet part.
gui1117 Dec 23, 2020
0d56f2b
refactor with less intricated code
gui1117 Dec 23, 2020
d9fdc4b
Merge remote-tracking branch 'origin/master' into gui-macro-attribute
gui1117 Dec 23, 2020
98cdc0e
fix ui test with new image
gui1117 Dec 23, 2020
ae38859
fix ui tests
gui1117 Dec 23, 2020
c158e5f
add minor tests
gui1117 Dec 24, 2020
10e8a92
Merge remote-tracking branch 'origin/master' into gui-macro-attribute
gui1117 Dec 24, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 40 additions & 19 deletions frame/support/procedural/src/construct_runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,17 @@ use proc_macro2::{TokenStream as TokenStream2};
use quote::quote;
use syn::{Ident, Result, TypePath};
use std::collections::HashMap;
use syn::spanned::Spanned;

/// The fixed name of the system module.
const SYSTEM_MODULE_NAME: &str = "System";

/// The complete definition of a module with the resulting fixed index.
#[derive(Debug, Clone)]
pub struct Module {
/// A Pallet use either Module (if defined with old macros) or Pallet.
/// This contains the struct to access Pallet information.
pub pallet_or_module: syn::Ident,
pub name: Ident,
pub index: u8,
pub module: Ident,
Expand Down Expand Up @@ -88,7 +92,29 @@ fn complete_modules(decl: impl Iterator<Item = ModuleDeclaration>) -> syn::Resul
return Err(err);
}

// Get if pallet use struct Module or Pallet.
let module_part = module.module_parts.iter().find(|part| part.name() == "Module");
let pallet_part = module.module_parts.iter().find(|part| part.name() == "Pallet");
let pallet_or_module = match (module_part, pallet_part) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before this was no requirement, why is it now required to have this?

Actually it was a very bad idea back in the days to introduce Module, as we enforce this anyway. Aka, a pallet/module without the struct doesn't work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah I see, I used this part Module to identify if we should use Module or Pallet. But actually it is not its usage.
if part Module exist then just metadata will contains some module associated metadata.

I think it is indeed better not to change this. Instead I should have new macro generating a type alias to Module. and Have construct_runtime use Module as before.
Later when we deprecate old macros we can factorize it but a type alias here is not an issue.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I make pallet generating a type alias Module and revert all changes to construct_runtime.

I think it is cleaner too, refactoring of construct_runtime can be done later and differently

(Some(module_part), None) => syn::Ident::new("Module", module_part.keyword.span()),
(None, Some(pallet_part)) => syn::Ident::new("Pallet", pallet_part.keyword.span()),
(None, None) => {
let msg = "Cannot find part `Module` nor `Pallet`, one must be defined";
return Err(syn::Error::new(module.name.span(), msg));
},
(Some(module_part), Some(pallet_part)) => {
let msg = "`Module` cannot be defined when `Pallet` is defined";
let mut err = syn::Error::new(module_part.keyword.span(), msg);
let msg1 = "`Module` cannot be defined when `Pallet` is defined";
let err1 = syn::Error::new(pallet_part.keyword.span(), msg1);
err.combine(err1);
return Err(err);
},
};


Ok(Module {
pallet_or_module,
name: module.name,
index: final_index,
module: module.module,
Expand Down Expand Up @@ -295,30 +321,22 @@ fn decl_runtime_metadata<'a>(
extrinsic: &TypePath,
) -> TokenStream2 {
let modules_tokens = module_declarations
.filter_map(|module_declaration| {
module_declaration.find_part("Module").map(|_| {
let filtered_names: Vec<_> = module_declaration
.module_parts()
.into_iter()
.filter(|part| part.name() != "Module")
.map(|part| part.ident())
.collect();
(module_declaration, filtered_names)
})
})
.map(|(module_declaration, filtered_names)| {
.map(|module_declaration| {
let parts = module_declaration.module_parts().into_iter()
.map(|part| part.ident());

let module = &module_declaration.module;
let name = &module_declaration.name;
let instance = module_declaration
.instance
.as_ref()
.map(|name| quote!(<#name>))
.into_iter();
let instance = module_declaration.instance.as_ref()
.map(|name| quote!(<#name>));

let pallet_or_module = &module_declaration.pallet_or_module;

let index = module_declaration.index;

quote!(
#module::Module #(#instance)* as #name { index #index } with #(#filtered_names)*,
// Note: impl_runtime_metadata can be given all parts, no need to filter out any part
#module::#pallet_or_module #instance as #name { index #index } with #(#parts)*,
)
});
quote!(
Expand Down Expand Up @@ -450,8 +468,11 @@ fn decl_all_modules<'a>(
.iter()
.map(|name| quote!(#module::#name)),
);

let pallet_or_module = &module_declaration.pallet_or_module;

let type_decl = quote!(
pub type #type_name = #module::Module <#(#generics),*>;
pub type #type_name = #module::#pallet_or_module <#(#generics),*>;
);
types.extend(type_decl);
names.push(&module_declaration.name);
Expand Down
6 changes: 6 additions & 0 deletions frame/support/procedural/src/construct_runtime/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ mod keyword {
syn::custom_keyword!(NodeBlock);
syn::custom_keyword!(UncheckedExtrinsic);
syn::custom_keyword!(Module);
syn::custom_keyword!(Pallet);
syn::custom_keyword!(Call);
syn::custom_keyword!(Storage);
syn::custom_keyword!(Event);
Expand Down Expand Up @@ -221,6 +222,7 @@ fn parse_module_parts(input: ParseStream) -> Result<Vec<ModulePart>> {
#[derive(Debug, Clone)]
pub enum ModulePartKeyword {
Module(keyword::Module),
Pallet(keyword::Pallet),
Call(keyword::Call),
Storage(keyword::Storage),
Event(keyword::Event),
Expand All @@ -236,6 +238,8 @@ impl Parse for ModulePartKeyword {

if lookahead.peek(keyword::Module) {
Ok(Self::Module(input.parse()?))
} else 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) {
Expand All @@ -261,6 +265,7 @@ impl ModulePartKeyword {
fn name(&self) -> &'static str {
match self {
Self::Module(_) => "Module",
Self::Pallet(_) => "Pallet",
Self::Call(_) => "Call",
Self::Storage(_) => "Storage",
Self::Event(_) => "Event",
Expand Down Expand Up @@ -291,6 +296,7 @@ impl Spanned for ModulePartKeyword {
fn span(&self) -> Span {
match self {
Self::Module(inner) => inner.span(),
Self::Pallet(inner) => inner.span(),
Self::Call(inner) => inner.span(),
Self::Storage(inner) => inner.span(),
Self::Event(inner) => inner.span(),
Expand Down
11 changes: 10 additions & 1 deletion frame/support/procedural/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@

mod storage;
mod construct_runtime;
mod pallet;
mod pallet_version;
mod transactional;
mod debug_no_bound;
mod clone_no_bound;
mod partial_eq_no_bound;

pub(crate) use storage::INHERENT_INSTANCE_NAME;
use proc_macro::TokenStream;

/// Declares strongly-typed wrappers around codec-compatible types in storage.
Expand Down Expand Up @@ -274,7 +276,8 @@ pub fn decl_storage(input: TokenStream) -> TokenStream {
///
/// We provide support for the following module parts in a pallet:
///
/// - `Module`
/// - `Pallet` or `Module`, mandatory. (one or the other must be provided depending if the pallet
/// uses the pallet attribute macro or old `decl_module` macro.
/// - `Call`
/// - `Storage`
/// - `Event` or `Event<T>` (if the event is generic)
Expand Down Expand Up @@ -305,6 +308,12 @@ pub fn construct_runtime(input: TokenStream) -> TokenStream {
construct_runtime::construct_runtime(input)
}

/// Macro to define a pallet. Docs are at `frame_support::pallet`.
#[proc_macro_attribute]
pub fn pallet(attr: TokenStream, item: TokenStream) -> TokenStream {
pallet::pallet(attr, item)
}

/// Execute the annotated function in a new storage transaction.
///
/// The return type of the annotated function must be `Result`. All changes to storage performed
Expand Down
201 changes: 201 additions & 0 deletions frame/support/procedural/src/pallet/expand/call.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
// This file is part of Substrate.

// Copyright (C) 2020 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 frame_support_procedural_tools::clean_type_string;
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 frame_support = &def.frame_support;
let frame_system = &def.frame_system;
let type_impl_gen = &def.type_impl_generics();
let type_decl_bounded_gen = &def.type_decl_bounded_generics();
let type_use_gen = &def.type_use_generics();
let call_ident = syn::Ident::new("Call", def.call.attr_span.clone());
let pallet_ident = &def.pallet_struct.pallet;
let where_clause = &def.call.where_clause;

let fn_name = def.call.methods.iter().map(|method| &method.name).collect::<Vec<_>>();

let fn_weight = def.call.methods.iter().map(|method| &method.weight);

let fn_doc = def.call.methods.iter().map(|method| &method.docs).collect::<Vec<_>>();

let args_name = def.call.methods.iter()
.map(|method| method.args.iter().map(|(_, name, _)| name.clone()).collect::<Vec<_>>())
.collect::<Vec<_>>();

let args_type = def.call.methods.iter()
.map(|method| method.args.iter().map(|(_, _, type_)| type_.clone()).collect::<Vec<_>>())
.collect::<Vec<_>>();

let args_compact_attr = def.call.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::<Vec<_>>()
});

let args_metadata_type = def.call.methods.iter().map(|method| {
method.args.iter()
.map(|(is_compact, _, type_)| {
let final_type = if *is_compact {
quote::quote!(Compact<#type_>)
} else {
quote::quote!(#type_)
};
clean_type_string(&final_type.to_string())
})
.collect::<Vec<_>>()
});

quote::quote_spanned!(def.call.attr_span =>
#[derive(
#frame_support::RuntimeDebugNoBound,
#frame_support::CloneNoBound,
#frame_support::EqNoBound,
#frame_support::PartialEqNoBound,
#frame_support::codec::Encode,
#frame_support::codec::Decode,
)]
#[allow(non_camel_case_types)]
pub enum #call_ident<#type_decl_bounded_gen> #where_clause {
#[doc(hidden)]
#[codec(skip)]
__Ignore(
#frame_support::sp_std::marker::PhantomData<(#type_use_gen,)>,
#frame_support::Never,
),
#( #fn_name( #( #args_compact_attr #args_type ),* ), )*
}

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 {
#(
Self::#fn_name ( #( ref #args_name, )* ) => {
let base_weight = #fn_weight;

let weight = <
dyn #frame_support::dispatch::WeighData<( #( & #args_type, )* )>
>::weigh_data(&base_weight, ( #( #args_name, )* ));

let class = <
dyn #frame_support::dispatch::ClassifyDispatch<
( #( & #args_type, )* )
>
>::classify_dispatch(&base_weight, ( #( #args_name, )* ));

let pays_fee = <
dyn #frame_support::dispatch::PaysFee<( #( & #args_type, )* )>
>::pays_fee(&base_weight, ( #( #args_name, )* ));

#frame_support::dispatch::DispatchInfo {
weight,
class,
pays_fee,
}
},
)*
Self::__Ignore(_, _) => unreachable!("__Ignore cannot be used"),
}
}
}

impl<#type_impl_gen> #frame_support::dispatch::GetCallName for #call_ident<#type_use_gen>
#where_clause
{
fn get_call_name(&self) -> &'static str {
match *self {
#( Self::#fn_name(..) => stringify!(#fn_name), )*
Self::__Ignore(_, _) => unreachable!("__PhantomItem cannot be used."),
}
}

fn get_call_names() -> &'static [&'static str] {
&[ #( stringify!(#fn_name), )* ]
}
}

impl<#type_impl_gen> #frame_support::traits::UnfilteredDispatchable
for #call_ident<#type_use_gen>
#where_clause
{
type Origin = #frame_system::pallet_prelude::OriginFor<T>;
fn dispatch_bypass_filter(
self,
origin: Self::Origin
gui1117 marked this conversation as resolved.
Show resolved Hide resolved
) -> #frame_support::dispatch::DispatchResultWithPostInfo {
match self {
#(
Self::#fn_name( #( #args_name, )* ) =>
<#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<T> for #pallet_ident<#type_use_gen>
#where_clause
{
type Call = #call_ident<#type_use_gen>;
}

impl<#type_impl_gen> #pallet_ident<#type_use_gen> #where_clause {
#[doc(hidden)]
pub fn call_functions() -> &'static [#frame_support::dispatch::FunctionMetadata] {
&[ #(
#frame_support::dispatch::FunctionMetadata {
name: #frame_support::dispatch::DecodeDifferent::Encode(
stringify!(#fn_name)
),
arguments: #frame_support::dispatch::DecodeDifferent::Encode(
&[ #(
#frame_support::dispatch::FunctionArgumentMetadata {
name: #frame_support::dispatch::DecodeDifferent::Encode(
stringify!(#args_name)
),
ty: #frame_support::dispatch::DecodeDifferent::Encode(
#args_metadata_type
),
},
)* ]
),
documentation: #frame_support::dispatch::DecodeDifferent::Encode(
&[ #( #fn_doc ),* ]
),
},
)* ]
}
}
)
}
Loading