#![cfg(not(any(
feature = "parser_tests",
feature = "analyzer_tests",
feature = "codegen_tests",
feature = "interpreter_tests",
feature = "conformance_tests",
feature = "integration_tests",
)))]
use windjammer::analyzer::{FunctionSignature, OwnershipMode};
use windjammer::parser::ast::Type;
use windjammer::*;
#[path = "common/test_utils.rs"]
mod test_utils;
#[test]
fn test_static_call_i32_param_not_cast_to_f32() {
let source = r#"
struct Vec3 {
x: f32,
y: f32,
z: f32,
}
impl Vec3 {
fn new(x: f32, y: f32, z: f32) -> Vec3 {
Vec3 { x: x, y: y, z: z }
}
}
struct Emitter {
pos: Vec3,
max_particles: i32,
rate: f32,
}
impl Emitter {
fn new(pos: Vec3, max_particles: i32) -> Emitter {
Emitter { pos: pos, max_particles: max_particles, rate: 10.0 }
}
}
fn create_emitter(pos: Vec3) -> Emitter {
let mut emitter = Emitter::new(pos, 30)
emitter.rate = 500.0
emitter
}
"#;
let rust_code = test_utils::compile_single(source);
assert!(
!rust_code.contains("30 as f32") && !rust_code.contains("30_i32 as f32"),
"Integer 30 passed to i32 param should NOT be cast to f32.\nGenerated:\n{}",
rust_code
);
}
#[test]
fn test_static_call_f32_param_still_casts_int() {
let source = r#"
struct Config {
value: f32,
}
impl Config {
fn new(value: f32) -> Config {
Config { value: value }
}
}
fn make_config() -> Config {
Config::new(42)
}
"#;
let rust_code = test_utils::compile_single(source);
assert!(
rust_code.contains("42 as f32")
|| rust_code.contains("42_f32")
|| rust_code.contains("42.0"),
"Integer 42 passed to f32 param SHOULD be cast to f32.\nGenerated:\n{}",
rust_code
);
}
#[test]
fn test_multiple_types_with_new_correct_dispatch() {
let source = r#"
struct TypeA {
count: i32,
}
impl TypeA {
fn new(count: i32) -> TypeA {
TypeA { count: count }
}
}
struct TypeB {
value: f32,
}
impl TypeB {
fn new(value: f32) -> TypeB {
TypeB { value: value }
}
}
fn test() {
let a = TypeA::new(10)
let b = TypeB::new(20)
}
"#;
let rust_code = test_utils::compile_single(source);
let lines: Vec<&str> = rust_code.lines().collect();
let mut _found_a_correct = false;
let mut _found_b_correct = false;
for line in &lines {
if line.contains("TypeA::new") {
assert!(
!line.contains("10 as f32") && !line.contains("10_i32 as f32"),
"TypeA::new(10) should NOT cast to f32 (param is i32).\nLine: {}",
line
);
_found_a_correct = true;
}
if line.contains("TypeB::new") {
assert!(
line.contains("20 as f32") || line.contains("20_f32") || line.contains("20.0"),
"TypeB::new(20) SHOULD cast to f32 (param is f32).\nLine: {}",
line
);
_found_b_correct = true;
}
}
}
#[test]
fn test_signature_collision_no_incorrect_cast() {
let source = r#"
struct Vec3 { x: f32, y: f32, z: f32 }
struct Emitter {
pos: Vec3,
max_particles: i32,
}
impl Emitter {
fn new(pos: Vec3, max_particles: i32) -> Emitter {
Emitter { pos: pos, max_particles: max_particles }
}
}
fn create(pos: Vec3) -> Emitter {
Emitter::new(pos, 30)
}
"#;
let mut global_sigs = analyzer::SignatureRegistry::new();
global_sigs.add_function(
"Emitter::new".to_string(),
FunctionSignature {
name: "new".to_string(),
param_types: vec![
Type::Custom("f32".to_string()),
Type::Custom("f32".to_string()),
],
param_ownership: vec![OwnershipMode::Owned, OwnershipMode::Owned],
return_type: Some(Type::Custom("Emitter".to_string())),
return_ownership: OwnershipMode::Owned,
has_self_receiver: false,
is_extern: false,
},
);
let rust_code = test_utils::compile_single(source);
assert!(
!rust_code.contains("30 as f32") && !rust_code.contains("30_i32 as f32"),
"Integer 30 passed to i32 param should NOT be cast to f32, even when global registry has wrong signature.\nGenerated:\n{}",
rust_code
);
}
#[test]
fn test_signature_collision_consumer_wrong_sig_wins() {
let files = &[
(
"effects.wj",
r#"
struct Vec3 { x: f32, y: f32, z: f32 }
struct Emitter { pos: Vec3, max_particles: i32 }
impl Emitter {
fn new(pos: Vec3, max_particles: i32) -> Emitter {
Emitter { pos: pos, max_particles: max_particles }
}
}
"#,
),
(
"particles.wj",
r#"
struct Emitter { rate: f32, lifetime: f32 }
impl Emitter {
fn new(rate: f32, lifetime: f32) -> Emitter {
Emitter { rate: rate, lifetime: lifetime }
}
}
"#,
),
(
"consumer.wj",
r#"
fn create() {
let e = Emitter::new(1.0, 30)
}
"#,
),
];
let results = test_utils::compile_project(files);
let consumer_rs = results.get("consumer.rs").expect("consumer.rs not generated");
assert!(
!consumer_rs.contains("30 as f32"),
"Integer 30 should NOT be cast to f32 when signature has collision.\nGenerated:\n{}",
consumer_rs
);
}
#[test]
fn test_signature_registry_collision_detection() {
let mut registry = analyzer::SignatureRegistry::new();
registry.add_function(
"Foo::new".to_string(),
FunctionSignature {
name: "new".to_string(),
param_types: vec![Type::Custom("f32".to_string())],
param_ownership: vec![OwnershipMode::Owned],
return_type: Some(Type::Custom("Foo".to_string())),
return_ownership: OwnershipMode::Owned,
has_self_receiver: false,
is_extern: false,
},
);
assert!(
!registry.has_collision("Foo::new"),
"No collision after first registration"
);
registry.add_function(
"Foo::new".to_string(),
FunctionSignature {
name: "new".to_string(),
param_types: vec![Type::Custom("f32".to_string())],
param_ownership: vec![OwnershipMode::Owned],
return_type: Some(Type::Custom("Foo".to_string())),
return_ownership: OwnershipMode::Owned,
has_self_receiver: false,
is_extern: false,
},
);
assert!(
!registry.has_collision("Foo::new"),
"Same signature should not be a collision"
);
registry.add_function(
"Foo::new".to_string(),
FunctionSignature {
name: "new".to_string(),
param_types: vec![Type::Custom("i32".to_string())],
param_ownership: vec![OwnershipMode::Owned],
return_type: Some(Type::Custom("Foo".to_string())),
return_ownership: OwnershipMode::Owned,
has_self_receiver: false,
is_extern: false,
},
);
assert!(
registry.has_collision("Foo::new"),
"Different param types should be a collision"
);
}
#[test]
fn test_collision_survives_merge() {
let mut reg1 = analyzer::SignatureRegistry::new();
reg1.add_function(
"Bar::new".to_string(),
FunctionSignature {
name: "new".to_string(),
param_types: vec![Type::Custom("f32".to_string())],
param_ownership: vec![OwnershipMode::Owned],
return_type: None,
return_ownership: OwnershipMode::Owned,
has_self_receiver: false,
is_extern: false,
},
);
let mut reg2 = analyzer::SignatureRegistry::new();
reg2.add_function(
"Bar::new".to_string(),
FunctionSignature {
name: "new".to_string(),
param_types: vec![Type::Custom("i32".to_string())],
param_ownership: vec![OwnershipMode::Owned],
return_type: None,
return_ownership: OwnershipMode::Owned,
has_self_receiver: false,
is_extern: false,
},
);
reg1.merge(®2);
assert!(
reg1.has_collision("Bar::new"),
"Collision should be detected during merge"
);
}