diff --git a/miette-derive/src/label.rs b/miette-derive/src/label.rs index ab2ceac..22879bb 100644 --- a/miette-derive/src/label.rs +++ b/miette-derive/src/label.rs @@ -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> { 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; diff --git a/tests/derive.rs b/tests/derive.rs index aa631dc..2884ea7 100644 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -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 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, + } + + 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")); +}