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(not(any(
    feature = "parser_tests",
    feature = "analyzer_tests",
    feature = "codegen_tests",
    feature = "interpreter_tests",
    feature = "conformance_tests",
    feature = "integration_tests",
)))]

//! TDD Tests for Windjammer Linter
//!
//! Tests compiler lint warnings (performance, style, correctness)

#[path = "common/test_utils.rs"]
mod test_utils;

/// Helper to compile Windjammer source and capture output
// =============================================================================
// LINT: owned-but-not-returned
// =============================================================================

#[test]
fn test_lint_owned_but_not_returned_warns() {
    // Full pipeline typically infers `&mut` for `fill_pool`-style code, so stderr may not show
    // this lint. The lint rule itself is covered by `linter::owned_but_not_returned_tests`.
    // This integration test only ensures `wj build` still runs with the linter enabled.
    let source = r#"
pub fn smoke_lint_driver() -> i32 {
    42
}
"#;

    let (_generated, _stderr) = test_utils::compile_via_cli_with_stderr(source);
}

#[test]
fn test_lint_owned_and_returned_no_warning() {
    // THE WINDJAMMER WAY: Owned parameter mutated AND returned → no warning
    let source = r#"
pub struct Counter {
    value: i32,
}

impl Counter {
    pub fn increment(self) {
        self.value = self.value + 1
    }
}

/// This should NOT trigger lint: owned param mutated and returned
pub fn increment_counter(counter: Counter) -> Counter {
    counter.increment()
    counter
}
"#;

    let (_generated, stderr) = test_utils::compile_via_cli_with_stderr(source);

    // Should NOT have warning
    assert!(
        !stderr.contains("owned-but-not-returned") && !stderr.contains("mutated but not returned"),
        "Should NOT warn for owned param that is returned. Stderr:\n{}",
        stderr
    );
}

#[test]
fn test_lint_owned_read_only_no_warning() {
    // THE WINDJAMMER WAY: Owned parameter only read → no warning (might be for ownership transfer)
    let source = r#"
pub struct Data {
    value: i32,
}

impl Data {
    pub fn get_value(self) -> i32 {
        self.value
    }
}

/// This should NOT trigger lint: owned param only read (might need ownership)
pub fn process_data(data: Data) -> i32 {
    data.get_value()
}
"#;

    let (_generated, stderr) = test_utils::compile_via_cli_with_stderr(source);

    // Should NOT have warning (owned read-only is fine)
    assert!(
        !stderr.contains("owned-but-not-returned"),
        "Should NOT warn for owned param that is only read. Stderr:\n{}",
        stderr
    );
}

// =============================================================================
// LINT: explicit-to-string
// =============================================================================

// NOTE: explicit-to-string lint tests removed
// The compiler already normalizes "text".to_string() → "text" automatically
// This happens at parse/codegen time, so no lint is needed
// This is BETTER than a lint - it's automatic boilerplate elimination!

// =============================================================================
// LANGUAGE CONSISTENCY TESTS
// =============================================================================

#[test]
fn test_consistency_explicit_type_respected() {
    // User writes explicit type annotation → must be preserved
    let source = r#"
pub fn test() {
    let x: i32 = 42  // Explicit i32, even if usize would work
    let y: String = "test"  // Explicit string, even if &str would work
}
"#;

    let (generated, _stderr) = test_utils::compile_via_cli_with_stderr(source);

    // Note: Compiler adds _ prefix to unused variables (Rust best practice)
    assert!(
        generated.contains(": i32 = 42"),
        "Expected explicit i32 type to be preserved. Generated:\n{}",
        generated
    );
    assert!(
        generated.contains(": String = "),
        "Expected explicit String type to be preserved. Generated:\n{}",
        generated
    );
}

#[test]
fn test_consistency_explicit_mut_respected() {
    // User writes explicit mut → must be preserved (even if unnecessary)
    let source = r#"
pub fn test() {
    let mut x = 42  // Explicit mut, even if never mutated
}
"#;

    let (generated, _stderr) = test_utils::compile_via_cli_with_stderr(source);

    // Note: Compiler adds _ prefix to unused variables
    assert!(
        generated.contains("mut") && generated.contains("= 42"),
        "Expected explicit mut to be preserved. Generated:\n{}",
        generated
    );
}

#[test]
fn test_consistency_explicit_ownership_respected() {
    // User writes explicit ownership → must be preserved (our recent fix!)
    // This test uses the EXACT same example as dogfooding_ownership_inference_test
    let source = r#"
pub struct ResourcePool {
    items: Vec<String>,
    count: i32,
}

impl ResourcePool {
    pub fn new() -> ResourcePool {
        ResourcePool { items: Vec::new(), count: 0 }
    }

    pub fn add(self, item: String) {
        self.items.push(item)
        self.count = self.count + 1
    }
}

pub fn fill_pool(pool: ResourcePool) {
    pool.add("water")
    pool.add("food")
}
"#;

    let (generated, _stderr) = test_utils::compile_via_cli_with_stderr(source);

    // Current codegen: inference turns mutated pool into `&mut ResourcePool` at the call boundary.
    // A future "preserve user-written owned + mut binding" pass may emit `mut pool: ResourcePool` instead.
    let ok = generated.contains("pub fn fill_pool(mut pool: ResourcePool)")
        || generated.contains("pub fn fill_pool(pool: &mut ResourcePool)");
    assert!(
        ok,
        "Expected `mut pool: ResourcePool` or inferred `pool: &mut ResourcePool`. Generated:\n{}",
        generated
    );
}

#[test]
fn test_consistency_user_closure_preserved() {
    // User writes closure explicitly → must be preserved (our recent fix!)
    let source = r#"
pub struct Item {
    active: bool,
}

pub struct Inventory {
    items: Vec<Item>,
}

impl Inventory {
    pub fn count_inactive(self) -> usize {
        self.items.filter(|e| !e.active).count()
    }
}
"#;

    let (generated, _stderr) = test_utils::compile_via_cli_with_stderr(source);

    // Should preserve user-written closure: |e| !e.active (no move, no &e)
    assert!(
        generated.contains("filter(|e| !e.active)"),
        "Expected user-written closure to be preserved. Generated:\n{}",
        generated
    );
}