Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions rust/src/analyzer/conditionals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -463,4 +463,76 @@ end
let ret_vtx = info.return_vertex.unwrap();
assert_eq!(get_type_show(&genv, ret_vtx), "String");
}

// Test 11: ternary operator - different types → union
#[test]
fn test_ternary_union_type() {
let source = r#"
class Foo
def bar
true ? "hello" : 42
end
end
"#;
let genv = analyze(source);
let info = genv
.resolve_method(&Type::instance("Foo"), "bar")
.expect("Foo#bar should be registered");
let ret_vtx = info.return_vertex.unwrap();
assert_eq!(get_type_show(&genv, ret_vtx), "(Integer | String)");
}

// Test 12: ternary operator - same type → single type
#[test]
fn test_ternary_same_type() {
let source = r#"
class Foo
def bar
true ? "hello" : "world"
end
end
"#;
let genv = analyze(source);
let info = genv
.resolve_method(&Type::instance("Foo"), "bar")
.expect("Foo#bar should be registered");
let ret_vtx = info.return_vertex.unwrap();
assert_eq!(get_type_show(&genv, ret_vtx), "String");
}

// Test 13: ternary operator - nil branch → union with nil
#[test]
fn test_ternary_nil_branch() {
let source = r#"
class Foo
def bar
true ? "hello" : nil
end
end
"#;
let genv = analyze(source);
let info = genv
.resolve_method(&Type::instance("Foo"), "bar")
.expect("Foo#bar should be registered");
let ret_vtx = info.return_vertex.unwrap();
assert_eq!(get_type_show(&genv, ret_vtx), "(String | nil)");
}

// Test 14: nested ternary operator
#[test]
fn test_ternary_nested() {
let source = r#"
class Foo
def bar
true ? (false ? "inner" : 42) : :sym
end
end
"#;
let genv = analyze(source);
let info = genv
.resolve_method(&Type::instance("Foo"), "bar")
.expect("Foo#bar should be registered");
let ret_vtx = info.return_vertex.unwrap();
assert_eq!(get_type_show(&genv, ret_vtx), "(Integer | String | Symbol)");
}
}
86 changes: 86 additions & 0 deletions test/conditional_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -215,4 +215,90 @@ def baz

assert_check_error(source, method_name: 'upcase', receiver_type: 'Integer')
end

# ============================================
# Ternary Operator - Type Inference
# ============================================

def test_ternary_union_type
source = <<~RUBY
x = true ? "hello" : 42
RUBY

types = infer(source)
assert_equal "(Integer | String)", types["x"]
end

def test_ternary_same_type
source = <<~RUBY
x = true ? "hello" : "world"
RUBY

assert_type source, "x", "String"
end

def test_ternary_nil_branch
source = <<~RUBY
x = true ? "hello" : nil
RUBY

types = infer(source)
assert_equal "(String | nil)", types["x"]
end

# ============================================
# Ternary Operator - No Error
# ============================================

def test_ternary_string_upcase
source = <<~RUBY
class Foo
def bar
true ? "a" : "b"
end

def baz
self.bar.upcase
end
end
RUBY

assert_no_check_errors(source)
end

# ============================================
# Ternary Operator - Error Detection
# ============================================

def test_ternary_string_even_error
source = <<~RUBY
class Foo
def bar
true ? "a" : "b"
end

def baz
self.bar.even?
end
end
RUBY

assert_check_error(source, method_name: 'even?', receiver_type: 'String')
end

def test_ternary_nested_error
source = <<~RUBY
class Foo
def bar
true ? (false ? "inner" : "other") : "fallback"
end

def baz
self.bar.even?
end
end
RUBY

assert_check_error(source, method_name: 'even?', receiver_type: 'String')
end
end