-
Notifications
You must be signed in to change notification settings - Fork 111
fix(derive-encode): emit friendly compile errors instead of panicking #307
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,33 @@ pub fn derive_encode_label_set(input: TokenStream) -> TokenStream { | |
| let ast: DeriveInput = syn::parse(input).unwrap(); | ||
| let name = &ast.ident; | ||
|
|
||
| if let syn::Data::Struct(syn::DataStruct { ref fields, .. }) = ast.data { | ||
| for f in fields.iter() { | ||
| for a in &f.attrs { | ||
| if !a.path().is_ident("prometheus") { | ||
| continue; | ||
| } | ||
| match a.parse_args::<syn::Ident>() { | ||
| Ok(ident) if ident == "flatten" => {} | ||
| Ok(other) => { | ||
| let msg = format!( | ||
| "Provided attribute '{other}', but only 'flatten' is supported" | ||
| ); | ||
| return quote! { compile_error!(#msg); }.into(); | ||
| } | ||
| Err(_) => { | ||
| return quote! { | ||
| compile_error!( | ||
| "Attribute on `#[prometheus(...)]` must be an identifier, e.g. `#[prometheus(flatten)]`" | ||
| ); | ||
| } | ||
| .into(); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| let body: TokenStream2 = match ast.clone().data { | ||
| syn::Data::Struct(s) => match s.fields { | ||
| syn::Fields::Named(syn::FieldsNamed { named, .. }) => named | ||
|
|
@@ -27,13 +54,7 @@ pub fn derive_encode_label_set(input: TokenStream) -> TokenStream { | |
| .iter() | ||
| .find(|a| a.path().is_ident("prometheus")) | ||
| .map(|a| a.parse_args::<syn::Ident>().unwrap().to_string()); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This |
||
| let flatten = match attribute.as_deref() { | ||
| Some("flatten") => true, | ||
| Some(other) => { | ||
| panic!("Provided attribute '{other}', but only 'flatten' is supported") | ||
| } | ||
| None => false, | ||
| }; | ||
| let flatten = attribute.as_deref() == Some("flatten"); | ||
| let ident = f.ident.unwrap(); | ||
| if flatten { | ||
| quote! { | ||
|
|
@@ -60,14 +81,30 @@ pub fn derive_encode_label_set(input: TokenStream) -> TokenStream { | |
| }) | ||
| .collect(), | ||
| syn::Fields::Unnamed(_) => { | ||
| panic!("Can not derive Encode for struct with unnamed fields.") | ||
| return quote! { | ||
| compile_error!("Can not derive Encode for struct with unnamed fields."); | ||
| } | ||
| .into(); | ||
| } | ||
| syn::Fields::Unit => { | ||
| return quote! { | ||
| compile_error!("Can not derive Encode for struct with unit field."); | ||
| } | ||
| .into(); | ||
| } | ||
| syn::Fields::Unit => panic!("Can not derive Encode for struct with unit field."), | ||
| }, | ||
| syn::Data::Enum(syn::DataEnum { .. }) => { | ||
| panic!("Can not derive Encode for enum.") | ||
| return quote! { | ||
| compile_error!("Can not derive Encode for enum."); | ||
| } | ||
| .into(); | ||
| } | ||
| syn::Data::Union(_) => { | ||
| return quote! { | ||
| compile_error!("Can not derive Encode for union."); | ||
| } | ||
| .into(); | ||
| } | ||
| syn::Data::Union(_) => panic!("Can not derive Encode for union."), | ||
| }; | ||
|
|
||
| let gen = quote! { | ||
|
|
@@ -95,7 +132,10 @@ pub fn derive_encode_label_value(input: TokenStream) -> TokenStream { | |
|
|
||
| let body = match ast.clone().data { | ||
| syn::Data::Struct(_) => { | ||
| panic!("Can not derive EncodeLabel for struct.") | ||
| return quote! { | ||
| compile_error!("Can not derive EncodeLabel for struct."); | ||
| } | ||
| .into(); | ||
| } | ||
| syn::Data::Enum(syn::DataEnum { variants, .. }) => { | ||
| let match_arms: TokenStream2 = variants | ||
|
|
@@ -114,7 +154,12 @@ pub fn derive_encode_label_value(input: TokenStream) -> TokenStream { | |
| } | ||
| } | ||
| } | ||
| syn::Data::Union(_) => panic!("Can not derive Encode for union."), | ||
| syn::Data::Union(_) => { | ||
| return quote! { | ||
| compile_error!("Can not derive Encode for union."); | ||
| } | ||
| .into(); | ||
| } | ||
| }; | ||
|
|
||
| let gen = quote! { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| use prometheus_client::encoding::EncodeLabelSet; | ||
| use prometheus_client::encoding::EncodeLabelValue; | ||
|
|
||
| // Unnamed struct fields: not supported by `EncodeLabelSet`. | ||
| #[derive(EncodeLabelSet)] | ||
| struct Unnamed(String); | ||
|
|
||
| // Unit struct: not supported by `EncodeLabelSet`. | ||
| #[derive(EncodeLabelSet)] | ||
| struct UnitStruct; | ||
|
|
||
| // Enum: not supported by `EncodeLabelSet`. | ||
| #[derive(EncodeLabelSet)] | ||
| enum NotAllowedSet { | ||
| A, | ||
| B, | ||
| } | ||
|
|
||
| // Unknown attribute: only `#[prometheus(flatten)]` is accepted. | ||
| #[derive(EncodeLabelSet)] | ||
| struct BadAttr { | ||
| #[prometheus(unknown)] | ||
| a: u64, | ||
| } | ||
|
|
||
| // Struct: not supported by `EncodeLabelValue` (only enums are). | ||
| #[derive(EncodeLabelValue)] | ||
| struct StructForValue; | ||
|
|
||
| // Union: not supported by either derive. | ||
| #[derive(EncodeLabelSet)] | ||
| union UnionSet { | ||
| a: u32, | ||
| b: f32, | ||
| } | ||
|
|
||
| fn main() {} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| error: Can not derive Encode for struct with unnamed fields. | ||
| --> tests/build/friendly-compilation-error-msg.rs:5:10 | ||
| | | ||
| 5 | #[derive(EncodeLabelSet)] | ||
| | ^^^^^^^^^^^^^^ | ||
| | | ||
| = note: this error originates in the derive macro `EncodeLabelSet` (in Nightly builds, run with -Z macro-backtrace for more info) | ||
|
|
||
| error: Can not derive Encode for struct with unit field. | ||
| --> tests/build/friendly-compilation-error-msg.rs:9:10 | ||
| | | ||
| 9 | #[derive(EncodeLabelSet)] | ||
| | ^^^^^^^^^^^^^^ | ||
| | | ||
| = note: this error originates in the derive macro `EncodeLabelSet` (in Nightly builds, run with -Z macro-backtrace for more info) | ||
|
|
||
| error: Can not derive Encode for enum. | ||
| --> tests/build/friendly-compilation-error-msg.rs:13:10 | ||
| | | ||
| 13 | #[derive(EncodeLabelSet)] | ||
| | ^^^^^^^^^^^^^^ | ||
| | | ||
| = note: this error originates in the derive macro `EncodeLabelSet` (in Nightly builds, run with -Z macro-backtrace for more info) | ||
|
|
||
| error: Provided attribute 'unknown', but only 'flatten' is supported | ||
| --> tests/build/friendly-compilation-error-msg.rs:20:10 | ||
| | | ||
| 20 | #[derive(EncodeLabelSet)] | ||
| | ^^^^^^^^^^^^^^ | ||
| | | ||
| = note: this error originates in the derive macro `EncodeLabelSet` (in Nightly builds, run with -Z macro-backtrace for more info) | ||
|
|
||
| error: Can not derive EncodeLabel for struct. | ||
| --> tests/build/friendly-compilation-error-msg.rs:27:10 | ||
| | | ||
| 27 | #[derive(EncodeLabelValue)] | ||
| | ^^^^^^^^^^^^^^^^ | ||
| | | ||
| = note: this error originates in the derive macro `EncodeLabelValue` (in Nightly builds, run with -Z macro-backtrace for more info) | ||
|
|
||
| error: Can not derive Encode for union. | ||
| --> tests/build/friendly-compilation-error-msg.rs:31:10 | ||
| | | ||
| 31 | #[derive(EncodeLabelSet)] | ||
| | ^^^^^^^^^^^^^^ | ||
| | | ||
| = note: this error originates in the derive macro `EncodeLabelSet` (in Nightly builds, run with -Z macro-backtrace for more info) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could, instead of hand crafting the
compile_error!, you could usesyn::Error::new_spanned(&other, msg).to_compile_error(). It generates the same kind of error, but the little^^^^arrow points right at the unknown in[prometheus(unknown)]instead of#[derive(...)]We could do these for the other sites too,
syn::Error::new_spanned(&ast, "...")for the enum/union/unnamed-struct cases points at the offending type.