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

Commit

Permalink
Implement #[pallet::hold_reason]
Browse files Browse the repository at this point in the history
  • Loading branch information
KiChjang committed Mar 27, 2023
1 parent 1184143 commit 127e0bb
Show file tree
Hide file tree
Showing 12 changed files with 209 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -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 crate::construct_runtime::{parse::PalletPath, Pallet};
use proc_macro2::{Ident, TokenStream};
use quote::quote;

pub fn expand_outer_hold_reason(pallet_decls: &[Pallet]) -> TokenStream {
let mut conversion_fns = Vec::new();
let hold_reason_variants = pallet_decls.iter().filter_map(|decl| {
decl.find_part("HoldReason").map(|_| {
let variant_name = &decl.name;
let path = &decl.path;
let index = &decl.index;

conversion_fns.push(expand_conversion_fn(path, variant_name));

quote! {
#[codec(index = #index)]
#variant_name(#path::HoldReason),
}
})
});

quote! {
#[derive(
Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, MaxEncodedLen, Debug,
TypeInfo,
)]
pub enum RuntimeHoldReason {
#( #hold_reason_variants )*
}

#( #conversion_fns )*
}
}

fn expand_conversion_fn(path: &PalletPath, variant_name: &Ident) -> TokenStream {
quote! {
impl From<#path::HoldReason> for RuntimeHoldReason {
fn from(hr: #path::HoldReason) -> Self {
RuntimeHoldReason::#variant_name
}
}
}
}
2 changes: 2 additions & 0 deletions frame/support/procedural/src/construct_runtime/expand/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
mod call;
mod config;
mod event;
mod hold_reason;
mod inherent;
mod metadata;
mod origin;
Expand All @@ -26,6 +27,7 @@ mod unsigned;
pub use call::expand_outer_dispatch;
pub use config::expand_outer_config;
pub use event::expand_outer_event;
pub use hold_reason::expand_outer_hold_reason;
pub use inherent::expand_outer_inherent;
pub use metadata::expand_runtime_metadata;
pub use origin::expand_outer_origin;
Expand Down
3 changes: 3 additions & 0 deletions frame/support/procedural/src/construct_runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ fn construct_runtime_final_expansion(
let inherent =
expand::expand_outer_inherent(&name, &block, &unchecked_extrinsic, &pallets, &scrate);
let validate_unsigned = expand::expand_outer_validate_unsigned(&name, &pallets, &scrate);
let hold_reason = expand::expand_outer_hold_reason(&pallets);
let integrity_test = decl_integrity_test(&scrate);
let static_assertions = decl_static_assertions(&name, &pallets, &scrate);

Expand Down Expand Up @@ -307,6 +308,8 @@ fn construct_runtime_final_expansion(

#validate_unsigned

#hold_reason

#integrity_test

#static_assertions
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 @@ -38,6 +38,7 @@ mod keyword {
syn::custom_keyword!(Origin);
syn::custom_keyword!(Inherent);
syn::custom_keyword!(ValidateUnsigned);
syn::custom_keyword!(HoldReason);
syn::custom_keyword!(exclude_parts);
syn::custom_keyword!(use_parts);
}
Expand Down Expand Up @@ -370,6 +371,7 @@ pub enum PalletPartKeyword {
Origin(keyword::Origin),
Inherent(keyword::Inherent),
ValidateUnsigned(keyword::ValidateUnsigned),
HoldReason(keyword::HoldReason),
}

impl Parse for PalletPartKeyword {
Expand All @@ -392,6 +394,8 @@ impl Parse for PalletPartKeyword {
Ok(Self::Inherent(input.parse()?))
} else if lookahead.peek(keyword::ValidateUnsigned) {
Ok(Self::ValidateUnsigned(input.parse()?))
} else if lookahead.peek(keyword::HoldReason) {
Ok(Self::HoldReason(input.parse()?))
} else {
Err(lookahead.error())
}
Expand All @@ -410,6 +414,7 @@ impl PalletPartKeyword {
Self::Origin(_) => "Origin",
Self::Inherent(_) => "Inherent",
Self::ValidateUnsigned(_) => "ValidateUnsigned",
Self::HoldReason(_) => "HoldReason",
}
}

Expand All @@ -435,6 +440,7 @@ impl Spanned for PalletPartKeyword {
Self::Origin(inner) => inner.span(),
Self::Inherent(inner) => inner.span(),
Self::ValidateUnsigned(inner) => inner.span(),
Self::HoldReason(inner) => inner.span(),
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion frame/support/procedural/src/pallet/parse/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ impl EventDef {
let item = if let syn::Item::Enum(item) = item {
item
} else {
return Err(syn::Error::new(item.span(), "Invalid pallet::event, expected item enum"))
return Err(syn::Error::new(item.span(), "Invalid pallet::event, expected enum item"))
};

let event_attrs: Vec<PalletEventDepositAttr> =
Expand Down
83 changes: 83 additions & 0 deletions frame/support/procedural/src/pallet/parse/hold_reason.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// 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;

mod keyword {
syn::custom_keyword!(HoldReason);
}

pub struct HoldReasonDef {
/// The index of the HoldReason item in the pallet module.
pub index: usize,
/// The HoldReason keyword used (contains span).
pub hold_reason: keyword::HoldReason,
/// The span of the pallet::hold_reason attribute.
pub attr_span: proc_macro2::Span,
}

impl HoldReasonDef {
pub fn try_from(
attr_span: proc_macro2::Span,
index: usize,
item: &mut syn::Item,
) -> syn::Result<Self> {
let item = if let syn::Item::Enum(item) = item {
item
} else {
return Err(syn::Error::new(
item.span(),
"Invalid pallet::hold_reason, expected enum item",
))
};

if !matches!(item.vis, syn::Visibility::Public(_)) {
let msg = "Invalid pallet::hold_reason, `HoldReason` must be public";
return Err(syn::Error::new(item.span(), msg))
}

let has_derive_attr = item
.attrs
.iter()
.find(|attr| {
attr.parse_meta()
.ok()
.map(|meta| match meta {
syn::Meta::List(syn::MetaList { path, .. }) =>
path.get_ident().map(|ident| ident == "derive").unwrap_or(false),
_ => false,
})
.unwrap_or(false)
})
.is_some();

if !has_derive_attr {
let derive_attr: syn::Attribute = syn::parse_quote! {
#[derive(
Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, MaxEncodedLen,
Debug, TypeInfo,
)]
};
item.attrs.push(derive_attr);
}

let hold_reason = syn::parse2::<keyword::HoldReason>(item.ident.to_token_stream())?;

Ok(HoldReasonDef { index, hold_reason, attr_span })
}
}
11 changes: 11 additions & 0 deletions frame/support/procedural/src/pallet/parse/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub mod extra_constants;
pub mod genesis_build;
pub mod genesis_config;
pub mod helper;
pub mod hold_reason;
pub mod hooks;
pub mod inherent;
pub mod origin;
Expand Down Expand Up @@ -56,6 +57,7 @@ pub struct Def {
pub genesis_build: Option<genesis_build::GenesisBuildDef>,
pub validate_unsigned: Option<validate_unsigned::ValidateUnsignedDef>,
pub extra_constants: Option<extra_constants::ExtraConstantsDef>,
pub hold_reason: Option<hold_reason::HoldReasonDef>,
pub type_values: Vec<type_value::TypeValueDef>,
pub frame_system: syn::Ident,
pub frame_support: syn::Ident,
Expand Down Expand Up @@ -89,6 +91,7 @@ impl Def {
let mut genesis_build = None;
let mut validate_unsigned = None;
let mut extra_constants = None;
let mut hold_reason = None;
let mut storages = vec![];
let mut type_values = vec![];

Expand Down Expand Up @@ -135,6 +138,8 @@ impl Def {
Some(PalletAttr::ExtraConstants(_)) =>
extra_constants =
Some(extra_constants::ExtraConstantsDef::try_from(index, item)?),
Some(PalletAttr::HoldReason(span)) =>
hold_reason = Some(hold_reason::HoldReasonDef::try_from(span, index, item)?),
Some(attr) => {
let msg = "Invalid duplicated attribute";
return Err(syn::Error::new(attr.span(), msg))
Expand Down Expand Up @@ -171,6 +176,7 @@ impl Def {
origin,
inherent,
storages,
hold_reason,
type_values,
frame_system,
frame_support,
Expand Down Expand Up @@ -385,6 +391,7 @@ mod keyword {
syn::custom_keyword!(generate_store);
syn::custom_keyword!(Store);
syn::custom_keyword!(extra_constants);
syn::custom_keyword!(hold_reason);
}

/// Parse attributes for item in pallet module
Expand All @@ -404,6 +411,7 @@ enum PalletAttr {
ValidateUnsigned(proc_macro2::Span),
TypeValue(proc_macro2::Span),
ExtraConstants(proc_macro2::Span),
HoldReason(proc_macro2::Span),
}

impl PalletAttr {
Expand All @@ -423,6 +431,7 @@ impl PalletAttr {
Self::ValidateUnsigned(span) => *span,
Self::TypeValue(span) => *span,
Self::ExtraConstants(span) => *span,
Self::HoldReason(span) => *span,
}
}
}
Expand Down Expand Up @@ -464,6 +473,8 @@ impl syn::parse::Parse for PalletAttr {
Ok(PalletAttr::TypeValue(content.parse::<keyword::type_value>()?.span()))
} else if lookahead.peek(keyword::extra_constants) {
Ok(PalletAttr::ExtraConstants(content.parse::<keyword::extra_constants>()?.span()))
} else if lookahead.peek(keyword::hold_reason) {
Ok(PalletAttr::HoldReason(content.parse::<keyword::hold_reason>()?.span()))
} else {
Err(lookahead.error())
}
Expand Down
5 changes: 5 additions & 0 deletions frame/support/test/tests/pallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,11 @@ pub mod pallet {
}
}

#[pallet::hold_reason]
pub enum HoldReason {
Staking,
}

#[derive(codec::Encode, sp_runtime::RuntimeDebug)]
#[cfg_attr(feature = "std", derive(codec::Decode))]
pub enum InherentError {
Expand Down
14 changes: 14 additions & 0 deletions frame/support/test/tests/pallet_ui/hold_reason_non_enum.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#[frame_support::pallet]
mod pallet {
#[pallet::config]
pub trait Config: frame_system::Config {}

#[pallet::pallet]
pub struct Pallet<T>(core::marker::PhantomData<T>);

#[pallet::hold_reason]
pub struct HoldReason;
}

fn main() {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
error: Invalid pallet::hold_reason, expected enum item
--> tests/pallet_ui/hold_reason_non_enum.rs:10:2
|
10 | pub struct HoldReason;
| ^^^
14 changes: 14 additions & 0 deletions frame/support/test/tests/pallet_ui/hold_reason_not_pub.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#[frame_support::pallet]
mod pallet {
#[pallet::config]
pub trait Config: frame_system::Config {}

#[pallet::pallet]
pub struct Pallet<T>(core::marker::PhantomData<T>);

#[pallet::hold_reason]
enum HoldReason {}
}

fn main() {
}
5 changes: 5 additions & 0 deletions frame/support/test/tests/pallet_ui/hold_reason_not_pub.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
error: Invalid pallet::hold_reason, `HoldReason` must be public
--> tests/pallet_ui/hold_reason_not_pub.rs:10:5
|
10 | enum HoldReason {}
| ^^^^

0 comments on commit 127e0bb

Please sign in to comment.