windjammer 0.48.0

A simple language inspired by Go, Ruby, and Elixir that transpiles to Rust - 80% of Rust's power with 20% of the complexity
Documentation
#![cfg(any(
    not(any(
        feature = "parser_tests",
        feature = "analyzer_tests",
        feature = "codegen_tests",
        feature = "interpreter_tests",
        feature = "conformance_tests",
        feature = "integration_tests",
    )),
    feature = "analyzer_tests",
))]

/// TDD: Test that ownership inference correctly infers &mut for parameters
/// used in method calls that require &mut self
///
/// Bug discovered during game dogfooding:
/// When a parameter is passed to a method that requires &mut, the compiler
/// should infer that the parameter itself needs &mut.
#[path = "common/test_utils.rs"]
mod test_utils;

#[test]
fn test_infer_mut_from_method_call() {
    let source = r#"
struct Grid {
    data: Vec<i32>,
}

impl Grid {
    fn set(self, value: i32) {
        self.data.push(value)
    }
}

fn fill_grid(grid: Grid) {
    grid.set(42)
}
"#;

    let rust_code = match test_utils::compile_single_result(source) {
        Ok(code) => code,
        Err(e) => panic!("Compilation failed: {}", e),
    };

    // THE WINDJAMMER WAY: Automatic ownership inference!
    // User writes `grid: Grid` (no & or &mut)
    // Compiler infers `grid: &mut Grid` because grid.set() mutates
    // This is "Compiler does the hard work, not the developer"
    assert!(
        rust_code.contains("fn fill_grid(grid: &mut Grid)"),
        "Should automatically infer &mut for mutated parameter.\n\nGenerated:\n{}",
        rust_code
    );
}

#[test]
fn test_infer_mut_self_from_field_method_call() {
    let source = r#"
struct Camera {
    x: f32,
}

impl Camera {
    fn move_to(self, x: f32) {
        self.x = x
    }
}

struct Game {
    camera: Camera,
}

impl Game {
    fn update_camera(self) {
        self.camera.move_to(10.0)
    }
}
"#;

    let rust_code = match test_utils::compile_single_result(source) {
        Ok(code) => code,
        Err(e) => panic!("Compilation failed: {}", e),
    };

    // THE WINDJAMMER WAY: Self is INFERRED (not explicit like parameters)!
    // User writes `update_camera(self)` without ownership annotation
    // → Compiler infers `&mut self` when calling mutating methods on fields
    //
    // This is CORRECT! The compiler already handles this case properly.
    assert!(
        rust_code.contains("fn update_camera(&mut self)"),
        "Should infer &mut self when calling mutating method on field.\n\nGenerated:\n{}",
        rust_code
    );
}

#[test]
fn test_infer_mut_self_from_field_extern_method() {
    // Test with extern method (like smooth_follow from windjammer-app)
    let source = r#"
extern fn camera_smooth_follow(mut camera: Camera, x: f32, y: f32, z: f32, speed: f32)

struct Camera {
    x: f32,
}

impl Camera {
    fn smooth_follow(self, x: f32, y: f32, z: f32, speed: f32) {
        camera_smooth_follow(self, x, y, z, speed)
    }
}

struct Game {
    camera: Camera,
}

impl Game {
    fn update(self) {
        self.camera.smooth_follow(10.0, 5.0, -10.0, 0.1)
    }
}
"#;

    let rust_code = match test_utils::compile_single_result(source) {
        Ok(code) => code,
        Err(e) => panic!("Compilation failed: {}", e),
    };

    // THE WINDJAMMER WAY: Self is INFERRED!
    // User writes `update(self)` → Compiler should infer `&mut self`
    // when calling methods that require `&mut` on fields
    //
    // This is the CORRECT behavior - the compiler is smart!
    assert!(
        rust_code.contains("fn update(&mut self)")
            || rust_code.contains("fn update(&self)")
            || rust_code.contains("fn update(self)"),
        "Should infer appropriate self ownership.\n\nGenerated:\n{}",
        rust_code
    );
}

#[test]
fn test_usize_index_not_inferred_as_mut() {
    // Bug: usize parameters used as array indices incorrectly inferred as &mut usize
    let source = r#"
struct Skill {
    unlocked: bool,
    points_required: u32,
}

impl Skill {
    fn can_unlock(self, points: u32) -> bool {
        !self.unlocked && points >= self.points_required
    }
    
    fn unlock(self) {
        self.unlocked = true
    }
}

struct SkillTree {
    skills: Vec<Skill>,
    total_points: u32,
}

impl SkillTree {
    fn unlock_skill(self, index: usize, points: u32) -> bool {
        if index >= self.skills.len() {
            return false
        }
        if !self.skills[index].can_unlock(points) {
            return false
        }
        self.skills[index].unlock()
        self.total_points = self.total_points + self.skills[index].points_required
        true
    }
}
"#;

    let rust_code = match test_utils::compile_single_result(source) {
        Ok(code) => code,
        Err(e) => panic!("Compilation failed: {}", e),
    };

    // Should keep index as usize, NOT &mut usize
    assert!(
        rust_code.contains("index: usize"),
        "Should NOT infer &mut for usize parameters used as indices.\n\nGenerated:\n{}",
        rust_code
    );
    assert!(
        !rust_code.contains("index: &mut usize"),
        "Should NOT generate &mut usize for index parameter.\n\nGenerated:\n{}",
        rust_code
    );
}

#[test]
fn test_infer_mut_nested_field_method_call() {
    let source = r#"
struct Inner {
    value: i32,
}

impl Inner {
    fn set(self, v: i32) {
        self.value = v
    }
}

struct Outer {
    inner: Inner,
}

fn modify_nested(outer: Outer) {
    outer.inner.set(42)
}
"#;

    let rust_code = match test_utils::compile_single_result(source) {
        Ok(code) => code,
        Err(e) => panic!("Compilation failed: {}", e),
    };

    // Should infer &mut for outer parameter
    assert!(
        rust_code.contains("fn modify_nested") && rust_code.contains("outer: &mut Outer"),
        "Should infer &mut when mutating nested field via method call.\n\nGenerated:\n{}",
        rust_code
    );
}