fix: diagnostics insted of panicking
This commit is contained in:
parent
42632f81e4
commit
f7735bec52
4 changed files with 38 additions and 16 deletions
|
@ -15,4 +15,4 @@ members = [
|
|||
]
|
||||
|
||||
[dependencies]
|
||||
binf_macros = { version = "1.0.1" }
|
||||
binf_macros = { version = "1.0.1", path = "macros" }
|
||||
|
|
|
@ -14,5 +14,6 @@ proc-macro = true
|
|||
|
||||
[dependencies]
|
||||
proc-macro2 = "1.0.68"
|
||||
proc-macro2-diagnostics = "0.10.1"
|
||||
quote = "1.0.33"
|
||||
syn = { version = "2.0.38", features = ["full"] }
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use proc_macro::TokenStream;
|
||||
use proc_macro2_diagnostics::Diagnostic;
|
||||
|
||||
/// Attribute proc macro that turns a struct into a bitflag
|
||||
/// The underlying type of the bitflag is chosen based on the number of fields in the struct
|
||||
|
@ -24,15 +25,16 @@ use proc_macro::TokenStream;
|
|||
/// you can also call functions from BitFlag trait beacause the new struct implements deref and deref_mut to the underlying type
|
||||
#[proc_macro_attribute]
|
||||
pub fn bitflag(_attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let mut diagnostics = Vec::new();
|
||||
|
||||
let structdef: syn::ItemStruct = syn::parse_macro_input!(input as syn::ItemStruct);
|
||||
// check if the struct has correct shape
|
||||
if !check_struct(&structdef) {
|
||||
panic!("struct has incorrect shape");
|
||||
}
|
||||
check_struct(&mut diagnostics, &structdef);
|
||||
|
||||
let structname = structdef.ident;
|
||||
let structfields = match &structdef.fields {
|
||||
syn::Fields::Named(f) => f.named.iter().map(|f| f.ident.clone().unwrap()).collect::<Vec<_>>(),
|
||||
_ => panic!("struct has incorrect shape"),
|
||||
_ => {diagnostics.push(syn::Error::new_spanned(&structdef.fields, "struct has incorrect shape, only works on struct with named fields").into()); Vec::new()},
|
||||
};
|
||||
|
||||
let vis = structdef.vis.clone();
|
||||
|
@ -47,7 +49,7 @@ pub fn bitflag(_attr: TokenStream, input: TokenStream) -> TokenStream {
|
|||
17..=32 => quote::quote! { u32 },
|
||||
33..=64 => quote::quote! { u64 },
|
||||
65..=128 => quote::quote! { u128 },
|
||||
_ => panic!("struct has too many fields"),
|
||||
_ => {diagnostics.push(syn::Error::new_spanned(structdef.fields, "struct has too many fields").into()); quote::quote! { u8 }}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -107,7 +109,9 @@ pub fn bitflag(_attr: TokenStream, input: TokenStream) -> TokenStream {
|
|||
}
|
||||
};
|
||||
|
||||
let diagnostics = diagnostics.iter().map(|d| d.clone().emit_as_item_tokens());
|
||||
quote::quote! {
|
||||
#(#diagnostics)*
|
||||
#newstruct
|
||||
#impls
|
||||
#deref_impl
|
||||
|
@ -116,28 +120,33 @@ pub fn bitflag(_attr: TokenStream, input: TokenStream) -> TokenStream {
|
|||
}
|
||||
|
||||
/// returns true if the struct has a correct shape
|
||||
fn check_struct(input: &syn::ItemStruct) -> bool {
|
||||
fn check_struct(diagnostics: &mut Vec<Diagnostic>, input: &syn::ItemStruct) {
|
||||
if input.generics.lt_token.is_some() {
|
||||
return false;
|
||||
diagnostics.push(syn::Error::new_spanned(&input.generics, "generics not allowed in bitflag structs").into());
|
||||
}
|
||||
if input.generics.gt_token.is_some() {
|
||||
return false;
|
||||
diagnostics.push(syn::Error::new_spanned(&input.generics, "generics not allowed in bitflag structs").into());
|
||||
}
|
||||
if input.generics.where_clause.is_some() {
|
||||
return false;
|
||||
diagnostics.push(syn::Error::new_spanned(&input.generics, "generics not allowed in bitflag structs").into());
|
||||
}
|
||||
if !input.generics.params.is_empty() {
|
||||
return false;
|
||||
diagnostics.push(syn::Error::new_spanned(&input.generics, "generics not allowed in bitflag structs").into());
|
||||
}
|
||||
|
||||
match &input.fields {
|
||||
syn::Fields::Unnamed(_) => false,
|
||||
syn::Fields::Unit => false,
|
||||
syn::Fields::Unnamed(f) => diagnostics.push(syn::Error::new_spanned(f, "struct has incorrect shape, found tuple struct").into()),
|
||||
syn::Fields::Unit => diagnostics.push(syn::Error::new_spanned(input, "struct has incorrect shape, found unit struct").into()),
|
||||
syn::Fields::Named(f) => {
|
||||
if f.named.len() > 128 {
|
||||
return false;
|
||||
diagnostics.push(syn::Error::new_spanned(f, "struct has too many fields").into());
|
||||
}
|
||||
for field in f.named.iter() {
|
||||
match &field.ty {
|
||||
syn::Type::Path(path_type) if path_type.path.is_ident("bool") => {},
|
||||
_ => diagnostics.push(syn::Error::new_spanned(field, "struct has incorrect shape, found non bool field").into()),
|
||||
}
|
||||
}
|
||||
true
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use binf::*;
|
||||
use binf_macros::*;
|
||||
|
||||
#[bitflag]
|
||||
pub struct Test {
|
||||
|
@ -70,3 +69,16 @@ fn u64_test() {
|
|||
flags.set_flag(0, true);
|
||||
assert_eq!(flags, 0b10101011);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn off_test() {
|
||||
let flags = Test::new(0b0);
|
||||
assert_eq!(flags.a(), false);
|
||||
assert_eq!(flags.b(), false);
|
||||
assert_eq!(flags.c(), false);
|
||||
assert_eq!(flags.d(), false);
|
||||
assert_eq!(flags.e(), false);
|
||||
assert_eq!(flags.f(), false);
|
||||
assert_eq!(flags.g(), false);
|
||||
assert_eq!(flags.h(), false);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue