Skip to content
Open
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
2 changes: 1 addition & 1 deletion bilge-impl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ serde = []
syn = { version = "2.0", features = ["full"] }
quote = "1.0"
proc-macro2 = "1.0"
proc-macro-error2 = { version = "2.0", default-features = false }
manyhow = "0.11"
itertools = ">=0.11.0, <=0.14"

[dev-dependencies]
Expand Down
49 changes: 26 additions & 23 deletions bilge-impl/src/bitsize.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
mod split;

use manyhow::bail;
use proc_macro2::{Ident, TokenStream};
use proc_macro_error2::{abort, abort_call_site};
use quote::quote;
use split::SplitAttributes;
use syn::{punctuated::Iter, spanned::Spanned, Fields, Item, ItemEnum, ItemStruct, Type, Variant};
Expand All @@ -14,42 +14,42 @@ struct ItemIr {
expanded: TokenStream,
}

pub(super) fn bitsize(args: TokenStream, item: TokenStream) -> TokenStream {
let (item, declared_bitsize) = parse(item, args);
let attrs = SplitAttributes::from_item(&item);
pub(super) fn bitsize(args: TokenStream, item: TokenStream) -> manyhow::Result {
let (item, declared_bitsize) = parse(item, args)?;
let attrs = SplitAttributes::from_item(&item)?;
let ir = match item {
Item::Struct(mut item) => {
modify_special_field_names(&mut item.fields);
analyze_struct(&item.fields);
analyze_struct(&item.fields)?;
let expanded = generate_struct(&item, declared_bitsize);
ItemIr { expanded }
}
Item::Enum(item) => {
analyze_enum(declared_bitsize, item.variants.iter());
analyze_enum(declared_bitsize, item.variants.iter())?;
let expanded = generate_enum(&item);
ItemIr { expanded }
}
_ => unreachable(()),
};
generate_common(ir, attrs, declared_bitsize)
Ok(generate_common(ir, attrs, declared_bitsize))
}

fn parse(item: TokenStream, args: TokenStream) -> (Item, BitSize) {
fn parse(item: TokenStream, args: TokenStream) -> manyhow::Result<(Item, BitSize)> {
let item = syn::parse2(item).unwrap_or_else(unreachable);

if args.is_empty() {
abort_call_site!("missing attribute value"; help = "you need to define the size like this: `#[bitsize(32)]`")
bail!("missing attribute value"; help = "you need to define the size like this: `#[bitsize(32)]`")
}

let (declared_bitsize, _arb_int) = shared::bitsize_and_arbitrary_int_from(args);
(item, declared_bitsize)
let (declared_bitsize, _arb_int) = shared::bitsize_and_arbitrary_int_from(args)?;
Ok((item, declared_bitsize))
}

fn check_type_is_supported(ty: &Type) {
fn check_type_is_supported(ty: &Type) -> manyhow::Result<()> {
use Type::*;
match ty {
Tuple(tuple) => tuple.elems.iter().for_each(check_type_is_supported),
Array(array) => check_type_is_supported(&array.elem),
Tuple(tuple) => tuple.elems.iter().try_for_each(check_type_is_supported)?,
Array(array) => check_type_is_supported(&array.elem)?,
// Probably okay (compilation would validate that this type is also Bitsized)
Path(_) => (),
// These don't work with structs or aren't useful in bitfields.
Expand All @@ -61,9 +61,10 @@ fn check_type_is_supported(ty: &Type) {
// Something to investigate, but doesn't seem useful/usable here either.
TraitObject(_) |
// I have no idea where this is used.
Verbatim(_) | Paren(_) => abort!(ty, "This field type is not supported"),
_ => abort!(ty, "This field type is currently not supported"),
Verbatim(_) | Paren(_) => bail!(ty, "This field type is not supported"),
_ => bail!(ty, "This field type is currently not supported"),
}
Ok(())
}

/// Allows you to give multiple fields the name `reserved` or `padding`
Expand All @@ -90,34 +91,36 @@ fn modify_special_field_names(fields: &mut Fields) {
}
}

fn analyze_struct(fields: &Fields) {
fn analyze_struct(fields: &Fields) -> manyhow::Result<()> {
if fields.is_empty() {
abort_call_site!("structs without fields are not supported")
bail!("structs without fields are not supported")
}

// don't move this. we validate all nested field types here as well
// and later assume this was checked.
for field in fields {
check_type_is_supported(&field.ty)
check_type_is_supported(&field.ty)?
}
Ok(())
}

fn analyze_enum(bitsize: BitSize, variants: Iter<Variant>) {
fn analyze_enum(bitsize: BitSize, variants: Iter<Variant>) -> manyhow::Result<()> {
if bitsize > MAX_ENUM_BIT_SIZE {
abort_call_site!("enum bitsize is limited to {}", MAX_ENUM_BIT_SIZE)
bail!("enum bitsize is limited to {}", MAX_ENUM_BIT_SIZE)
}

let variant_count = variants.clone().count();
if variant_count == 0 {
abort_call_site!("empty enums are not supported");
bail!("empty enums are not supported");
}

let has_fallback = variants.flat_map(|variant| &variant.attrs).any(is_fallback_attribute);

if !has_fallback {
// this has a side-effect of validating the enum count
let _ = enum_fills_bitsize(bitsize, variant_count);
let _ = enum_fills_bitsize(bitsize, variant_count)?;
}
Ok(())
}

fn generate_struct(item: &ItemStruct, declared_bitsize: u8) -> TokenStream {
Expand Down
33 changes: 16 additions & 17 deletions bilge-impl/src/bitsize/split.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use proc_macro_error2::{abort, abort_call_site};
use manyhow::bail;
use quote::ToTokens;
use syn::{meta::ParseNestedMeta, parse_quote, Attribute, Item, Meta, Path};

Expand Down Expand Up @@ -51,15 +51,13 @@ impl SplitAttributes {
///
/// Any derives with suffix `Bits` will be able to access field information.
/// This way, users of `bilge` can define their own derives working on the uncompressed bitfield.
pub fn from_item(item: &Item) -> SplitAttributes {
pub fn from_item(item: &Item) -> manyhow::Result<SplitAttributes> {
let attrs = match item {
Item::Enum(item) => &item.attrs,
Item::Struct(item) => &item.attrs,
_ => abort_call_site!("item is not a struct or enum"; help = "`#[bitsize]` can only be used on structs and enums"),
_ => bail!("item is not a struct or enum"; help = "`#[bitsize]` can only be used on structs and enums"),
};

let parsed = attrs.iter().map(parse_attribute);

let is_struct = matches!(item, Item::Struct(..));

let mut from_bytes = None;
Expand All @@ -68,16 +66,16 @@ impl SplitAttributes {
let mut before_compression = vec![];
let mut after_compression = vec![];

for parsed_attr in parsed {
match parsed_attr {
for attr in attrs {
match parse_attribute(attr)? {
ParsedAttribute::DeriveList(derives) => {
for mut derive in derives {
if derive.matches(&["zerocopy", "FromBytes"]) {
from_bytes = Some(derive.clone());
} else if derive.matches(&["bilge", "FromBits"]) {
has_frombits = true;
} else if derive.matches_core_or_std(&["fmt", "Debug"]) && is_struct {
abort!(derive.0, "use derive(DebugBits) for structs")
bail!(derive.0, "use derive(DebugBits) for structs")
} else if derive.matches_core_or_std(&["default", "Default"]) && is_struct {
// emit_warning!(derive.0, "use derive(DefaultBits) for structs")
derive.0 = syn::parse_quote!(::bilge::DefaultBits);
Expand All @@ -93,7 +91,7 @@ impl SplitAttributes {
}

ParsedAttribute::BitsizeInternal(attr) => {
abort!(attr, "remove bitsize_internal"; help = "attribute bitsize_internal can only be applied internally by the bitsize macros")
bail!(attr, "remove bitsize_internal"; help = "attribute bitsize_internal can only be applied internally by the bitsize macros")
}

ParsedAttribute::Other(attr) => {
Expand All @@ -106,7 +104,7 @@ impl SplitAttributes {

if let Some(from_bytes) = from_bytes {
if !has_frombits {
abort!(from_bytes.0, "a bitfield with zerocopy::FromBytes also needs to have FromBits")
bail!(from_bytes.0, "a bitfield with zerocopy::FromBytes also needs to have FromBits")
}
}

Expand All @@ -115,15 +113,15 @@ impl SplitAttributes {
before_compression.append(&mut after_compression)
}

SplitAttributes {
Ok(SplitAttributes {
before_compression,
after_compression,
}
})
}
}

fn parse_attribute(attribute: &Attribute) -> ParsedAttribute<'_> {
match &attribute.meta {
fn parse_attribute(attribute: &Attribute) -> manyhow::Result<ParsedAttribute<'_>> {
Ok(match &attribute.meta {
Meta::List(list) if list.path.is_ident("derive") => {
let mut derives = Vec::new();
let add_derive = |meta: ParseNestedMeta| {
Expand All @@ -133,16 +131,17 @@ fn parse_attribute(attribute: &Attribute) -> ParsedAttribute<'_> {
Ok(())
};

list.parse_nested_meta(add_derive)
.unwrap_or_else(|e| abort!(list.tokens, "failed to parse derive: {}", e));
if let Err(e) = list.parse_nested_meta(add_derive) {
bail!(list.tokens, "failed to parse derive: {}", e);
}

ParsedAttribute::DeriveList(derives)
}

meta if contains_anywhere(meta, "bitsize_internal") => ParsedAttribute::BitsizeInternal(attribute),

_ => ParsedAttribute::Other(attribute),
}
})
}

/// a crude approximation of things we currently consider in item attributes
Expand Down
12 changes: 6 additions & 6 deletions bilge-impl/src/bitsize_internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ struct ItemIr<'a> {
expanded: TokenStream,
}

pub(super) fn bitsize_internal(args: TokenStream, item: TokenStream) -> TokenStream {
let (item, arb_int) = parse(item, args);
pub(super) fn bitsize_internal(args: TokenStream, item: TokenStream) -> manyhow::Result {
let (item, arb_int) = parse(item, args)?;
let ir = match item {
Item::Struct(ref item) => {
let expanded = generate_struct(item, &arb_int);
Expand All @@ -31,13 +31,13 @@ pub(super) fn bitsize_internal(args: TokenStream, item: TokenStream) -> TokenStr
}
_ => unreachable(()),
};
generate_common(ir, &arb_int)
Ok(generate_common(ir, &arb_int))
}

fn parse(item: TokenStream, args: TokenStream) -> (Item, TokenStream) {
fn parse(item: TokenStream, args: TokenStream) -> manyhow::Result<(Item, TokenStream)> {
let item = syn::parse2(item).unwrap_or_else(unreachable);
let (_declared_bitsize, arb_int) = shared::bitsize_and_arbitrary_int_from(args);
(item, arb_int)
let (_declared_bitsize, arb_int) = shared::bitsize_and_arbitrary_int_from(args)?;
Ok((item, arb_int))
}

fn generate_struct(struct_data: &ItemStruct, arb_int: &TokenStream) -> TokenStream {
Expand Down
10 changes: 5 additions & 5 deletions bilge-impl/src/debug_bits.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
use manyhow::bail;
use proc_macro2::{Ident, TokenStream};
use proc_macro_error2::abort_call_site;
use quote::quote;
use syn::{Data, Fields};

use crate::shared::{self, unreachable};

pub(super) fn debug_bits(item: TokenStream) -> TokenStream {
pub(super) fn debug_bits(item: TokenStream) -> manyhow::Result {
let derive_input = shared::parse_derive(item);
let name = &derive_input.ident;
let name_str = name.to_string();
let struct_data = match derive_input.data {
Data::Struct(s) => s,
Data::Enum(_) => abort_call_site!("use derive(Debug) for enums"),
Data::Enum(_) => bail!("use derive(Debug) for enums"),
Data::Union(_) => unreachable(()),
};

Expand Down Expand Up @@ -43,11 +43,11 @@ pub(super) fn debug_bits(item: TokenStream) -> TokenStream {
Fields::Unit => todo!("this is a unit struct, which is not supported right now"),
};

quote! {
Ok(quote! {
impl ::core::fmt::Debug for #name {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
#fmt_impl
}
}
}
})
}
12 changes: 6 additions & 6 deletions bilge-impl/src/default_bits.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
use manyhow::bail;
use proc_macro2::{Ident, TokenStream};
use proc_macro_error2::abort_call_site;
use quote::quote;
use syn::{Data, DeriveInput, Fields, Type};

use crate::shared::{self, fallback::Fallback, unreachable, BitSize};

pub(crate) fn default_bits(item: TokenStream) -> TokenStream {
pub(crate) fn default_bits(item: TokenStream) -> manyhow::Result {
let derive_input = parse(item);
//TODO: does fallback need handling?
let (derive_data, _, name, ..) = analyze(&derive_input);
let (derive_data, _, name, ..) = analyze(&derive_input)?;

match derive_data {
Data::Struct(data) => generate_struct_default_impl(name, &data.fields),
Data::Enum(_) => abort_call_site!("use derive(Default) for enums"),
Data::Struct(data) => Ok(generate_struct_default_impl(name, &data.fields)),
Data::Enum(_) => bail!("use derive(Default) for enums"),
_ => unreachable(()),
}
}
Expand Down Expand Up @@ -87,6 +87,6 @@ fn parse(item: TokenStream) -> DeriveInput {
shared::parse_derive(item)
}

fn analyze(derive_input: &DeriveInput) -> (&Data, TokenStream, &Ident, BitSize, Option<Fallback>) {
fn analyze(derive_input: &DeriveInput) -> manyhow::Result<(&Data, TokenStream, &Ident, BitSize, Option<Fallback>)> {
shared::analyze_derive(derive_input, false)
}
Loading