xso-proc: improve error messages on cast mismatch for extracts

The old error message was pointing at the `FromXml` / `AsXml` invocation
and not on the field which actually caused the problem. The new error
message points exactly at the type of the affected field.
This commit is contained in:
Jonas Schäfer 2024-08-18 11:50:41 +02:00
commit c01eb090b8
2 changed files with 30 additions and 8 deletions

View file

@ -572,6 +572,15 @@ impl Compound {
}
}
/// Construct a tuple type with this compound's field's types in the same
/// order as they appear in the compound.
pub(crate) fn to_single_or_tuple_ty(&self) -> Type {
match self.single_ty() {
None => self.to_tuple_ty().into(),
Some(v) => v.clone(),
}
}
/// Construct a tuple type with references to this compound's field's
/// types in the same order as they appear in the compound, with the given
/// lifetime.

View file

@ -10,8 +10,8 @@
//! implementations in a single type.
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::*;
use quote::{quote, quote_spanned};
use syn::{spanned::Spanned, *};
use crate::compound::Compound;
use crate::error_message::{self, ParentRef};
@ -53,9 +53,13 @@ impl Field for ChildField {
};
let (extra_defs, matcher, fetch, builder) = match self.extract {
Some(ref extract) => {
extract.make_from_xml_builder_parts(scope, container_name, member, is_container)?
}
Some(ref extract) => extract.make_from_xml_builder_parts(
scope,
container_name,
member,
is_container,
ty,
)?,
None => {
let FromEventsScope {
ref substate_result,
@ -165,6 +169,7 @@ impl Field for ChildField {
let (extra_defs, init, iter_ty) = match self.extract {
Some(ref extract) => extract.make_as_item_iter_parts(
scope,
ty,
container_name,
bound_name,
member,
@ -271,6 +276,7 @@ impl ExtractDef {
container_name: &ParentRef,
member: &Member,
collecting_into_container: bool,
output_ty: &Type,
) -> Result<(TokenStream, TokenStream, TokenStream, Type)> {
let FromEventsScope {
ref substate_result,
@ -303,6 +309,8 @@ impl ExtractDef {
let matcher = quote! { #state_ty_ident::new(name, attrs).map(|x| #from_xml_builder_ty_ident(::core::option::Option::Some(x))) };
let inner_ty = self.parts.to_single_or_tuple_ty();
let fetch = if self.parts.field_count() == 1 {
// If we extract only a single field, we automatically unwrap the
// tuple, because that behaviour is more obvious to users.
@ -337,8 +345,9 @@ impl ExtractDef {
// the type constraint imposed by the place the value is *used*,
// which is strictly bound by the field's type (so there is, in
// fact, no ambiguity). So this works all kinds of magic.
quote! {
#fetch.into()
quote_spanned! {
output_ty.span()=>
<#output_ty as ::core::convert::From::<#inner_ty>>::from(#fetch)
}
};
@ -352,6 +361,7 @@ impl ExtractDef {
fn make_as_item_iter_parts(
&self,
scope: &AsItemsScope,
input_ty: &Type,
container_name: &ParentRef,
bound_name: &Ident,
member: &Member,
@ -450,9 +460,12 @@ impl ExtractDef {
// about that in [`make_from_xml_builder_parts`] implementation
// corresponding to this code above, and we will not repeat it
// here.
let cast = quote_spanned! { input_ty.span()=>
::core::option::Option::from(#bound_name)
};
(
quote! {
::xso::asxml::OptionAsXml::new(::core::option::Option::from(#bound_name).map(|#bound_name: #inner_ty| {
::xso::asxml::OptionAsXml::new(#cast.map(|#bound_name: #inner_ty| {
#item_iter_ty_ident::new((#repack))
}).transpose()?)
},