Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
157 changes: 101 additions & 56 deletions bitcode_derive/src/attribute.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use crate::{err, error};
use proc_macro2::TokenStream;
use quote::quote;
use std::str::FromStr;
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::{parse2, Attribute, Expr, ExprLit, Lit, Meta, Path, Result, Token, Type};

enum BitcodeAttr {
BoundType(Type),
CrateAlias(Path),
CrateName(Path),
Skip,
}

Expand Down Expand Up @@ -49,7 +50,7 @@ impl BitcodeAttr {
// removed: https://github.com/SoftbearStudios/bitcode/pull/28#issuecomment-2227465515
// path.leading_colon = Some(Token![::](str_lit.span()));

Ok(Self::CrateAlias(path))
Ok(Self::CrateName(path))
}
_ => err(&nested, "expected name value"),
},
Expand All @@ -58,89 +59,133 @@ impl BitcodeAttr {
}
}

fn apply(self, attrs: &mut BitcodeAttrs, nested: &Meta) -> Result<()> {
fn apply(self, attrs: &mut BitcodeAnyAttrs<'_, '_>, nested: &Meta) -> Result<()> {
fn set_if_not_duplicate<T: Default + PartialEq>(
v: &mut T,
new_value: T,
nested: &Meta,
) -> Result<()> {
if new_value == Default::default() {
return err(nested, "cannot set to default value");
}
if &*v != &Default::default() {
return err(nested, "duplicate");
}
*v = new_value;
Ok(())
}

match self {
Self::BoundType(bound_type) => {
if let AttrType::Field { bound_type: b, .. } = &mut attrs.attr_type {
if b.is_some() {
return err(nested, "duplicate");
}
*b = Some(bound_type);
Ok(())
if let BitcodeAnyAttrs::Field(field) = attrs {
set_if_not_duplicate(&mut field.bound_type, Some(bound_type), nested)
} else {
err(nested, "can only apply bound to fields")
err(nested, r#"can only apply to fields"#)
}
}
Self::CrateAlias(crate_name) => {
if let AttrType::Derive = attrs.attr_type {
attrs.crate_name = crate_name;
Ok(())
Self::CrateName(crate_name) => {
if let BitcodeAnyAttrs::Derive(derive) = attrs {
set_if_not_duplicate(&mut derive.crate_name, Some(crate_name), nested)
} else {
err(nested, "can only apply crate rename to derives")
err(nested, r#"can only apply to struct/enum definition"#)
}
}
Self::Skip => {
if let AttrType::Field { .. } = &attrs.attr_type {
attrs.skip = true;
Ok(())
if let BitcodeAnyAttrs::Field(field) = attrs {
set_if_not_duplicate(&mut field.skip, true, nested)
} else {
err(nested, "can only apply skip to fields")
err(nested, "can only apply to fields")
}
}
}
}
}

#[derive(Clone)]
pub struct BitcodeAttrs {
attr_type: AttrType,
/// The crate name to use for the generated code, defaults to "bitcode".
pub crate_name: Path,
/// Whether to skip this field during (de)serialisation.
pub skip: bool,
}

#[derive(Clone)]
enum AttrType {
Derive,
Variant,
Field { bound_type: Option<Type> },
pub struct BitcodeDeriveAttrs {
crate_name: Option<Path>,
pub private: TokenStream,
}

impl BitcodeAttrs {
fn new(attr_type: AttrType) -> Self {
Self {
attr_type,
crate_name: syn::parse_str("bitcode").expect("invalid crate name"),
skip: false,
}
impl BitcodeDeriveAttrs {
pub fn parse(attrs: &[Attribute]) -> Result<Self> {
let mut ret = Self {
crate_name: Default::default(),
private: quote! {},
};
BitcodeAnyAttrs::Derive(&mut ret).parse_inner(attrs)?;
let crate_name = ret
.crate_name
.clone()
.unwrap_or_else(|| syn::parse_str("bitcode").unwrap());

ret.private = quote! { #crate_name::__private };
Ok(ret)
}
}

pub fn bound_type(&self) -> Option<Type> {
match &self.attr_type {
AttrType::Field { bound_type, .. } => bound_type.as_ref().cloned(),
_ => unreachable!(),
}
pub struct BitcodeVariantAttrs<'a> {
parent: &'a BitcodeDeriveAttrs,
}
impl std::ops::Deref for BitcodeVariantAttrs<'_> {
type Target = BitcodeDeriveAttrs;
fn deref(&self) -> &Self::Target {
self.parent
}
}

pub fn parse_derive(attrs: &[Attribute]) -> Result<Self> {
let mut ret = Self::new(AttrType::Derive);
ret.parse_inner(attrs)?;
impl<'a> BitcodeVariantAttrs<'a> {
pub fn parse(attrs: &[Attribute], parent: &'a BitcodeDeriveAttrs) -> Result<Self> {
let mut ret = Self { parent };
BitcodeAnyAttrs::Variant { _unused: &mut ret }.parse_inner(attrs)?;
Ok(ret)
}
}

pub fn parse_variant(attrs: &[Attribute], _derive_attrs: &Self) -> Result<Self> {
let mut ret = Self::new(AttrType::Variant);
ret.parse_inner(attrs)?;
Ok(ret)
#[derive(Copy, Clone)]
pub enum BitcodeDeriveOrVariantAttrs<'a> {
Derive(&'a BitcodeDeriveAttrs),
Variant(&'a BitcodeVariantAttrs<'a>),
}
impl std::ops::Deref for BitcodeDeriveOrVariantAttrs<'_> {
type Target = BitcodeDeriveAttrs;
fn deref(&self) -> &Self::Target {
match self {
Self::Derive(v) => v,
Self::Variant(v) => v.parent,
}
}
}

pub fn parse_field(attrs: &[Attribute], _parent_attrs: &Self) -> Result<Self> {
let mut ret = Self::new(AttrType::Field { bound_type: None });
ret.parse_inner(attrs)?;
pub struct BitcodeFieldAttrs<'a> {
parent: BitcodeDeriveOrVariantAttrs<'a>,
pub bound_type: Option<Type>,
pub skip: bool,
}
impl<'a> std::ops::Deref for BitcodeFieldAttrs<'a> {
type Target = BitcodeDeriveOrVariantAttrs<'a>;
fn deref(&self) -> &Self::Target {
&self.parent
}
}
impl<'a> BitcodeFieldAttrs<'a> {
pub fn parse(attrs: &[Attribute], parent: BitcodeDeriveOrVariantAttrs<'a>) -> Result<Self> {
let mut ret = Self {
parent,
bound_type: Default::default(),
skip: Default::default(),
};
BitcodeAnyAttrs::Field(&mut ret).parse_inner(attrs)?;
Ok(ret)
}
}

enum BitcodeAnyAttrs<'a, 'b> {
Derive(&'a mut BitcodeDeriveAttrs),
Variant {
_unused: &'a mut BitcodeVariantAttrs<'b>,
},
Field(&'a mut BitcodeFieldAttrs<'b>),
}
impl BitcodeAnyAttrs<'_, '_> {
fn parse_inner(&mut self, attrs: &[Attribute]) -> Result<()> {
for attr in attrs {
let path = path_ident_string(attr.path(), attr)?;
Expand Down
6 changes: 3 additions & 3 deletions bitcode_derive/src/bound.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::attribute::BitcodeAttrs;
use crate::attribute::BitcodeFieldAttrs;
use std::collections::{HashMap, HashSet};
use syn::punctuated::Pair;
use syn::Token;
Expand All @@ -12,11 +12,11 @@ impl FieldBounds {
pub fn add_bound_type(
&mut self,
field: syn::Field,
field_attrs: &BitcodeAttrs,
field_attrs: &BitcodeFieldAttrs,
bound: syn::Path,
) {
let bounds = self.bounds.entry(bound).or_default();
if let Some(bound_type) = field_attrs.bound_type() {
if let Some(bound_type) = field_attrs.bound_type.clone() {
bounds.1.push(bound_type);
} else {
bounds.0.push(field);
Expand Down
30 changes: 14 additions & 16 deletions bitcode_derive/src/decode.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use crate::attribute::BitcodeAttrs;
use crate::private;
use crate::attribute::{BitcodeDeriveAttrs, BitcodeFieldAttrs};
use crate::shared::{remove_lifetimes, replace_lifetimes, VariantIndexType};
use proc_macro2::{Ident, Span, TokenStream};
use quote::{quote, ToTokens};
Expand Down Expand Up @@ -36,20 +35,19 @@ impl Item {
impl crate::shared::Item for Item {
fn field_impl(
self,
crate_name: &Path,
attrs: &BitcodeFieldAttrs,
field_name: TokenStream,
global_field_name: TokenStream,
real_field_name: TokenStream,
field_type: &Type,
field_attrs: &BitcodeAttrs,
) -> TokenStream {
match self {
Self::Type => {
let mut de_type = replace_lifetimes(field_type, DE_LIFETIME).to_token_stream();
if field_attrs.skip {
if attrs.skip {
de_type = quote! { ::core::marker::PhantomData<#de_type> };
}
let private = private(crate_name);
let private = &attrs.private;
let de = de_lifetime();
quote! {
#global_field_name: <#de_type as #private::Decode<#de>>::Decoder,
Expand All @@ -63,7 +61,7 @@ impl crate::shared::Item for Item {
},
// Only used by enum variants.
Self::Decode => {
let value = if field_attrs.skip {
let value = if attrs.skip {
quote! {
Default::default()
}
Expand All @@ -78,11 +76,11 @@ impl crate::shared::Item for Item {
}
Self::DecodeInPlace => {
let de_type = replace_lifetimes(field_type, DE_LIFETIME);
let private = private(crate_name);
let private = &attrs.private;
let target = quote! {
#private::uninit_field!(out.#real_field_name: #de_type)
};
if field_attrs.skip {
if attrs.skip {
quote! {{
(#target).write(Default::default());
}}
Expand All @@ -109,7 +107,7 @@ impl crate::shared::Item for Item {

fn enum_impl(
self,
crate_name: &Path,
attrs: &BitcodeDeriveAttrs,
variant_count: usize,
variant_index_type: VariantIndexType,
pattern: impl Fn(usize) -> TokenStream,
Expand All @@ -125,7 +123,7 @@ impl crate::shared::Item for Item {
let inners: TokenStream = (0..variant_count).map(|i| inner(self, i)).collect();
let variants = decode_variants
.then(|| {
let private = private(crate_name);
let private = &attrs.private;
let c_style = inners.is_empty();
let histogram = if c_style {
0
Expand All @@ -152,7 +150,7 @@ impl crate::shared::Item for Item {
}
Self::Populate => {
if never {
let private = private(crate_name);
let private = &attrs.private;
return quote! {
if __length != 0 {
return #private::invalid_enum_variant();
Expand Down Expand Up @@ -253,8 +251,8 @@ impl crate::shared::Derive<{ Item::COUNT }> for Decode {
type Item = Item;
const ALL: [Self::Item; Item::COUNT] = Item::ALL;

fn bound(&self, crate_name: &Path) -> Path {
let private = private(crate_name);
fn bound(&self, attrs: &BitcodeDeriveAttrs) -> Path {
let private = &attrs.private;
let de = de_lifetime();
parse_quote!(#private::Decode<#de>)
}
Expand All @@ -265,7 +263,7 @@ impl crate::shared::Derive<{ Item::COUNT }> for Decode {

fn derive_impl(
&self,
crate_name: &Path,
attrs: &BitcodeDeriveAttrs,
output: [TokenStream; Item::COUNT],
ident: Ident,
mut generics: Generics,
Expand Down Expand Up @@ -321,7 +319,7 @@ impl crate::shared::Derive<{ Item::COUNT }> for Decode {

let decoder_ident = Ident::new(&format!("{ident}Decoder"), Span::call_site());
let decoder_ty = quote! { #decoder_ident #decoder_generics };
let private = private(crate_name);
let private = &attrs.private;

quote! {
#[allow(clippy::pedantic)]
Expand Down
Loading
Loading