fix(derive): allow #[label] on Option<T> when T: !Copy

This commit is contained in:
minty-sh 2026-06-02 15:43:09 +02:00
parent df7bcfa17d
commit 62c74617d0
No known key found for this signature in database
GPG Key ID: 76316762DB7A622C
2 changed files with 80 additions and 14 deletions

View File

@ -105,6 +105,16 @@ impl Parse for LabelAttr {
}
}
/// Check if a type is syntactically `Option<...>`.
fn is_option_type(ty: &syn::Type) -> bool {
if let syn::Type::Path(type_path) = ty {
if let Some(segment) = type_path.path.segments.last() {
return segment.ident == "Option";
}
}
false
}
impl Labels {
pub fn from_fields(fields: &syn::Fields) -> syn::Result<Option<Self>> {
match fields {
@ -185,13 +195,23 @@ impl Labels {
quote! { miette::LabeledSpan::new_with_span }
};
Some(quote! {
miette::macro_helpers::OptionalWrapper::<#ty>::new().to_option(&self.#span)
.map(|#var| #ctor(
#display,
#var.clone(),
))
})
let label_code = if is_option_type(ty) {
quote! {
self.#span.as_ref().map(|#var| #ctor(
#display,
#var.clone(),
))
}
} else {
quote! {
miette::macro_helpers::OptionalWrapper::<#ty>::new().to_option(&self.#span)
.map(|#var| #ctor(
#display,
#var.clone(),
))
}
};
Some(label_code)
});
let collections_chain = self.0.iter().filter_map(|label| {
let Label {
@ -272,13 +292,23 @@ impl Labels {
quote! { miette::LabeledSpan::new_with_span }
};
Some(quote! {
miette::macro_helpers::OptionalWrapper::<#ty>::new().to_option(#field)
.map(|#var| #ctor(
#display,
#var.clone(),
))
})
let variant_label_code = if is_option_type(ty) {
quote! {
#field.as_ref().map(|#var| #ctor(
#display,
#var.clone(),
))
}
} else {
quote! {
miette::macro_helpers::OptionalWrapper::<#ty>::new().to_option(#field)
.map(|#var| #ctor(
#display,
#var.clone(),
))
}
};
Some(variant_label_code)
});
let collections_chain = labels.0.iter().filter_map(|label| {
let Label { span, label, ty: _, lbl_ty } = label;

View File

@ -632,3 +632,39 @@ fn test_optional_source_code() {
.source_code()
.is_some());
}
#[test]
fn test_label_option_non_copy() {
#[derive(Debug)]
struct NonCopySpan(SourceSpan);
impl From<NonCopySpan> for SourceSpan {
fn from(v: NonCopySpan) -> Self {
v.0
}
}
impl From<&NonCopySpan> for SourceSpan {
fn from(v: &NonCopySpan) -> Self {
v.0
}
}
#[derive(Debug, Error, Diagnostic)]
#[error("test error")]
struct E {
#[source_code]
src: String,
#[label("here")]
span: Option<NonCopySpan>,
}
let e = E {
src: "hello there".into(),
span: Some(NonCopySpan((0..5).into())),
};
let labels: Vec<_> = e.labels().unwrap().collect();
assert_eq!(labels.len(), 1);
assert_eq!(labels[0].offset(), 0);
assert_eq!(labels[0].len(), 5);
assert_eq!(labels[0].label(), Some("here"));
}