#![cfg(any(
not(any(
feature = "parser_tests",
feature = "analyzer_tests",
feature = "codegen_tests",
feature = "interpreter_tests",
feature = "conformance_tests",
feature = "integration_tests",
)),
feature = "parser_tests",
))]
use windjammer::lexer::Lexer;
use windjammer::parser::ast::*;
use windjammer::parser_impl::Parser;
fn parse_program(input: &str) -> Program<'_> {
let mut lexer = Lexer::new(input);
let tokens = lexer.tokenize_with_locations();
let mut parser = Parser::new(tokens);
parser.parse().expect("Failed to parse program")
}
fn get_fn_param_type(input: &str) -> Type {
let program = parse_program(input);
if let Some(Item::Function { decl, .. }) = program.items.first() {
if let Some(param) = decl.parameters.first() {
return param.type_.clone();
}
}
panic!("Failed to extract parameter type from: {}", input);
}
fn get_fn_return_type(input: &str) -> Option<Type> {
let program = parse_program(input);
if let Some(Item::Function { decl, .. }) = program.items.first() {
return decl.return_type.clone();
}
panic!("Failed to extract return type from: {}", input);
}
fn get_struct_field_type(input: &str) -> Type {
let program = parse_program(input);
if let Some(Item::Struct { decl, .. }) = program.items.first() {
if let Some(field) = decl.fields.first() {
return field.field_type.clone();
}
}
panic!("Failed to extract field type from: {}", input);
}
#[allow(dead_code)]
fn is_numeric_type(ty: &Type, expected_name: &str) -> bool {
match ty {
Type::Int | Type::Int32 | Type::Uint | Type::Float => true,
Type::Custom(name) => name == expected_name,
_ => false,
}
}
#[test]
fn test_type_i32() {
let ty = get_fn_param_type("fn foo(x: i32) { }");
assert!(
matches!(ty, Type::Int32 | Type::Custom(_)),
"Expected Int32 or Custom, got {:?}",
ty
);
}
#[test]
fn test_type_int() {
let ty = get_fn_param_type("fn foo(x: int) { }");
assert!(
matches!(ty, Type::Int | Type::Custom(_)),
"Expected Int or Custom, got {:?}",
ty
);
}
#[test]
fn test_type_float() {
let ty = get_fn_param_type("fn foo(x: float) { }");
assert!(
matches!(ty, Type::Float | Type::Custom(_)),
"Expected Float or Custom, got {:?}",
ty
);
}
#[test]
fn test_type_bool() {
let ty = get_fn_param_type("fn foo(x: bool) { }");
assert!(matches!(ty, Type::Bool), "Expected Bool, got {:?}", ty);
}
#[test]
fn test_type_string() {
let ty = get_fn_param_type("fn foo(x: String) { }");
assert!(matches!(ty, Type::String), "Expected String, got {:?}", ty);
}
#[test]
fn test_type_custom() {
let ty = get_fn_param_type("fn foo(x: Point) { }");
if let Type::Custom(name) = ty {
assert_eq!(name, "Point");
} else {
panic!("Expected Custom type, got {:?}", ty);
}
}
#[test]
fn test_type_custom_camelcase() {
let ty = get_fn_param_type("fn foo(x: MyCustomType) { }");
if let Type::Custom(name) = ty {
assert_eq!(name, "MyCustomType");
} else {
panic!("Expected Custom type");
}
}
#[test]
fn test_type_vec() {
let ty = get_fn_param_type("fn foo(x: Vec<i32>) { }");
match ty {
Type::Vec(inner) => {
assert!(matches!(*inner, Type::Int32 | Type::Custom(_)));
}
Type::Parameterized(name, args) => {
assert_eq!(name, "Vec");
assert_eq!(args.len(), 1);
}
_ => panic!("Expected Vec or Parameterized type, got {:?}", ty),
}
}
#[test]
fn test_type_option() {
let ty = get_fn_param_type("fn foo(x: Option<String>) { }");
match ty {
Type::Option(inner) => {
assert!(matches!(*inner, Type::String | Type::Custom(_)));
}
Type::Parameterized(name, args) => {
assert_eq!(name, "Option");
assert_eq!(args.len(), 1);
}
_ => panic!("Expected Option or Parameterized type, got {:?}", ty),
}
}
#[test]
fn test_type_result() {
let ty = get_fn_param_type("fn foo(x: Result<i32, Error>) { }");
match ty {
Type::Result(ok, _err) => {
assert!(matches!(*ok, Type::Int32 | Type::Custom(_)));
}
Type::Parameterized(name, args) => {
assert_eq!(name, "Result");
assert_eq!(args.len(), 2);
}
_ => panic!("Expected Result or Parameterized type, got {:?}", ty),
}
}
#[test]
fn test_type_parameterized() {
let ty = get_fn_param_type("fn foo(x: HashMap<String, i32>) { }");
if let Type::Parameterized(name, args) = ty {
assert_eq!(name, "HashMap");
assert_eq!(args.len(), 2);
} else {
panic!("Expected Parameterized type, got {:?}", ty);
}
}
#[test]
fn test_type_nested_generic() {
let ty = get_fn_param_type("fn foo(x: Vec<Option<i32> >) { }");
match ty {
Type::Vec(inner) => {
assert!(matches!(
*inner,
Type::Option(_) | Type::Parameterized(_, _)
));
}
Type::Parameterized(name, args) => {
assert_eq!(name, "Vec");
assert_eq!(args.len(), 1);
}
_ => panic!("Expected Vec or Parameterized type, got {:?}", ty),
}
}
#[test]
fn test_type_ref() {
let ty = get_fn_param_type("fn foo(x: &i32) { }");
if let Type::Reference(inner) = ty {
assert!(matches!(*inner, Type::Int32 | Type::Custom(_)));
} else {
panic!("Expected Reference type, got {:?}", ty);
}
}
#[test]
fn test_type_mut_ref() {
let ty = get_fn_param_type("fn foo(x: &mut i32) { }");
if let Type::MutableReference(inner) = ty {
assert!(matches!(*inner, Type::Int32 | Type::Custom(_)));
} else {
panic!("Expected MutableReference type, got {:?}", ty);
}
}
#[test]
fn test_type_ref_string() {
let ty = get_fn_param_type("fn foo(x: &string) { }");
if let Type::Reference(inner) = ty {
assert!(matches!(*inner, Type::String));
} else {
panic!("Expected Reference type");
}
}
#[test]
fn test_type_ref_custom() {
let ty = get_fn_param_type("fn foo(x: &Point) { }");
if let Type::Reference(inner) = ty {
if let Type::Custom(name) = *inner {
assert_eq!(name, "Point");
} else {
panic!("Expected Custom inner type");
}
} else {
panic!("Expected Reference type");
}
}
#[test]
fn test_type_array() {
let ty = get_fn_param_type("fn foo(x: [i32; 10]) { }");
if let Type::Array(element, size) = ty {
assert!(matches!(*element, Type::Int32 | Type::Custom(_)));
assert_eq!(size, 10);
} else {
panic!("Expected Array type, got {:?}", ty);
}
}
#[test]
fn test_type_tuple_pair() {
let ty = get_fn_param_type("fn foo(x: (i32, string)) { }");
if let Type::Tuple(elements) = ty {
assert_eq!(elements.len(), 2);
} else {
panic!("Expected Tuple type, got {:?}", ty);
}
}
#[test]
fn test_type_tuple_triple() {
let ty = get_fn_param_type("fn foo(x: (i32, float, bool)) { }");
if let Type::Tuple(elements) = ty {
assert_eq!(elements.len(), 3);
} else {
panic!("Expected Tuple type");
}
}
#[test]
fn test_type_unit() {
let ret = get_fn_return_type("fn foo() -> () { }");
if let Some(Type::Tuple(elements)) = ret {
assert!(elements.is_empty());
} else {
panic!("Expected unit type (empty tuple), got {:?}", ret);
}
}
#[test]
fn test_fn_param_str_type_is_custom_str() {
let t = get_fn_param_type("fn f(key: str) { }");
let ok = matches!(&t, Type::String) || matches!(&t, Type::Custom(s) if s == "str");
assert!(
ok,
"expected `key: String` → Type:: String or Custom(\"str\"), got {:?}",
t
);
}
#[test]
fn test_type_fn_pointer() {
let ty = get_fn_param_type("fn foo(f: fn(i32) -> i32) { }");
if let Type::FunctionPointer {
params,
return_type,
} = ty
{
assert_eq!(params.len(), 1);
assert!(return_type.is_some());
} else {
panic!("Expected FunctionPointer type, got {:?}", ty);
}
}
#[test]
fn test_type_fn_pointer_no_return() {
let ty = get_fn_param_type("fn foo(f: fn(i32)) { }");
if let Type::FunctionPointer {
params,
return_type,
} = ty
{
assert_eq!(params.len(), 1);
assert!(return_type.is_none());
} else {
panic!("Expected FunctionPointer type");
}
}
#[test]
fn test_type_fn_pointer_multiple_params() {
let ty = get_fn_param_type("fn foo(f: fn(i32, string, bool) -> float) { }");
if let Type::FunctionPointer { params, .. } = ty {
assert_eq!(params.len(), 3);
} else {
panic!("Expected FunctionPointer type");
}
}
#[test]
fn test_return_type_simple() {
let ret = get_fn_return_type("fn foo() -> i32 { 42 }");
assert!(ret.is_some());
if let Some(ty) = ret {
assert!(matches!(ty, Type::Int32 | Type::Custom(_)));
}
}
#[test]
fn test_return_type_none() {
let ret = get_fn_return_type("fn foo() { }");
assert!(ret.is_none());
}
#[test]
fn test_return_type_vec() {
let ret = get_fn_return_type("fn foo() -> Vec<i32> { Vec::new() }");
match ret {
Some(Type::Vec(_)) | Some(Type::Parameterized(_, _)) => {}
_ => panic!("Expected Vec or Parameterized return type, got {:?}", ret),
}
}
#[test]
fn test_return_type_option() {
let ret = get_fn_return_type("fn foo() -> Option<String> { None }");
match ret {
Some(Type::Option(_)) | Some(Type::Parameterized(_, _)) => {}
_ => panic!(
"Expected Option or Parameterized return type, got {:?}",
ret
),
}
}
#[test]
fn test_field_type_primitive() {
let ty = get_struct_field_type("struct Point { x: i32 }");
assert!(matches!(ty, Type::Int32 | Type::Custom(_)));
}
#[test]
fn test_field_type_string() {
let ty = get_struct_field_type("struct Person { name: String }");
assert!(matches!(ty, Type:: String));
}
#[test]
fn test_field_type_vec() {
let ty = get_struct_field_type("struct Container { items: Vec<Item> }");
match ty {
Type::Vec(_) | Type::Parameterized(_, _) => {}
_ => panic!("Expected Vec or Parameterized field type, got {:?}", ty),
}
}
#[test]
fn test_field_type_option() {
let ty = get_struct_field_type("struct Node { parent: Option<Node> }");
match ty {
Type::Option(_) | Type::Parameterized(_, _) => {}
_ => panic!("Expected Option or Parameterized field type, got {:?}", ty),
}
}
#[test]
fn test_type_ref_to_vec() {
let ty = get_fn_param_type("fn foo(x: &Vec<i32>) { }");
if let Type::Reference(inner) = ty {
match *inner {
Type::Vec(_) | Type::Parameterized(_, _) => {}
_ => panic!("Expected Vec or Parameterized inside Reference"),
}
} else {
panic!("Expected Reference type");
}
}
#[test]
fn test_type_mut_ref_to_parameterized() {
let program = parse_program("fn foo(mut x: HashMap<string, i32>) { }");
if let Some(Item::Function { decl, .. }) = program.items.first() {
let param = &decl.parameters[0];
assert!(param.is_mutable, "mut param should set is_mutable");
if let Type::Parameterized(name, _) = ¶m.type_ {
assert_eq!(name, "HashMap");
} else {
panic!("Expected Parameterized HashMap type, got {:?}", param.type_);
}
} else {
panic!("Expected function item");
}
}
#[test]
fn test_generic_fn_type_param() {
let code = "fn foo<T>(x: T) -> T { x }";
let program = parse_program(code);
if let Some(Item::Function { decl, .. }) = program.items.first() {
assert!(!decl.type_params.is_empty());
if let Some(param) = decl.parameters.first() {
match ¶m.type_ {
Type::Generic(name) | Type::Custom(name) => assert_eq!(name, "T"),
_ => panic!(
"Expected Generic or Custom type for param, got {:?}",
param.type_
),
}
}
} else {
panic!("Expected Function");
}
}
#[test]
fn test_generic_fn_multiple_type_params() {
let code = "fn foo<T, U>(x: T, y: U) -> T { x }";
let program = parse_program(code);
if let Some(Item::Function { decl, .. }) = program.items.first() {
assert_eq!(decl.type_params.len(), 2);
} else {
panic!("Expected Function");
}
}
#[test]
fn test_generic_struct_type_param() {
let code = "struct Container<T> { value: T }";
let program = parse_program(code);
if let Some(Item::Struct { decl, .. }) = program.items.first() {
assert!(!decl.type_params.is_empty());
} else {
panic!("Expected Struct");
}
}
#[test]
fn test_type_with_bound() {
let code = "struct Container<T: Clone> { value: T }";
let program = parse_program(code);
if let Some(Item::Struct { decl, .. }) = program.items.first() {
assert!(!decl.type_params.is_empty());
if let Some(tp) = decl.type_params.first() {
assert!(!tp.bounds.is_empty());
}
} else {
panic!("Expected Struct");
}
}
#[test]
fn test_where_clause() {
let code = r#"
impl<T> Container<T> where T: Clone {
fn clone_value(self) -> T { self.value }
}
"#;
let program = parse_program(code);
if let Some(Item::Impl { block, .. }) = program.items.first() {
assert!(!block.where_clause.is_empty());
} else {
panic!("Expected Impl");
}
}
#[test]
fn test_type_infer() {
let ty = get_fn_param_type("fn foo(x: _) { }");
assert!(matches!(ty, Type::Infer));
}
#[test]
fn test_type_trait_object() {
let ty = get_fn_param_type("fn foo(x: dyn Display) { }");
if let Type::TraitObject(trait_name) = ty {
assert_eq!(trait_name, "Display");
} else {
panic!("Expected TraitObject type, got {:?}", ty);
}
}
#[test]
fn test_vec_of_lowercase_module_path_is_custom_not_associated() {
let ty = get_fn_return_type("pub fn f() -> Vec<ffi::GpuVertex> { }").expect("return type");
match ty {
Type::Vec(inner) => match *inner {
Type::Custom(name) => assert_eq!(name, "ffi::GpuVertex"),
other => panic!("expected Vec<Custom(ffi::GpuVertex)>, got Vec<{:?}>", other),
},
other => panic!("expected Vec, got {:?}", other),
}
}
#[test]
fn test_result_ok_uses_module_path_under_comma_delimiter() {
let ty = get_fn_return_type("pub fn f() -> Result<ffi::GpuVertex, i32> { }").expect("return");
match ty {
Type::Result(ok, _err) => match *ok {
Type::Custom(name) => assert_eq!(name, "ffi::GpuVertex"),
other => panic!("expected Custom(ffi::GpuVertex), got {:?}", other),
},
other => panic!("expected Result, got {:?}", other),
}
}
#[test]
fn test_associated_type_t_output_still_parsed() {
let ty = get_fn_param_type("pub fn g(x: T::Output) { }");
match ty {
Type::Associated(base, assoc) => {
assert_eq!(base, "T");
assert_eq!(assoc, "Output");
}
other => panic!("expected Associated(T, Output), got {:?}", other),
}
}
#[test]
fn test_associated_type_self_item_still_parsed() {
let ty = get_fn_param_type("pub fn g(x: Self::Item) { }");
match ty {
Type::Associated(base, assoc) => {
assert_eq!(base, "Self");
assert_eq!(assoc, "Item");
}
other => panic!("expected Associated(Self, Item), got {:?}", other),
}
}
#[test]
fn test_impl_method_return_vec_ffi_gpuvertex_is_custom_not_associated() {
let code = r#"
pub struct VoxelRenderer {}
impl VoxelRenderer {
fn convert_vertices(self, vertices: Vec<Vertex>) -> Vec<ffi::GpuVertex> {
Vec::new()
}
}
"#;
let program = parse_program(code);
let block = program
.items
.iter()
.find_map(|item| match item {
Item::Impl { block, .. } => Some(block),
_ => None,
})
.expect("impl block");
let func = block
.functions
.iter()
.find(|f| f.name == "convert_vertices")
.expect("convert_vertices");
let ret = func.return_type.as_ref().expect("return type");
match ret {
Type::Vec(inner) => match **inner {
Type::Custom(ref n) => assert_eq!(n, "ffi::GpuVertex"),
ref other => panic!("expected Vec<Custom(ffi::GpuVertex)>, got Vec<{:?}>", other),
},
ref other => panic!("expected Vec return, got {:?}", other),
}
}