From e36911d1782a02786ba5bd483ff5c3fb21ef9d10 Mon Sep 17 00:00:00 2001 From: dak2 Date: Sun, 22 Feb 2026 11:42:24 +0900 Subject: [PATCH] Add ternary operator type inference tests Ternary expressions (a ? b : c) are parsed as IfNode by ruby_prism, so existing conditional handling already supports them. Add tests to verify and prevent regressions. --- rust/src/analyzer/conditionals.rs | 72 ++++++++++++++++++++++++++ test/conditional_test.rb | 86 +++++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+) diff --git a/rust/src/analyzer/conditionals.rs b/rust/src/analyzer/conditionals.rs index 71b5552..65b3374 100644 --- a/rust/src/analyzer/conditionals.rs +++ b/rust/src/analyzer/conditionals.rs @@ -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)"); + } } diff --git a/test/conditional_test.rb b/test/conditional_test.rb index 2fb4341..7c0590d 100644 --- a/test/conditional_test.rb +++ b/test/conditional_test.rb @@ -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