Parameter Type Validation Helper: Added

validate_parameter_type(&syn::Type):

Verifies the type is a reference (syn::Type::Reference).
Ensures the reference is mutable (type_ref.mutability.is_some()),
reporting: #[turtle_main] parameter must be a mutable reference: '&mut
TurtlePlan'.
Checks that the target type (type_ref.elem) is a path whose trailing
identifier is TurtlePlan, allowing both &mut TurtlePlan and qualified
paths like &mut turtle_lib::TurtlePlan.
Rejects other types (like value: i32 or owned t: TurtlePlan), reporting:
Integration into validate_input: Called
validate_parameter_type(&pat_type.ty)?; directly within
syn::FnArg::Typed(pat_type).

Unit Tests Added:

test_valid_qualified_type: verifies &mut turtle_lib::TurtlePlan is
accepted.
test_rejects_wrong_type: verifies value: i32 is rejected with an
informative error.
test_rejects_immutable_reference: verifies t: &TurtlePlan is rejected.
test_rejects_owned_type: verifies t: TurtlePlan is rejected.
test_rejects_wrong_reference_type: verifies t: &mut i32 is rejected.
This commit is contained in:
2026-09-19 20:55:00 +02:00
parent 99a51ef40e
commit c4831967be
+80
View File
@@ -102,6 +102,40 @@ use syn::ItemFn;
/// });
/// }
/// ```
fn validate_parameter_type(ty: &syn::Type) -> Result<(), syn::Error> {
match ty {
syn::Type::Reference(type_ref) => {
if type_ref.mutability.is_none() {
return Err(syn::Error::new_spanned(
type_ref,
"#[turtle_main] parameter must be a mutable reference: `&mut TurtlePlan`",
));
}
if let syn::Type::Path(type_path) = &*type_ref.elem {
let is_turtle_plan = type_path
.path
.segments
.last()
.is_some_and(|seg| seg.ident == "TurtlePlan");
if is_turtle_plan {
return Ok(());
}
}
Err(syn::Error::new_spanned(
&type_ref.elem,
"#[turtle_main] expected reference to `TurtlePlan`, e.g. `&mut TurtlePlan`",
))
}
_ => Err(syn::Error::new_spanned(
ty,
"#[turtle_main] parameter must be of type `&mut TurtlePlan`",
)),
}
}
fn validate_input(input_fn: &ItemFn) -> Result<(), syn::Error> {
if input_fn.sig.asyncness.is_some() {
return Err(syn::Error::new_spanned(
@@ -137,6 +171,8 @@ fn validate_input(input_fn: &ItemFn) -> Result<(), syn::Error> {
));
}
}
validate_parameter_type(&pat_type.ty)?;
}
}
}
@@ -356,6 +392,50 @@ mod tests {
assert!(err.to_string().contains("cannot have a return type"));
}
#[test]
fn test_valid_qualified_type() {
let input: ItemFn = parse_quote! {
fn my_draw(t: &mut turtle_lib::TurtlePlan) {}
};
assert!(validate_input(&input).is_ok());
}
#[test]
fn test_rejects_wrong_type() {
let input: ItemFn = parse_quote! {
fn my_draw(value: i32) {}
};
let err = validate_input(&input).unwrap_err();
assert!(err.to_string().contains("parameter must be of type `&mut TurtlePlan`"));
}
#[test]
fn test_rejects_immutable_reference() {
let input: ItemFn = parse_quote! {
fn my_draw(t: &TurtlePlan) {}
};
let err = validate_input(&input).unwrap_err();
assert!(err.to_string().contains("parameter must be a mutable reference: `&mut TurtlePlan`"));
}
#[test]
fn test_rejects_owned_type() {
let input: ItemFn = parse_quote! {
fn my_draw(t: TurtlePlan) {}
};
let err = validate_input(&input).unwrap_err();
assert!(err.to_string().contains("parameter must be of type `&mut TurtlePlan`"));
}
#[test]
fn test_rejects_wrong_reference_type() {
let input: ItemFn = parse_quote! {
fn my_draw(t: &mut i32) {}
};
let err = validate_input(&input).unwrap_err();
assert!(err.to_string().contains("expected reference to `TurtlePlan`"));
}
#[test]
fn test_expansion_preserves_custom_param_name() {
let input = quote! {