diff --git a/rust/src/rbs/method_loader.rb b/rust/src/rbs/method_loader.rb index 2694c0c..a0f8f05 100644 --- a/rust/src/rbs/method_loader.rb +++ b/rust/src/rbs/method_loader.rb @@ -12,6 +12,7 @@ class MethodLoader String Integer Float Array Hash Symbol TrueClass FalseClass NilClass Range Regexp Struct Enumerable + Kernel Object ].freeze def initialize diff --git a/test/kernel_test.rb b/test/kernel_test.rb new file mode 100644 index 0000000..d57f221 --- /dev/null +++ b/test/kernel_test.rb @@ -0,0 +1,178 @@ +# frozen_string_literal: true + +require 'test_helper' + +class KernelTest < Minitest::Test + include CLITestHelper + + # ============================================ + # No Error + # ============================================ + + def test_freeze_on_integer + source = <<~RUBY + class Freezer + def freeze_value + x = 42 + x.freeze + end + end + RUBY + + assert_no_check_errors(source) + end + + def test_frozen_on_string + source = <<~RUBY + class Checker + def check + x = "hello" + x.frozen? + end + end + RUBY + + assert_no_check_errors(source) + end + + def test_tap_on_string + source = <<~RUBY + class Logger + def log + x = "hello" + x.tap + end + end + RUBY + + assert_no_check_errors(source) + end + + def test_object_id_on_string + source = <<~RUBY + class Inspector + def inspect_id + x = "hello" + x.object_id + end + end + RUBY + + assert_no_check_errors(source) + end + + def test_send_on_string + source = <<~RUBY + class Caller + def dynamic_call + x = "hello" + x.send(:upcase) + end + end + RUBY + + assert_no_check_errors(source) + end + + def test_kernel_methods_on_array + source = <<~RUBY + class Processor + def process + x = [1, 2, 3] + x.freeze + x.frozen? + x.object_id + end + end + RUBY + + assert_no_check_errors(source) + end + + def test_kernel_methods_on_hash + source = <<~RUBY + class Processor + def process + x = { a: 1 } + x.freeze + x.frozen? + x.object_id + end + end + RUBY + + assert_no_check_errors(source) + end + + # ============================================ + # Override + # ============================================ + + def test_override_freeze_no_error + source = <<~RUBY + class Foo + def freeze + self + end + + def bar + freeze + end + end + RUBY + + assert_no_check_errors(source) + end + + def test_override_hash_no_error + source = <<~RUBY + class Foo + def hash + 42 + end + + def bar + hash.even? + end + end + RUBY + + assert_no_check_errors(source) + end + + # ============================================ + # Error Detection (Override) + # ============================================ + + def test_override_freeze_return_type_error + source = <<~RUBY + class Foo + def freeze + self + end + + def bar + freeze.baz + end + end + RUBY + + assert_check_error(source, method_name: 'baz', receiver_type: 'Foo') + end + + def test_override_hash_return_type_error + source = <<~RUBY + class Foo + def hash + 42 + end + + def bar + hash.upcase + end + end + RUBY + + assert_check_error(source, method_name: 'upcase', receiver_type: 'Integer') + end +end diff --git a/test/method_loader_test.rb b/test/method_loader_test.rb new file mode 100644 index 0000000..212f2c3 --- /dev/null +++ b/test/method_loader_test.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require 'test_helper' + +class MethodLoaderTest < Minitest::Test + def setup + require_relative '../rust/src/rbs/method_loader' + @loader = Rbs::MethodLoader.new + @results = @loader.load_methods + end + + def test_existing_classes_still_loaded + loaded_classes = @results.map { |r| r[:receiver_class] }.uniq + %w[String Integer Float Array Hash Symbol Kernel Object].each do |cls| + assert_includes loaded_classes, cls, "#{cls} should be loaded" + end + end +end diff --git a/test/object_test.rb b/test/object_test.rb new file mode 100644 index 0000000..a93c9f0 --- /dev/null +++ b/test/object_test.rb @@ -0,0 +1,241 @@ +# frozen_string_literal: true + +require 'test_helper' + +class ObjectTest < Minitest::Test + include CLITestHelper + + # ============================================ + # No Error + # ============================================ + + def test_nil_check_on_string + source = <<~RUBY + class Checker + def check + x = "hello" + x.nil? + end + end + RUBY + + assert_no_check_errors(source) + end + + def test_nil_check_on_integer + source = <<~RUBY + class Checker + def check + x = 42 + x.nil? + end + end + RUBY + + assert_no_check_errors(source) + end + + def test_class_method_on_string + source = <<~RUBY + class Inspector + def inspect_type + x = "hello" + x.class + end + end + RUBY + + assert_no_check_errors(source) + end + + def test_dup_on_string + source = <<~RUBY + class Copier + def copy + x = "hello" + x.dup + end + end + RUBY + + assert_no_check_errors(source) + end + + def test_freeze_on_string + source = <<~RUBY + class Freezer + def freeze_value + x = "hello" + x.freeze + end + end + RUBY + + assert_no_check_errors(source) + end + + def test_frozen_check_on_array + source = <<~RUBY + class Checker + def check + x = [1, 2, 3] + x.frozen? + end + end + RUBY + + assert_no_check_errors(source) + end + + def test_is_a_on_string + source = <<~RUBY + class TypeChecker + def check + x = "hello" + x.is_a?(String) + end + end + RUBY + + assert_no_check_errors(source) + end + + def test_respond_to_on_integer + source = <<~RUBY + class Checker + def check + x = 42 + x.respond_to?(:even?) + end + end + RUBY + + assert_no_check_errors(source) + end + + def test_to_s_on_integer + source = <<~RUBY + class Converter + def convert + x = 42 + x.to_s + end + end + RUBY + + assert_no_check_errors(source) + end + + def test_hash_method_on_string + source = <<~RUBY + class Hasher + def compute + x = "hello" + x.hash + end + end + RUBY + + assert_no_check_errors(source) + end + + def test_equal_check_on_integer + source = <<~RUBY + class Comparer + def compare + x = 42 + x.equal?(42) + end + end + RUBY + + assert_no_check_errors(source) + end + + def test_object_methods_on_hash + source = <<~RUBY + class Processor + def process + x = { a: 1 } + x.nil? + x.frozen? + x.class + end + end + RUBY + + assert_no_check_errors(source) + end + + # ============================================ + # Override + # ============================================ + + def test_override_to_s_no_error + source = <<~RUBY + class Foo + def to_s + "Foo" + end + + def bar + to_s.upcase + end + end + RUBY + + assert_no_check_errors(source) + end + + def test_override_nil_no_error + source = <<~RUBY + class Foo + def nil? + false + end + + def bar + nil? + end + end + RUBY + + assert_no_check_errors(source) + end + + # ============================================ + # Error Detection (Override) + # ============================================ + + def test_override_to_s_return_type_error + source = <<~RUBY + class Foo + def to_s + "Foo" + end + + def bar + to_s.even? + end + end + RUBY + + assert_check_error(source, method_name: 'even?', receiver_type: 'String') + end + + def test_override_dup_return_type_error + source = <<~RUBY + class Foo + def dup + "copy" + end + + def bar + dup.even? + end + end + RUBY + + assert_check_error(source, method_name: 'even?', receiver_type: 'String') + end +end