Allow additional trait bounds for #[pallet::constant] (#9050)

* Allow additional trait bounds for constants

* Add ui test for constants with additional trait bounds

* Update trait constant ui test

* Import syn::Error

* Use reference instead of cloning

* Add extra invalid bound ui test

* Out or order valid bounds

* Fix ui test

* Fix ui test

* Apply review suggestion about error message
This commit is contained in:
Andrew Jones
2021-06-13 01:36:36 +01:00
committed by GitHub
parent 945bf4f2c1
commit 02930e098b
6 changed files with 96 additions and 30 deletions
@@ -16,6 +16,7 @@
// limitations under the License.
use super::helper;
use core::convert::TryFrom;
use syn::spanned::Spanned;
use quote::ToTokens;
@@ -25,7 +26,6 @@ mod keyword {
syn::custom_keyword!(From);
syn::custom_keyword!(T);
syn::custom_keyword!(I);
syn::custom_keyword!(Get);
syn::custom_keyword!(config);
syn::custom_keyword!(IsType);
syn::custom_keyword!(Event);
@@ -62,19 +62,41 @@ pub struct ConstMetadataDef {
pub doc: Vec<syn::Lit>,
}
impl syn::parse::Parse for ConstMetadataDef {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let doc = helper::get_doc_literals(&syn::Attribute::parse_outer(input)?);
input.parse::<syn::Token![type]>()?;
let ident = input.parse::<syn::Ident>()?;
input.parse::<syn::Token![:]>()?;
input.parse::<keyword::Get>()?;
input.parse::<syn::Token![<]>()?;
let mut type_ = input.parse::<syn::Type>()?;
type_ = syn::parse2::<syn::Type>(replace_self_by_t(type_.to_token_stream()))
impl TryFrom<&syn::TraitItemType> for ConstMetadataDef {
type Error = syn::Error;
fn try_from(trait_ty: &syn::TraitItemType) -> Result<Self, Self::Error> {
let err = |span, msg|
syn::Error::new(span, format!("Invalid usage of `#[pallet::constant]`: {}", msg));
let doc = helper::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<T>` 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::<syn::Type>(replace_self_by_t(type_arg.to_token_stream()))
.expect("Internal error: replacing `Self` by `T` should result in valid type");
input.parse::<syn::Token![>]>()?;
input.parse::<syn::Token![;]>()?;
Ok(Self { ident, type_, doc })
}
@@ -322,16 +344,8 @@ impl ConfigDef {
if type_attrs_const.len() == 1 {
match trait_item {
syn::TraitItem::Type(type_) => {
let constant = syn::parse2::<ConstMetadataDef>(type_.to_token_stream())
.map_err(|e| {
let error_msg = "Invalid usage of `#[pallet::constant]`, syntax \
must be `type $SomeIdent: Get<$SomeType>;`";
let mut err = syn::Error::new(type_.span(), error_msg);
err.combine(e);
err
})?;
syn::TraitItem::Type(ref type_) => {
let constant = ConstMetadataDef::try_from(type_)?;
consts_metadata.push(constant);
},
_ => {