Skip to content

Commit

Permalink
Use unique encoded generic parameter names in derive (#509)
Browse files Browse the repository at this point in the history
This ensures that we don't clash with the generic parameter names of the user.
  • Loading branch information
bkchr authored Sep 4, 2023
1 parent 76c5c4a commit 1516bb9
Showing 1 changed file with 142 additions and 138 deletions.
280 changes: 142 additions & 138 deletions derive/src/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,7 @@
use std::str::from_utf8;

use proc_macro2::{Ident, Span, TokenStream};
use syn::{
punctuated::Punctuated,
spanned::Spanned,
token::Comma,
Data, Field, Fields, Error,
};
use syn::{punctuated::Punctuated, spanned::Spanned, token::Comma, Data, Error, Field, Fields};

use crate::utils;

Expand All @@ -38,15 +33,17 @@ fn encode_single_field(
if utils::should_skip(&field.attrs) {
return Error::new(
Span::call_site(),
"Internal error: cannot encode single field optimisation if skipped"
).to_compile_error();
"Internal error: cannot encode single field optimisation if skipped",
)
.to_compile_error()
}

if encoded_as.is_some() && compact {
return Error::new(
Span::call_site(),
"`encoded_as` and `compact` can not be used at the same time!"
).to_compile_error();
"`encoded_as` and `compact` can not be used at the same time!",
)
.to_compile_error()
}

let final_field_variable = if compact {
Expand Down Expand Up @@ -90,7 +87,13 @@ fn encode_single_field(
#crate_path::Encode::encode(&#final_field_variable)
}

fn using_encoded<R, F: ::core::ops::FnOnce(&[::core::primitive::u8]) -> R>(&#i_self, f: F) -> R {
fn using_encoded<
__CodecOutputReturn,
__CodecUsingEncodedCallback: ::core::ops::FnOnce(
&[::core::primitive::u8]
) -> __CodecOutputReturn
>(&#i_self, f: __CodecUsingEncodedCallback) -> __CodecOutputReturn
{
#crate_path::Encode::using_encoded(&#final_field_variable, f)
}
}
Expand Down Expand Up @@ -123,8 +126,9 @@ where
if encoded_as.is_some() as u8 + compact as u8 + skip as u8 > 1 {
return Error::new(
f.span(),
"`encoded_as`, `compact` and `skip` can only be used one at a time!"
).to_compile_error();
"`encoded_as`, `compact` and `skip` can only be used one at a time!",
)
.to_compile_error()
}

// Based on the seen attribute, we call a handler that generates code for a specific
Expand Down Expand Up @@ -152,107 +156,114 @@ fn encode_fields<F>(
where
F: Fn(usize, &Option<Ident>) -> TokenStream,
{
iterate_over_fields(fields, field_name, |field, field_attribute| match field_attribute {
FieldAttribute::None(f) => quote_spanned! { f.span() =>
#crate_path::Encode::encode_to(#field, #dest);
iterate_over_fields(
fields,
field_name,
|field, field_attribute| match field_attribute {
FieldAttribute::None(f) => quote_spanned! { f.span() =>
#crate_path::Encode::encode_to(#field, #dest);
},
FieldAttribute::Compact(f) => {
let field_type = &f.ty;
quote_spanned! {
f.span() => {
#crate_path::Encode::encode_to(
&<
<#field_type as #crate_path::HasCompact>::Type as
#crate_path::EncodeAsRef<'_, #field_type>
>::RefType::from(#field),
#dest,
);
}
}
},
FieldAttribute::EncodedAs { field: f, encoded_as } => {
let field_type = &f.ty;
quote_spanned! {
f.span() => {
#crate_path::Encode::encode_to(
&<
#encoded_as as
#crate_path::EncodeAsRef<'_, #field_type>
>::RefType::from(#field),
#dest,
);
}
}
},
FieldAttribute::Skip(_) => quote! {
let _ = #field;
},
},
|recurse| {
quote! {
#( #recurse )*
}
},
FieldAttribute::Compact(f) => {
let field_type = &f.ty;
quote_spanned! {
f.span() => {
#crate_path::Encode::encode_to(
)
}

fn size_hint_fields<F>(fields: &FieldsList, field_name: F, crate_path: &syn::Path) -> TokenStream
where
F: Fn(usize, &Option<Ident>) -> TokenStream,
{
iterate_over_fields(
fields,
field_name,
|field, field_attribute| match field_attribute {
FieldAttribute::None(f) => quote_spanned! { f.span() =>
.saturating_add(#crate_path::Encode::size_hint(#field))
},
FieldAttribute::Compact(f) => {
let field_type = &f.ty;
quote_spanned! {
f.span() => .saturating_add(#crate_path::Encode::size_hint(
&<
<#field_type as #crate_path::HasCompact>::Type as
#crate_path::EncodeAsRef<'_, #field_type>
>::RefType::from(#field),
#dest,
);
))
}
}
},
FieldAttribute::EncodedAs { field: f, encoded_as } => {
let field_type = &f.ty;
quote_spanned! {
f.span() => {
#crate_path::Encode::encode_to(
},
FieldAttribute::EncodedAs { field: f, encoded_as } => {
let field_type = &f.ty;
quote_spanned! {
f.span() => .saturating_add(#crate_path::Encode::size_hint(
&<
#encoded_as as
#crate_path::EncodeAsRef<'_, #field_type>
>::RefType::from(#field),
#dest,
);
))
}
}
},
FieldAttribute::Skip(_) => quote!(),
},
FieldAttribute::Skip(_) => quote! {
let _ = #field;
},
}, |recurse| quote! {
#( #recurse )*
})
}

fn size_hint_fields<F>(fields: &FieldsList, field_name: F, crate_path: &syn::Path) -> TokenStream
where
F: Fn(usize, &Option<Ident>) -> TokenStream,
{
iterate_over_fields(fields, field_name, |field, field_attribute| match field_attribute {
FieldAttribute::None(f) => quote_spanned! { f.span() =>
.saturating_add(#crate_path::Encode::size_hint(#field))
},
FieldAttribute::Compact(f) => {
let field_type = &f.ty;
quote_spanned! {
f.span() => .saturating_add(#crate_path::Encode::size_hint(
&<
<#field_type as #crate_path::HasCompact>::Type as
#crate_path::EncodeAsRef<'_, #field_type>
>::RefType::from(#field),
))
|recurse| {
quote! {
0_usize #( #recurse )*
}
},
FieldAttribute::EncodedAs { field: f, encoded_as } => {
let field_type = &f.ty;
quote_spanned! {
f.span() => .saturating_add(#crate_path::Encode::size_hint(
&<
#encoded_as as
#crate_path::EncodeAsRef<'_, #field_type>
>::RefType::from(#field),
))
}
},
FieldAttribute::Skip(_) => quote!(),
}, |recurse| quote! {
0_usize #( #recurse )*
})
)
}

fn try_impl_encode_single_field_optimisation(data: &Data, crate_path: &syn::Path) -> Option<TokenStream> {
fn try_impl_encode_single_field_optimisation(
data: &Data,
crate_path: &syn::Path,
) -> Option<TokenStream> {
match *data {
Data::Struct(ref data) => {
match data.fields {
Fields::Named(ref fields) if utils::filter_skip_named(fields).count() == 1 => {
let field = utils::filter_skip_named(fields).next().unwrap();
let name = &field.ident;
Some(encode_single_field(
field,
quote!(&self.#name),
crate_path,
))
},
Fields::Unnamed(ref fields) if utils::filter_skip_unnamed(fields).count() == 1 => {
let (id, field) = utils::filter_skip_unnamed(fields).next().unwrap();
let id = syn::Index::from(id);

Some(encode_single_field(
field,
quote!(&self.#id),
crate_path,
))
},
_ => None,
}
Data::Struct(ref data) => match data.fields {
Fields::Named(ref fields) if utils::filter_skip_named(fields).count() == 1 => {
let field = utils::filter_skip_named(fields).next().unwrap();
let name = &field.ident;
Some(encode_single_field(field, quote!(&self.#name), crate_path))
},
Fields::Unnamed(ref fields) if utils::filter_skip_unnamed(fields).count() == 1 => {
let (id, field) = utils::filter_skip_unnamed(fields).next().unwrap();
let id = syn::Index::from(id);

Some(encode_single_field(field, quote!(&self.#id), crate_path))
},
_ => None,
},
_ => None,
}
Expand All @@ -262,45 +273,45 @@ fn impl_encode(data: &Data, type_name: &Ident, crate_path: &syn::Path) -> TokenS
let self_ = quote!(self);
let dest = &quote!(__codec_dest_edqy);
let [hinting, encoding] = match *data {
Data::Struct(ref data) => {
match data.fields {
Fields::Named(ref fields) => {
let fields = &fields.named;
let field_name = |_, name: &Option<Ident>| quote!(&#self_.#name);

let hinting = size_hint_fields(fields, field_name, crate_path);
let encoding = encode_fields(dest, fields, field_name, crate_path);

[hinting, encoding]
},
Fields::Unnamed(ref fields) => {
let fields = &fields.unnamed;
let field_name = |i, _: &Option<Ident>| {
let i = syn::Index::from(i);
quote!(&#self_.#i)
};

let hinting = size_hint_fields(fields, field_name, crate_path);
let encoding = encode_fields(dest, fields, field_name, crate_path);

[hinting, encoding]
},
Fields::Unit => [quote! { 0_usize }, quote!()],
}
Data::Struct(ref data) => match data.fields {
Fields::Named(ref fields) => {
let fields = &fields.named;
let field_name = |_, name: &Option<Ident>| quote!(&#self_.#name);

let hinting = size_hint_fields(fields, field_name, crate_path);
let encoding = encode_fields(dest, fields, field_name, crate_path);

[hinting, encoding]
},
Fields::Unnamed(ref fields) => {
let fields = &fields.unnamed;
let field_name = |i, _: &Option<Ident>| {
let i = syn::Index::from(i);
quote!(&#self_.#i)
};

let hinting = size_hint_fields(fields, field_name, crate_path);
let encoding = encode_fields(dest, fields, field_name, crate_path);

[hinting, encoding]
},
Fields::Unit => [quote! { 0_usize }, quote!()],
},
Data::Enum(ref data) => {
let data_variants = || data.variants.iter().filter(|variant| !utils::should_skip(&variant.attrs));
let data_variants =
|| data.variants.iter().filter(|variant| !utils::should_skip(&variant.attrs));

if data_variants().count() > 256 {
return Error::new(
data.variants.span(),
"Currently only enums with at most 256 variants are encodable."
).to_compile_error();
"Currently only enums with at most 256 variants are encodable.",
)
.to_compile_error()
}

// If the enum has no variants, we don't need to encode anything.
if data_variants().count() == 0 {
return quote!();
return quote!()
}

let recurse = data_variants().enumerate().map(|(i, f)| {
Expand All @@ -312,10 +323,7 @@ fn impl_encode(data: &Data, type_name: &Ident, crate_path: &syn::Path) -> TokenS
let fields = &fields.named;
let field_name = |_, ident: &Option<Ident>| quote!(#ident);

let names = fields
.iter()
.enumerate()
.map(|(i, f)| field_name(i, &f.ident));
let names = fields.iter().enumerate().map(|(i, f)| field_name(i, &f.ident));

let field_name = |a, b: &Option<Ident>| field_name(a, b);

Expand Down Expand Up @@ -348,10 +356,7 @@ fn impl_encode(data: &Data, type_name: &Ident, crate_path: &syn::Path) -> TokenS
quote!(#ident)
};

let names = fields
.iter()
.enumerate()
.map(|(i, f)| field_name(i, &f.ident));
let names = fields.iter().enumerate().map(|(i, f)| field_name(i, &f.ident));

let field_name = |a, b: &Option<Ident>| field_name(a, b);

Expand Down Expand Up @@ -414,10 +419,9 @@ fn impl_encode(data: &Data, type_name: &Ident, crate_path: &syn::Path) -> TokenS

[hinting, encoding]
},
Data::Union(ref data) => return Error::new(
data.union_token.span(),
"Union types are not supported."
).to_compile_error(),
Data::Union(ref data) =>
return Error::new(data.union_token.span(), "Union types are not supported.")
.to_compile_error(),
};
quote! {
fn size_hint(&#self_) -> usize {
Expand Down

0 comments on commit 1516bb9

Please sign in to comment.