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:
@@ -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> {
|
fn validate_input(input_fn: &ItemFn) -> Result<(), syn::Error> {
|
||||||
if input_fn.sig.asyncness.is_some() {
|
if input_fn.sig.asyncness.is_some() {
|
||||||
return Err(syn::Error::new_spanned(
|
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"));
|
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]
|
#[test]
|
||||||
fn test_expansion_preserves_custom_param_name() {
|
fn test_expansion_preserves_custom_param_name() {
|
||||||
let input = quote! {
|
let input = quote! {
|
||||||
|
|||||||
Reference in New Issue
Block a user