Unverified Commit 02c93d30 authored by David's avatar David Committed by GitHub
Browse files

Docs and cleanup for utils (#233)

parent 9e966cb3
Pipeline #118521 passed with stages
in 16 minutes and 40 seconds
......@@ -35,7 +35,7 @@ pub fn quote(data: &Data, type_name: &Ident, input: &TokenStream) -> TokenStream
},
},
Data::Enum(ref data) => {
let data_variants = || data.variants.iter().filter(|variant| crate::utils::get_skip(&variant.attrs).is_none());
let data_variants = || data.variants.iter().filter(|variant| !utils::should_skip(&variant.attrs));
if data_variants().count() > 256 {
return Error::new(
......@@ -46,7 +46,7 @@ pub fn quote(data: &Data, type_name: &Ident, input: &TokenStream) -> TokenStream
let recurse = data_variants().enumerate().map(|(i, v)| {
let name = &v.ident;
let index = utils::index(v, i);
let index = utils::variant_index(v, i);
let create = create_instance(
quote! { #type_name :: #name },
......@@ -76,8 +76,8 @@ pub fn quote(data: &Data, type_name: &Ident, input: &TokenStream) -> TokenStream
fn create_decode_expr(field: &Field, name: &str, input: &TokenStream) -> TokenStream {
let encoded_as = utils::get_encoded_as_type(field);
let compact = utils::get_enable_compact(field);
let skip = utils::get_skip(&field.attrs).is_some();
let compact = utils::is_compact(field);
let skip = utils::should_skip(&field.attrs);
let res = quote!(__codec_res_edqy);
......
......@@ -32,9 +32,9 @@ fn encode_single_field(
field_name: TokenStream,
) -> TokenStream {
let encoded_as = utils::get_encoded_as_type(field);
let compact = utils::get_enable_compact(field);
let compact = utils::is_compact(field);
if utils::get_skip(&field.attrs).is_some() {
if utils::should_skip(&field.attrs) {
return Error::new(
Span::call_site(),
"Internal error: cannot encode single field optimisation if skipped"
......@@ -101,8 +101,8 @@ fn encode_fields<F>(
let recurse = fields.iter().enumerate().map(|(i, f)| {
let field = field_name(i, &f.ident);
let encoded_as = utils::get_encoded_as_type(f);
let compact = utils::get_enable_compact(f);
let skip = utils::get_skip(&f.attrs).is_some();
let compact = utils::is_compact(f);
let skip = utils::should_skip(&f.attrs);
if encoded_as.is_some() as u8 + compact as u8 + skip as u8 > 1 {
return Error::new(
......@@ -200,7 +200,7 @@ fn impl_encode(data: &Data, type_name: &Ident) -> TokenStream {
}
},
Data::Enum(ref data) => {
let data_variants = || data.variants.iter().filter(|variant| crate::utils::get_skip(&variant.attrs).is_none());
let data_variants = || data.variants.iter().filter(|variant| !utils::should_skip(&variant.attrs));
if data_variants().count() > 256 {
return Error::new(
......@@ -216,7 +216,7 @@ fn impl_encode(data: &Data, type_name: &Ident) -> TokenStream {
let recurse = data_variants().enumerate().map(|(i, f)| {
let name = &f.ident;
let index = utils::index(f, i);
let index = utils::variant_index(f, i);
match f.fields {
Fields::Named(ref fields) => {
......
......@@ -146,7 +146,7 @@ pub fn encode_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream
&input.data,
parse_quote!(_parity_scale_codec::Encode),
None,
utils::get_dumb_trait_bound(&input.attrs),
utils::has_dumb_trait_bound(&input.attrs),
) {
return e.to_compile_error().into();
}
......@@ -187,7 +187,7 @@ pub fn decode_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream
&input.data,
parse_quote!(_parity_scale_codec::Decode),
Some(parse_quote!(Default)),
utils::get_dumb_trait_bound(&input.attrs),
utils::has_dumb_trait_bound(&input.attrs),
) {
return e.to_compile_error().into();
}
......@@ -242,7 +242,7 @@ pub fn compact_as_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStr
&input.data,
parse_quote!(_parity_scale_codec::CompactAs),
None,
utils::get_dumb_trait_bound(&input.attrs),
utils::has_dumb_trait_bound(&input.attrs),
) {
return e.to_compile_error().into();
}
......@@ -251,7 +251,7 @@ pub fn compact_as_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStr
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
fn val_or_default(field: &Field) -> proc_macro2::TokenStream {
let skip = utils::get_skip(&field.attrs).is_some();
let skip = utils::should_skip(&field.attrs);
if skip {
quote_spanned!(field.span()=> Default::default())
} else {
......
......@@ -21,6 +21,8 @@ use syn::{
Generics, Result, Type, TypePath,
};
use crate::utils;
/// Visits the ast and checks if one of the given idents is found.
struct ContainIdents<'a> {
result: bool,
......@@ -118,14 +120,15 @@ pub fn add(
let codec_types = get_types_to_add_trait_bound(input_ident, data, &ty_params, dumb_trait_bounds)?;
let compact_types = collect_types(&data, needs_has_compact_bound, variant_not_skipped)?
let compact_types = collect_types(&data, utils::is_compact)?
.into_iter()
// Only add a bound if the type uses a generic
.filter(|ty| type_contain_idents(ty, &ty_params))
.collect::<Vec<_>>();
let skip_types = if codec_skip_bound.is_some() {
collect_types(&data, needs_default_bound, variant_not_skipped)?
let needs_default_bound = |f: &syn::Field| utils::should_skip(&f.attrs);
collect_types(&data, needs_default_bound)?
.into_iter()
// Only add a bound if the type uses a generic
.filter(|ty| type_contain_idents(ty, &ty_params))
......@@ -171,11 +174,14 @@ fn get_types_to_add_trait_bound(
if dumb_trait_bound {
Ok(ty_params.iter().map(|t| parse_quote!( #t )).collect())
} else {
let res = collect_types(&data, needs_codec_bound, variant_not_skipped)?
let needs_codec_bound = |f: &syn::Field| !utils::is_compact(f)
&& utils::get_encoded_as_type(f).is_none()
&& !utils::should_skip(&f.attrs);
let res = collect_types(&data, needs_codec_bound)?
.into_iter()
// Only add a bound if the type uses a generic
.filter(|ty| type_contain_idents(ty, &ty_params))
// If a struct is cotaining itself as field type, we can not add this type into the where clause.
// If a struct contains itself as field type, we can not add this type into the where clause.
// This is required to work a round the following compiler bug: https://github.com/rust-lang/rust/issues/47032
.flat_map(|ty| {
find_type_paths_not_start_or_contain_ident(&ty, input_ident)
......@@ -183,7 +189,7 @@ fn get_types_to_add_trait_bound(
.map(|ty| Type::Path(ty.clone()))
// Remove again types that do not contain any of our generic parameters
.filter(|ty| type_contain_idents(ty, &ty_params))
// Add back the original type, as we don't want to loose him.
// Add back the original type, as we don't want to loose it.
.chain(iter::once(ty))
})
// Remove all remaining types that start/contain the input ident to not have them in the where clause.
......@@ -194,28 +200,9 @@ fn get_types_to_add_trait_bound(
}
}
fn needs_codec_bound(field: &syn::Field) -> bool {
!crate::utils::get_enable_compact(field)
&& crate::utils::get_encoded_as_type(field).is_none()
&& crate::utils::get_skip(&field.attrs).is_none()
}
fn needs_has_compact_bound(field: &syn::Field) -> bool {
crate::utils::get_enable_compact(field)
}
fn needs_default_bound(field: &syn::Field) -> bool {
crate::utils::get_skip(&field.attrs).is_some()
}
fn variant_not_skipped(variant: &syn::Variant) -> bool {
crate::utils::get_skip(&variant.attrs).is_none()
}
fn collect_types(
data: &syn::Data,
type_filter: fn(&syn::Field) -> bool,
variant_filter: fn(&syn::Variant) -> bool,
) -> Result<Vec<syn::Type>> {
use syn::*;
......@@ -233,7 +220,7 @@ fn collect_types(
},
Data::Enum(ref data) => data.variants.iter()
.filter(|variant| variant_filter(variant))
.filter(|variant| !utils::should_skip(&variant.attrs))
.flat_map(|variant| {
match &variant.fields {
| Fields::Named(FieldsNamed { named: fields , .. })
......
// Copyright 2018 Parity Technologies
// Copyright 2018-2020 Parity Technologies
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
......@@ -14,11 +14,12 @@
//! Various internal utils.
//!
//! NOTE: attributes finder must be checked using check_attribute first, otherwise macro can panic.
//! NOTE: attributes finder must be checked using check_attribute first,
//! otherwise the macro can panic.
use std::str::FromStr;
use proc_macro2::{TokenStream, Span};
use proc_macro2::TokenStream;
use syn::{
spanned::Spanned,
Meta, NestedMeta, Lit, Attribute, Variant, Field, DeriveInput, Fields, Data, FieldsUnnamed,
......@@ -42,8 +43,10 @@ fn find_meta_item<'a, F, R, I>(itr: I, pred: F) -> Option<R> where
}).next()
}
pub fn index(v: &Variant, i: usize) -> TokenStream {
// look for an index in attributes
/// Look for a `#[scale(index = $int)]` attribute on a variant. If no attribute
/// is found, fall back to the discriminant or just the variant index.
pub fn variant_index(v: &Variant, i: usize) -> TokenStream {
// first look for an attribute
let index = find_meta_item(v.attrs.iter(), |meta| {
if let NestedMeta::Meta(Meta::NameValue(ref nv)) = meta {
if nv.path.is_ident("index") {
......@@ -67,9 +70,10 @@ pub fn index(v: &Variant, i: usize) -> TokenStream {
)
}
pub fn get_encoded_as_type(field_entry: &Field) -> Option<TokenStream> {
// look for an encoded_as in attributes
find_meta_item(field_entry.attrs.iter(), |meta| {
/// Look for a `#[codec(encoded_as = "SomeType")]` outer attribute on the given
/// `Field`.
pub fn get_encoded_as_type(field: &Field) -> Option<TokenStream> {
find_meta_item(field.attrs.iter(), |meta| {
if let NestedMeta::Meta(Meta::NameValue(ref nv)) = meta {
if nv.path.is_ident("encoded_as") {
if let Lit::Str(ref s) = nv.lit {
......@@ -85,9 +89,9 @@ pub fn get_encoded_as_type(field_entry: &Field) -> Option<TokenStream> {
})
}
pub fn get_enable_compact(field_entry: &Field) -> bool {
// look for `encode(compact)` in the attributes
find_meta_item(field_entry.attrs.iter(), |meta| {
/// Look for a `#[codec(compact)]` outer attribute on the given `Field`.
pub fn is_compact(field: &Field) -> bool {
find_meta_item(field.attrs.iter(), |meta| {
if let NestedMeta::Meta(Meta::Path(ref path)) = meta {
if path.is_ident("compact") {
return Some(());
......@@ -98,9 +102,8 @@ pub fn get_enable_compact(field_entry: &Field) -> bool {
}).is_some()
}
// return span of skip if found
pub fn get_skip(attrs: &[Attribute]) -> Option<Span> {
// look for `skip` in the attributes
/// Look for a `#[codec(skip)]` in the given attributes.
pub fn should_skip(attrs: &[Attribute]) -> bool {
find_meta_item(attrs.iter(), |meta| {
if let NestedMeta::Meta(Meta::Path(ref path)) = meta {
if path.is_ident("skip") {
......@@ -109,11 +112,11 @@ pub fn get_skip(attrs: &[Attribute]) -> Option<Span> {
}
None
})
}).is_some()
}
/// Returns if the `dumb_trait_bound` attribute is given in `attrs`.
pub fn get_dumb_trait_bound(attrs: &[Attribute]) -> bool {
/// Look for a `#[codec(dumb_trait_bound)]`in the given attributes.
pub fn has_dumb_trait_bound(attrs: &[Attribute]) -> bool {
find_meta_item(attrs.iter(), |meta| {
if let NestedMeta::Meta(Meta::Path(ref path)) = meta {
if path.is_ident("dumb_trait_bound") {
......@@ -125,17 +128,32 @@ pub fn get_dumb_trait_bound(attrs: &[Attribute]) -> bool {
}).is_some()
}
/// Given a set of named fields, return an iterator of `Field` where all fields
/// marked `#[codec(skip)]` are filtered out.
pub fn filter_skip_named<'a>(fields: &'a syn::FieldsNamed) -> impl Iterator<Item=&Field> + 'a {
fields.named.iter()
.filter(|f| get_skip(&f.attrs).is_none())
.filter(|f| !should_skip(&f.attrs))
}
/// Given a set of unnamed fields, return an iterator of `(index, Field)` where all fields
/// marked `#[codec(skip)]` are filtered out.
pub fn filter_skip_unnamed<'a>(fields: &'a syn::FieldsUnnamed) -> impl Iterator<Item=(usize, &Field)> + 'a {
fields.unnamed.iter()
.enumerate()
.filter(|(_, f)| get_skip(&f.attrs).is_none())
.filter(|(_, f)| !should_skip(&f.attrs))
}
/// Ensure attributes are correctly applied. This *must* be called before using
/// any of the attribute finder methods or the macro may panic if it encounters
/// misapplied attributes.
/// `#[codec(dumb_trait_bound)]` is the only accepted top attribute.
/// Fields can have the following attributes:
/// * `#[codec(skip)]`
/// * `#[codec(compact)]`
/// * `#[codec(encoded_as = "$EncodeAs")]` with $EncodedAs a valid TokenStream
/// Variants can have the following attributes:
/// * `#[codec(skip)]`
/// * `#[codec(index = $int)]`
pub fn check_attributes(input: &DeriveInput) -> syn::Result<()> {
for attr in &input.attrs {
check_top_attribute(attr)?;
......@@ -170,7 +188,7 @@ pub fn check_attributes(input: &DeriveInput) -> syn::Result<()> {
Ok(())
}
// Is accepted only:
// Ensure a field is decorated only with the following attributes:
// * `#[codec(skip)]`
// * `#[codec(compact)]`
// * `#[codec(encoded_as = "$EncodeAs")]` with $EncodedAs a valid TokenStream
......@@ -181,7 +199,7 @@ fn check_field_attribute(attr: &Attribute) -> syn::Result<()> {
if attr.path.is_ident("codec") {
match attr.parse_meta()? {
Meta::List(ref meta_list) if meta_list.nested.len() == 1 => {
match meta_list.nested.first().unwrap() {
match meta_list.nested.first().expect("Just checked that there is one item; qed") {
NestedMeta::Meta(Meta::Path(path))
if path.get_ident().map_or(false, |i| i == "skip") => Ok(()),
......@@ -203,7 +221,7 @@ fn check_field_attribute(attr: &Attribute) -> syn::Result<()> {
}
}
// Is accepted only:
// Ensure a field is decorated only with the following attributes:
// * `#[codec(skip)]`
// * `#[codec(index = $int)]`
fn check_variant_attribute(attr: &Attribute) -> syn::Result<()> {
......@@ -213,7 +231,7 @@ fn check_variant_attribute(attr: &Attribute) -> syn::Result<()> {
if attr.path.is_ident("codec") {
match attr.parse_meta()? {
Meta::List(ref meta_list) if meta_list.nested.len() == 1 => {
match meta_list.nested.first().unwrap() {
match meta_list.nested.first().expect("Just checked that there is one item; qed") {
NestedMeta::Meta(Meta::Path(path))
if path.get_ident().map_or(false, |i| i == "skip") => Ok(()),
......@@ -239,7 +257,7 @@ fn check_top_attribute(attr: &Attribute) -> syn::Result<()> {
if attr.path.is_ident("codec") {
match attr.parse_meta()? {
Meta::List(ref meta_list) if meta_list.nested.len() == 1 => {
match meta_list.nested.first().unwrap() {
match meta_list.nested.first().expect("Just checked that there is one item; qed") {
NestedMeta::Meta(Meta::Path(path))
if path.get_ident().map_or(false, |i| i == "dumb_trait_bound") => Ok(()),
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment