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: Extended mutation detection for .take(), .push(), .insert(), etc.
///
/// Bug: E0596 - cannot borrow as mutable (17 errors in windjammer-game)
/// Root Cause: is_mutating_method() doesn't detect Option::take(), Vec::push(), etc.
/// Fix: Add pattern-based detection for common stdlib mutating methods.
///
/// Philosophy: "Compiler does hard work" - automatic &mut self inference
#[path = "common/test_utils.rs"]
mod test_utils;

#[test]
fn test_take_method_infers_mut_self() {
    let code = r#"
pub struct Container {
    pub value: Option<i32>,
}

impl Container {
    pub fn extract_value(self) -> Option<i32> {
        // .take() mutates self.value - should infer self
        self.value.take()
    }
}
"#;

    let generated = test_utils::compile_single_result(code).expect("Compilation should succeed");

    // Should have &mut self (not &self)
    assert!(
        generated.contains("pub fn extract_value(&mut self)"),
        "extract_value() should infer &mut self because self.value.take() mutates.\nGenerated:\n{}",
        generated
    );
}

#[test]
fn test_push_method_infers_mut_self() {
    let code = r#"
pub struct Buffer {
    pub items: Vec<i32>,
}

impl Buffer {
    pub fn add_item(self, item: i32) {
        self.items.push(item)
    }
}
"#;

    let generated = test_utils::compile_single_result(code).expect("Compilation should succeed");

    assert!(
        generated.contains("pub fn add_item(&mut self, item: i32)"),
        "add_item() should infer &mut self because self.items.push() mutates.\nGenerated:\n{}",
        generated
    );
}

#[test]
fn test_insert_method_infers_mut_self() {
    let code = r#"
use std::collections::HashMap

pub struct Cache {
    pub data: HashMap<string, i32>,
}

impl Cache {
    pub fn store(self, key: string, value: i32) {
        self.data.insert(key, value)
    }
}
"#;

    let generated = test_utils::compile_single_result(code).expect("Compilation should succeed");

    assert!(
        generated.contains("pub fn store(&mut self, key: "),
        "store() should infer &mut self because self.data.insert() mutates.\nGenerated:\n{}",
        generated
    );
}

#[test]
fn test_clear_method_infers_mut_self() {
    let code = r#"
pub struct List {
    pub items: Vec<i32>,
}

impl List {
    pub fn remove_all(self) {
        self.items.clear()
    }
}
"#;

    let generated = test_utils::compile_single_result(code).expect("Compilation should succeed");

    assert!(
        generated.contains("pub fn remove_all(&mut self)"),
        "remove_all() should infer &mut self because self.items.clear() mutates.\nGenerated:\n{}",
        generated
    );
}

#[test]
fn test_pop_method_infers_mut_self() {
    let code = r#"
pub struct Stack {
    pub items: Vec<i32>,
}

impl Stack {
    pub fn pop_item(self) -> Option<i32> {
        self.items.pop()
    }
}
"#;

    let generated = test_utils::compile_single_result(code).expect("Compilation should succeed");

    assert!(
        generated.contains("pub fn pop_item(&mut self)"),
        "pop_item() should infer &mut self because self.items.pop() mutates.\nGenerated:\n{}",
        generated
    );
}

#[test]
fn test_indexed_field_take_infers_mut_self() {
    // Real-world pattern from inventory.wj: self.slots[i].take()
    let code = r#"
pub struct SlotContainer {
    pub slots: Vec<Option<i32>>,
}

impl SlotContainer {
    pub fn remove_at(self, index: usize) -> Option<i32> {
        self.slots[index].take()
    }
}
"#;

    let generated = test_utils::compile_single_result(code).expect("Compilation should succeed");

    assert!(
        generated.contains("pub fn remove_at(&mut self, index: usize)"),
        "remove_at() should infer &mut self because self.slots[index].take() mutates.\nGenerated:\n{}",
        generated
    );
}