diff options
Diffstat (limited to 'test/ruby/test_compile_prism.rb')
-rw-r--r-- | test/ruby/test_compile_prism.rb | 2671 |
1 files changed, 2671 insertions, 0 deletions
diff --git a/test/ruby/test_compile_prism.rb b/test/ruby/test_compile_prism.rb new file mode 100644 index 0000000000..d13b150f93 --- /dev/null +++ b/test/ruby/test_compile_prism.rb @@ -0,0 +1,2671 @@ +# frozen_string_literal: true + +# This file is organized to match itemization in https://github.com/ruby/prism/issues/1335 +module Prism + class TestCompilePrism < Test::Unit::TestCase + # Subclass is used for tests which need it + class Subclass; end + ############################################################################ + # Literals # + ############################################################################ + + def test_FalseNode + assert_prism_eval("false") + end + + def test_FloatNode + assert_prism_eval("1.2") + assert_prism_eval("1.2e3") + assert_prism_eval("+1.2e+3") + assert_prism_eval("-1.2e-3") + end + + def test_ImaginaryNode + assert_prism_eval("1i") + assert_prism_eval("+1.0i") + assert_prism_eval("1ri") + end + + def test_IntegerNode + assert_prism_eval("1") + assert_prism_eval("+1") + assert_prism_eval("-1") + assert_prism_eval("0x10") + assert_prism_eval("0b10") + assert_prism_eval("0o10") + assert_prism_eval("010") + assert_prism_eval("(0o00)") + end + + def test_NilNode + assert_prism_eval("nil") + end + + def test_RationalNode + assert_prism_eval("1.2r") + assert_prism_eval("+1.2r") + end + + def test_SelfNode + assert_prism_eval("self") + end + + def test_SourceEncodingNode + assert_prism_eval("__ENCODING__") + end + + def test_SourceFileNode + assert_prism_eval("__FILE__") + end + + def test_SourceLineNode + assert_prism_eval("__LINE__", raw: true) + end + + def test_TrueNode + assert_prism_eval("true") + end + + ############################################################################ + # Reads # + ############################################################################ + + def test_BackReferenceReadNode + assert_prism_eval("$+") + end + + def test_ClassVariableReadNode + assert_prism_eval("class Prism::TestCompilePrism; @@pit = 1; @@pit; end") + end + + def test_ConstantPathNode + assert_prism_eval("Prism::TestCompilePrism") + end + + def test_ConstantReadNode + assert_prism_eval("Prism") + end + + Z = 1 + + def test_DefinedNode + assert_prism_eval("defined? nil") + assert_prism_eval("defined? self") + assert_prism_eval("defined? true") + assert_prism_eval("defined? false") + assert_prism_eval("defined? 1") + assert_prism_eval("defined? 1i") + assert_prism_eval("defined? 1.0") + assert_prism_eval("defined? 1..2") + assert_prism_eval("defined? [A, B, C]") + assert_prism_eval("defined? [1, 2, 3]") + assert_prism_eval("defined?({ a: 1 })") + assert_prism_eval("defined? 'str'") + assert_prism_eval('defined?("#{expr}")') + assert_prism_eval("defined? :sym") + assert_prism_eval("defined? /foo/") + assert_prism_eval('defined?(/#{1}/)') + assert_prism_eval("defined? -> { 1 + 1 }") + assert_prism_eval("defined? a && b") + assert_prism_eval("defined? a || b") + assert_prism_eval("defined? __ENCODING__") + assert_prism_eval("defined? __FILE__") + assert_prism_eval("defined? __LINE__") + + assert_prism_eval("defined? %[1,2,3]") + assert_prism_eval("defined? %q[1,2,3]") + assert_prism_eval("defined? %Q[1,2,3]") + assert_prism_eval("defined? %r[1,2,3]") + assert_prism_eval("defined? %i[1,2,3]") + assert_prism_eval("defined? %I[1,2,3]") + assert_prism_eval("defined? %w[1,2,3]") + assert_prism_eval("defined? %W[1,2,3]") + assert_prism_eval("defined? %s[1,2,3]") + assert_prism_eval("defined? %x[1,2,3]") + + assert_prism_eval("defined? [*b]") + assert_prism_eval("defined? [[*1..2], 3, *4..5]") + assert_prism_eval("defined? [a: [:b, :c]]") + assert_prism_eval("defined? 1 in 1") + + assert_prism_eval("defined? @a") + assert_prism_eval("defined? $a") + assert_prism_eval("defined? @@a") + assert_prism_eval("defined? A") + assert_prism_eval("defined? ::A") + assert_prism_eval("defined? A::B") + assert_prism_eval("defined? A::B::C") + assert_prism_eval("defined? #{self.class.name}::Z::A") + assert_prism_eval("defined? yield") + assert_prism_eval("defined? super") + + assert_prism_eval("defined? X = 1") + assert_prism_eval("defined? X *= 1") + assert_prism_eval("defined? X /= 1") + assert_prism_eval("defined? X &= 1") + assert_prism_eval("defined? X ||= 1") + + assert_prism_eval("defined? $1") + assert_prism_eval("defined? $2") + assert_prism_eval("defined? $`") + assert_prism_eval("defined? $'") + assert_prism_eval("defined? $+") + + assert_prism_eval("defined? $X = 1") + assert_prism_eval("defined? $X *= 1") + assert_prism_eval("defined? $X /= 1") + assert_prism_eval("defined? $X &= 1") + assert_prism_eval("defined? $X ||= 1") + + assert_prism_eval("defined? @@X = 1") + assert_prism_eval("defined? @@X *= 1") + assert_prism_eval("defined? @@X /= 1") + assert_prism_eval("defined? @@X &= 1") + assert_prism_eval("defined? @@X ||= 1") + + assert_prism_eval("defined? @X = 1") + assert_prism_eval("defined? @X *= 1") + assert_prism_eval("defined? @X /= 1") + assert_prism_eval("defined? @X &= 1") + assert_prism_eval("defined? @X ||= 1") + + assert_prism_eval("x = 1; defined? x = 1") + assert_prism_eval("x = 1; defined? x *= 1") + assert_prism_eval("x = 1; defined? x /= 1") + assert_prism_eval("x = 1; defined? x &= 1") + assert_prism_eval("x = 1; defined? x ||= 1") + + assert_prism_eval("if defined? A; end") + + assert_prism_eval("defined?(())") + assert_prism_eval("defined?(('1'))") + + # method chain starting with self that's truthy + assert_prism_eval("defined?(self.itself.itself.itself)") + + # method chain starting with self that's false (exception swallowed) + assert_prism_eval("defined?(self.itself.itself.neat)") + + # single self with method, truthy + assert_prism_eval("defined?(self.itself)") + + # single self with method, false + assert_prism_eval("defined?(self.neat!)") + + # method chain implicit self that's truthy + assert_prism_eval("defined?(itself.itself.itself)") + + # method chain implicit self that's false + assert_prism_eval("defined?(itself.neat.itself)") + + ## single method implicit self that's truthy + assert_prism_eval("defined?(itself)") + + ## single method implicit self that's false + assert_prism_eval("defined?(neatneat)") + + assert_prism_eval("defined?(a(itself))") + assert_prism_eval("defined?(itself(itself))") + + # Method chain on a constant + assert_prism_eval(<<~RUBY) + class PrismDefinedNode + def m1; end + end + + defined?(PrismDefinedNode.new.m1) + RUBY + + assert_prism_eval("defined?(next)") + assert_prism_eval("defined?(break)") + assert_prism_eval("defined?(redo)") + assert_prism_eval("defined?(retry)") + + assert_prism_eval(<<~RUBY) + class PrismDefinedReturnNode + def self.m1; defined?(return) end + end + + PrismDefinedReturnNode.m1 + RUBY + + assert_prism_eval("defined?(begin; 1; end)") + + assert_prism_eval("defined?(defined?(a))") + assert_prism_eval('defined?(:"#{1}")') + assert_prism_eval("defined?(`echo #{1}`)") + + assert_prism_eval("defined?(PrismTestSubclass.test_call_and_write_node &&= 1)") + assert_prism_eval("defined?(PrismTestSubclass.test_call_operator_write_node += 1)") + assert_prism_eval("defined?(PrismTestSubclass.test_call_or_write_node ||= 1)") + assert_prism_eval("defined?(Prism::CPAWN &&= 1)") + assert_prism_eval("defined?(Prism::CPOWN += 1)") + assert_prism_eval("defined?(Prism::CPOrWN ||= 1)") + assert_prism_eval("defined?(Prism::CPWN = 1)") + assert_prism_eval("defined?([0][0] &&= 1)") + assert_prism_eval("defined?([0][0] += 1)") + assert_prism_eval("defined?([0][0] ||= 1)") + + assert_prism_eval("defined?(case :a; when :a; 1; else; 2; end)") + assert_prism_eval("defined?(case [1, 2, 3]; in [1, 2, 3]; 4; end)") + assert_prism_eval("defined?(class PrismClassA; end)") + assert_prism_eval("defined?(def prism_test_def_node; end)") + assert_prism_eval("defined?(for i in [1,2] do; i; end)") + assert_prism_eval("defined?(if true; 1; end)") + assert_prism_eval("defined?(/(?<foo>bar)/ =~ 'barbar')") + assert_prism_eval("defined?(1 => 1)") + assert_prism_eval("defined?(module M; end)") + assert_prism_eval("defined?(1.2r)") + assert_prism_eval("defined?(class << self; end)") + assert_prism_eval("defined?(while a != 1; end)") + assert_prism_eval("defined?(until a == 1; end)") + assert_prism_eval("defined?(unless true; 1; end)") + end + + def test_GlobalVariableReadNode + assert_prism_eval("$pit = 1; $pit") + end + + def test_InstanceVariableReadNode + assert_prism_eval("class Prism::TestCompilePrism; @pit = 1; @pit; end") + end + + def test_LocalVariableReadNode + assert_prism_eval("pit = 1; pit") + end + + def test_NumberedReferenceReadNode + assert_prism_eval("$1") + assert_prism_eval("$99999") + end + + ############################################################################ + # Writes # + ############################################################################ + + def test_ClassVariableAndWriteNode + assert_prism_eval("class Prism::TestCompilePrism; @@pit = 0; @@pit &&= 1; end") + end + + def test_ClassVariableOperatorWriteNode + assert_prism_eval("class Prism::TestCompilePrism; @@pit = 0; @@pit += 1; end") + end + + def test_ClassVariableOrWriteNode + assert_prism_eval("class Prism::TestCompilePrism; @@pit = 1; @@pit ||= 0; end") + assert_prism_eval("class Prism::TestCompilePrism; @@pit = nil; @@pit ||= 1; end") + end + + def test_ClassVariableWriteNode + assert_prism_eval("class Prism::TestCompilePrism; @@pit = 1; end") + end + + def test_ConstantAndWriteNode + assert_prism_eval("Constant = 1; Constant &&= 1") + end + + def test_ConstantOperatorWriteNode + assert_prism_eval("Constant = 1; Constant += 1") + end + + def test_ConstantOrWriteNode + assert_prism_eval("Constant = 1; Constant ||= 1") + end + + def test_ConstantWriteNode + # We don't call assert_prism_eval directly in this case because we + # don't want to assign the constant multiple times if we run + # with `--repeat-count` + # Instead, we eval manually here, and remove the constant to + constant_name = "YCT" + source = "#{constant_name} = 1" + prism_eval = RubyVM::InstructionSequence.compile_prism(source).eval + assert_equal prism_eval, 1 + Object.send(:remove_const, constant_name) + end + + def test_ConstantPathWriteNode + assert_prism_eval("Prism::CPWN = 1") + assert_prism_eval("::CPWN = 1") + end + + def test_ConstantPathAndWriteNode + assert_prism_eval("Prism::CPAWN = 1; Prism::CPAWN &&= 2") + assert_prism_eval("Prism::CPAWN &&= 1") + assert_prism_eval("::CPAWN = 1; ::CPAWN &&= 2") + end + + def test_ConstantPathOrWriteNode + assert_prism_eval("Prism::CPOrWN = nil; Prism::CPOrWN ||= 1") + assert_prism_eval("Prism::CPOrWN ||= 1") + assert_prism_eval("::CPOrWN = nil; ::CPOrWN ||= 1") + end + + def test_ConstantPathOperatorWriteNode + assert_prism_eval("Prism::CPOWN = 0; Prism::CPOWN += 1") + assert_prism_eval("::CPOWN = 0; ::CPOWN += 1") + end + + def test_GlobalVariableAndWriteNode + assert_prism_eval("$pit = 0; $pit &&= 1") + end + + def test_GlobalVariableOperatorWriteNode + assert_prism_eval("$pit = 0; $pit += 1") + end + + def test_GlobalVariableOrWriteNode + assert_prism_eval("$pit ||= 1") + end + + def test_GlobalVariableWriteNode + assert_prism_eval("$pit = 1") + end + + def test_InstanceVariableAndWriteNode + assert_prism_eval("@pit = 0; @pit &&= 1") + end + + def test_InstanceVariableOperatorWriteNode + assert_prism_eval("@pit = 0; @pit += 1") + end + + def test_InstanceVariableOrWriteNode + assert_prism_eval("@pit ||= 1") + end + + def test_InstanceVariableWriteNode + assert_prism_eval("class Prism::TestCompilePrism; @pit = 1; end") + end + + def test_LocalVariableAndWriteNode + assert_prism_eval("pit = 0; pit &&= 1") + end + + def test_LocalVariableOperatorWriteNode + assert_prism_eval("pit = 0; pit += 1") + end + + def test_LocalVariableOrWriteNode + assert_prism_eval("pit ||= 1") + end + + def test_LocalVariableWriteNode + assert_prism_eval("pit = 1") + assert_prism_eval(<<-CODE) + a = 0 + [].each do + a = 1 + end + a + CODE + + assert_prism_eval(<<-CODE) + a = 1 + d = 1 + [1].each do + b = 2 + a = 2 + [2].each do + c = 3 + d = 4 + a = 2 + end + end + [a, d] + CODE + end + + def test_MatchWriteNode + assert_prism_eval("/(?<foo>bar)(?<baz>bar>)/ =~ 'barbar'") + assert_prism_eval("/(?<foo>bar)/ =~ 'barbar'") + end + + ############################################################################ + # Multi-writes # + ############################################################################ + + def test_ClassVariableTargetNode + assert_prism_eval("class Prism::TestCompilePrism; @@pit, @@pit1 = 1; end") + end + + def test_ConstantTargetNode + # We don't call assert_prism_eval directly in this case because we + # don't want to assign the constant multiple times if we run + # with `--repeat-count` + # Instead, we eval manually here, and remove the constant to + constant_names = ["YCT", "YCT2"] + source = "#{constant_names.join(",")} = 1" + prism_eval = RubyVM::InstructionSequence.compile_prism(source).eval + assert_equal prism_eval, 1 + constant_names.map { |name| + Object.send(:remove_const, name) + } + end + + def test_ConstantPathTargetNode + assert_separately([], <<~'RUBY') + verbose = $VERBOSE + # Create some temporary nested constants + Object.send(:const_set, "MyFoo", Object) + Object.const_get("MyFoo").send(:const_set, "Bar", Object) + + constant_names = ["MyBar", "MyFoo::Bar", "MyFoo::Bar::Baz"] + source = "#{constant_names.join(",")} = Object" + iseq = RubyVM::InstructionSequence.compile_prism(source) + $VERBOSE = nil + prism_eval = iseq.eval + $VERBOSE = verbose + assert_equal prism_eval, Object + RUBY + end + + def test_GlobalVariableTargetNode + assert_prism_eval("$pit, $pit1 = 1") + end + + def test_InstanceVariableTargetNode + assert_prism_eval("class Prism::TestCompilePrism; @pit, @pit1 = 1; end") + end + + def test_LocalVariableTargetNode + assert_prism_eval("pit, pit1 = 1") + assert_prism_eval(<<-CODE) + a = 1 + [1].each do + c = 2 + a, b = 2 + end + a + CODE + end + + def test_MultiTargetNode + assert_prism_eval("a, (b, c) = [1, 2, 3]") + assert_prism_eval("a, (b, c) = [1, 2, 3]; a") + assert_prism_eval("a, (b, c) = [1, 2, 3]; b") + assert_prism_eval("a, (b, c) = [1, 2, 3]; c") + assert_prism_eval("a, (b, c) = [1, [2, 3]]; c") + assert_prism_eval("a, (b, *c) = [1, [2, 3]]; c") + assert_prism_eval("a, (b, *c) = 1, [2, 3]; c") + assert_prism_eval("a, (b, *) = 1, [2, 3]; b") + assert_prism_eval("a, (b, *c, d) = 1, [2, 3, 4]; [a, b, c, d]") + assert_prism_eval("(a, (b, c, d, e), f, g), h = [1, [2, 3]], 4, 5, [6, 7]; c") + end + + def test_MultiWriteNode + assert_prism_eval("foo, bar = [1, 2]") + assert_prism_eval("foo, = [1, 2]") + assert_prism_eval("foo, *, bar = [1, 2]") + assert_prism_eval("foo, bar = 1, 2") + assert_prism_eval("foo, *, bar = 1, 2") + assert_prism_eval("foo, *, bar = 1, 2, 3, 4") + assert_prism_eval("a, b, *, d = 1, 2, 3, 4") + assert_prism_eval("a, b, *, d = 1, 2") + assert_prism_eval("(a, b), *, c = [1, 3], 4, 5") + assert_prism_eval("(a, b), *, c = [1, 3], 4, 5; a") + assert_prism_eval("(a, b), *, c = [1, 3], 4, 5; b") + assert_prism_eval("(a, b), *, c = [1, 3], 4, 5; c") + assert_prism_eval("a, *, (c, d) = [1, 3], 4, 5; a") + assert_prism_eval("a, *, (c, d) = [1, 3], 4, 5; c") + assert_prism_eval("(a, b, c), *, (d, e) = [1, 3], 4, 5, [6, 7]") + assert_prism_eval("(a, b, c), *, (d, e) = [1, 3], 4, 5, [6, 7]; b") + assert_prism_eval("(a, b, c), *, (d, e) = [1, 3], 4, 5, [6, 7]; d") + assert_prism_eval("((a, *, b), *, (c, *, (d, *, e, f, g))), *, ((h, i, *, j), *, (k, l, m, *, n, o, p), q, r) = 1; a") + assert_prism_eval("*a = 1; a") + assert_prism_eval("_, {}[:foo] = 1") + assert_prism_eval("_, {}[:foo], _ = 1") + assert_prism_eval("_, {}[:foo], _ = 1") + assert_prism_eval("_,{}[:foo], _, {}[:bar] = 1") + assert_prism_eval("* = :foo") + assert_prism_eval("* = *[]") + assert_prism_eval("a, * = :foo") + + + assert_prism_eval(<<~CODE) + class Foo + def bar=(x); end + def baz=(c); end + end + foo = Foo.new + foo.bar, foo.baz = 1 + CODE + assert_prism_eval(<<~CODE) + class Foo + def bar=(x); end + def baz=(c); end + end + foo = Foo.new + _, foo.bar, foo.baz = 1 + CODE + assert_prism_eval(<<~CODE) + class Foo + def bar=(x); end + def baz=(c); end + end + foo = Foo.new + _, foo.bar, _, foo.baz = 1 + CODE + + # Test nested writes with method calls + assert_prism_eval(<<~RUBY) + class Foo + attr_accessor :bar + end + + a = Foo.new + + (a.bar, a.bar), b = [1], 2 + RUBY + assert_prism_eval(<<~RUBY) + h = {} + (h[:foo], h[:bar]), a = [1], 2 + RUBY + end + + ############################################################################ + # String-likes # + ############################################################################ + + def test_EmbeddedStatementsNode + assert_prism_eval('"foo #{to_s} baz"') + end + + def test_EmbeddedVariableNode + assert_prism_eval('class Prism::TestCompilePrism; @pit = 1; "#@pit"; end') + assert_prism_eval('class Prism::TestCompilePrism; @@pit = 1; "#@@pit"; end') + assert_prism_eval('$pit = 1; "#$pit"') + end + + def test_InterpolatedMatchLastLineNode + assert_prism_eval('$pit = ".oo"; if /"#{$pit}"/mix; end') + end + + def test_InterpolatedRegularExpressionNode + assert_prism_eval('$pit = 1; /1 #$pit 1/') + assert_prism_eval('$pit = 1; /#$pit/i') + assert_prism_eval('/1 #{1 + 2} 1/') + assert_prism_eval('/1 #{"2"} #{1 + 2} 1/') + end + + def test_InterpolatedStringNode + assert_prism_eval('$pit = 1; "1 #$pit 1"') + assert_prism_eval('"1 #{1 + 2} 1"') + assert_prism_eval('"Prism" "::" "TestCompilePrism"') + assert_prism_eval(<<-'RUBY') + # frozen_string_literal: true + + !("a""b""#{1}").frozen? + RUBY + assert_prism_eval(<<-'RUBY') + # frozen_string_literal: true + + !("a""#{1}""b").frozen? + RUBY + + # Test encoding of interpolated strings + assert_prism_eval(<<~'RUBY') + "#{"foo"}s".encoding + RUBY + assert_prism_eval(<<~'RUBY') + a = "foo" + b = "#{a}" << "Bar" + [a, b, b.encoding] + RUBY + end + + def test_concatenated_StringNode + assert_prism_eval('("a""b").frozen?') + assert_prism_eval(<<-CODE) + # frozen_string_literal: true + + ("a""b").frozen? + CODE + end + + def test_InterpolatedSymbolNode + assert_prism_eval('$pit = 1; :"1 #$pit 1"') + assert_prism_eval(':"1 #{1 + 2} 1"') + end + + def test_InterpolatedXStringNode + assert_prism_eval(<<~RUBY) + def self.`(command) = command * 2 + `echo \#{1}` + RUBY + + assert_prism_eval(<<~RUBY) + def self.`(command) = command * 2 + `echo \#{"100"}` + RUBY + end + + def test_MatchLastLineNode + assert_prism_eval("if /foo/; end") + assert_prism_eval("if /foo/i; end") + assert_prism_eval("if /foo/x; end") + assert_prism_eval("if /foo/m; end") + assert_prism_eval("if /foo/im; end") + assert_prism_eval("if /foo/mx; end") + assert_prism_eval("if /foo/xi; end") + assert_prism_eval("if /foo/ixm; end") + end + + def test_RegularExpressionNode + assert_prism_eval('/pit/') + assert_prism_eval('/pit/i') + assert_prism_eval('/pit/x') + assert_prism_eval('/pit/m') + assert_prism_eval('/pit/im') + assert_prism_eval('/pit/mx') + assert_prism_eval('/pit/xi') + assert_prism_eval('/pit/ixm') + + assert_prism_eval('/pit/u') + assert_prism_eval('/pit/e') + assert_prism_eval('/pit/s') + assert_prism_eval('/pit/n') + + assert_prism_eval('/pit/me') + assert_prism_eval('/pit/ne') + + assert_prism_eval('2.times.map { /#{1}/o }') + assert_prism_eval('2.times.map { foo = 1; /#{foo}/o }') + end + + def test_StringNode + assert_prism_eval('"pit"') + assert_prism_eval('"a".frozen?') + end + + def test_StringNode_frozen_string_literal_true + [ + # Test that string literal is frozen + <<~RUBY, + # frozen_string_literal: true + "a".frozen? + RUBY + # Test that two string literals with the same contents are the same string + <<~RUBY, + # frozen_string_literal: true + "hello".equal?("hello") + RUBY + ].each do |src| + assert_prism_eval(src, raw: true) + end + end + + def test_StringNode_frozen_string_literal_false + [ + # Test that string literal is frozen + <<~RUBY, + # frozen_string_literal: false + !"a".frozen? + RUBY + # Test that two string literals with the same contents are the same string + <<~RUBY, + # frozen_string_literal: false + !"hello".equal?("hello") + RUBY + ].each do |src| + assert_prism_eval(src, raw: true) + end + end + + def test_StringNode_frozen_string_literal_default + # Test that string literal is chilled + assert_prism_eval('"a".frozen?') + + # Test that two identical chilled string literals aren't the same object + assert_prism_eval('!"hello".equal?("hello")') + end + + def test_SymbolNode + assert_prism_eval(":pit") + + # Test UTF-8 symbol in a US-ASCII file + assert_prism_eval(<<~'RUBY', raw: true) + # -*- coding: us-ascii -*- + :"\u{e9}" + RUBY + + # Test ASCII-8BIT symbol in a US-ASCII file + assert_prism_eval(<<~'RUBY', raw: true) + # -*- coding: us-ascii -*- + :"\xff" + RUBY + + # Test US-ASCII symbol in a ASCII-8BIT file + assert_prism_eval(<<~'RUBY', raw: true) + # -*- coding: ascii-8bit -*- + :a + RUBY + end + + def test_XStringNode + assert_prism_eval(<<~RUBY) + class Prism::TestCompilePrism + def self.`(command) = command * 2 + `pit` + end + RUBY + end + + ############################################################################ + # Structures # + ############################################################################ + + def test_ArrayNode + assert_prism_eval("[]") + assert_prism_eval("[1, 2, 3]") + assert_prism_eval("%i[foo bar baz]") + assert_prism_eval("%w[foo bar baz]") + assert_prism_eval("[*1..2]") + assert_prism_eval("[*1..2, 3, 4, *5..6, 7, 8]") + assert_prism_eval("[*1..2, 3, 4, *5..6, 7, 8, *9..11]") + assert_prism_eval("[0, *1..2, 3, 4, *5..6, 7, 8, *9..11]") + assert_prism_eval("[-1, true, 0, *1..2, 3, 4, *5..6, 7, 8, *9..11]") + assert_prism_eval("a = [1,2]; [0, *a, 3, 4, *5..6, 7, 8, *9..11]") + assert_prism_eval("[[*1..2], 3, *4..5]") + + # Test keyword splat inside of array + assert_prism_eval("[**{x: 'hello'}]") + + # Test UTF-8 string array literal in a US-ASCII file + assert_prism_eval(<<~'RUBY', raw: true) + # -*- coding: us-ascii -*- + # frozen_string_literal: true + %W"\u{1f44b} \u{1f409}" + RUBY + end + + def test_AssocNode + assert_prism_eval("{ foo: :bar }") + end + + def test_AssocSplatNode + assert_prism_eval("foo = { a: 1 }; { **foo }") + assert_prism_eval("foo = { a: 1 }; bar = foo; { **foo, b: 2, **bar, c: 3 }") + assert_prism_eval("foo = { a: 1 }; { b: 2, **foo, c: 3}") + + # Test anonymous AssocSplatNode + assert_prism_eval(<<~RUBY) + o = Object.new + def o.bar(**) = Hash(**) + + o.bar(hello: "world") + RUBY + + # Test that AssocSplatNode is evaluated before BlockArgumentNode using + # the splatkw instruction + assert_prism_eval(<<~RUBY) + o = Struct.new(:ary) do + def to_hash + ary << :to_hash + {} + end + + def to_proc + ary << :to_proc + -> {} + end + + def t(...); end + end.new + o.ary = [] + + o.t(**o, &o) + o.ary + RUBY + end + + def test_HashNode + assert_prism_eval("{}") + assert_prism_eval("{ a: :a }") + assert_prism_eval("{ a: :a, b: :b }") + assert_prism_eval("a = 1; { a: a }") + assert_prism_eval("a = 1; { a: }") + assert_prism_eval("{ to_s: }") + assert_prism_eval("{ Prism: }") + assert_prism_eval("[ Prism: [:b, :c]]") + assert_prism_eval("{ [] => 1}") + end + + def test_ImplicitNode + assert_prism_eval("{ to_s: }") + end + + def test_RangeNode + assert_prism_eval("1..2") + assert_prism_eval("1...2") + assert_prism_eval("..2") + assert_prism_eval("...2") + assert_prism_eval("1..") + assert_prism_eval("1...") + end + + def test_SplatNode + assert_prism_eval("*b = []; b") + assert_prism_eval("*b = [1, 2, 3]; b") + assert_prism_eval("a, *b = [1, 2, 3]; a") + assert_prism_eval("a, *b = [1, 2, 3]; b") + assert_prism_eval("a, *b, c = [1, 2, 3]; a") + assert_prism_eval("a, *b, c = [1, 2, 3]; b") + assert_prism_eval("a, *b, c = [1, 2, 3]; c") + assert_prism_eval("*b, c = [1, 2, 3]; b") + assert_prism_eval("*b, c = [1, 2, 3]; c") + assert_prism_eval("a, *, c = [1, 2, 3]; a") + assert_prism_eval("a, *, c = [1, 2, 3]; c") + + # Test anonymous splat node + assert_prism_eval(<<~RUBY) + def self.bar(*) = Array(*) + + bar([1, 2, 3]) + RUBY + end + + ############################################################################ + # Jumps # + ############################################################################ + + def test_AndNode + assert_prism_eval("true && 1") + assert_prism_eval("false && 1") + end + + def test_CaseNode + assert_prism_eval("case :a; when :a; 1; else; 2; end") + assert_prism_eval("case :a; when :b; 1; else; 2; end") + assert_prism_eval("case :a; when :a; 1; else; 2; end") + assert_prism_eval("case :a; when :a; end") + assert_prism_eval("case :a; when :b, :c; end") + assert_prism_eval("case; when :a; end") + assert_prism_eval("case; when :a, :b; 1; else; 2 end") + assert_prism_eval("case :a; when :b; else; end") + assert_prism_eval("b = 1; case :a; when b; else; end") + assert_prism_eval(<<-CODE) + def self.prism_test_case_node + case :a + when :b + else + return 2 + end + 1 + end + prism_test_case_node + CODE + + # Test splat in when + assert_prism_eval(<<~RUBY) + ary = [1, 2] + case 1 + when *ary + :ok + else + :ng + end + RUBY + + # Test splat in when + assert_prism_eval(<<~RUBY) + ary = [1, 2] + case 1 + when :foo, *ary + :ok + else + :ng + end + RUBY + + # Test case without predicate + assert_prism_eval(<<~RUBY) + case + when 1 == 2 + :ng + else + :ok + end + RUBY + + # test splat with no predicate + assert_prism_eval(<<~RUBY) + case + when *[true] + :ok + else + :ng + end + RUBY + end + + def test_ElseNode + assert_prism_eval("if false; 0; else; 1; end") + assert_prism_eval("if true; 0; else; 1; end") + assert_prism_eval("true ? 1 : 0") + assert_prism_eval("false ? 0 : 1") + end + + def test_FlipFlopNode + assert_prism_eval("not (1 == 1) .. (2 == 2)") + assert_prism_eval("not (1 == 1) ... (2 == 2)") + end + + def test_IfNode + assert_prism_eval("if true; 1; end") + assert_prism_eval("1 if true") + assert_prism_eval('a = b = 1; if a..b; end') + assert_prism_eval('if "a".."b"; end') + assert_prism_eval('if "a"..; end') + assert_prism_eval('if .."b"; end') + assert_prism_eval('if ..1; end') + assert_prism_eval('if 1..; end') + assert_prism_eval('if 1..2; end') + assert_prism_eval('if true or true; end'); + end + + def test_OrNode + assert_prism_eval("true || 1") + assert_prism_eval("false || 1") + end + + def test_UnlessNode + assert_prism_eval("1 unless true") + assert_prism_eval("1 unless false") + assert_prism_eval("unless true; 1; end") + assert_prism_eval("unless false; 1; end") + end + + def test_UntilNode + assert_prism_eval("a = 0; until a == 1; a = a + 1; end") + + # Test UntilNode in rescue + assert_prism_eval(<<~RUBY) + o = Object.new + o.instance_variable_set(:@ret, []) + def o.foo = @ret << @ret.length + def o.bar = @ret.length > 3 + begin + raise + rescue + o.foo until o.bar + end + o.instance_variable_get(:@ret) + RUBY + end + + def test_WhileNode + assert_prism_eval("a = 0; while a != 1; a = a + 1; end") + + # Test WhileNode in rescue + assert_prism_eval(<<~RUBY) + o = Object.new + o.instance_variable_set(:@ret, []) + def o.foo = @ret << @ret.length + def o.bar = @ret.length < 3 + begin + raise + rescue + o.foo while o.bar + end + o.instance_variable_get(:@ret) + RUBY + end + + def test_ForNode + assert_prism_eval("for i in [1,2] do; i; end") + assert_prism_eval("for @i in [1,2] do; @i; end") + assert_prism_eval("for $i in [1,2] do; $i; end") + + assert_prism_eval("for foo, in [1,2,3] do end") + + assert_prism_eval("for i, j in {a: 'b'} do; i; j; end") + end + + ############################################################################ + # Throws # + ############################################################################ + + def test_BeginNode + assert_prism_eval("begin; 1; end") + assert_prism_eval("begin; end; 1") + end + + def test_BreakNode + assert_prism_eval("while true; break; end") + assert_prism_eval("while true; break 1; end") + assert_prism_eval("while true; break 1, 2; end") + + assert_prism_eval("[].each { break }") + assert_prism_eval("[true].map { break }") + end + + def test_ensure_in_methods + assert_prism_eval(<<-CODE) +def self.m + a = [] +ensure + a << 5 + return a +end +m + CODE + end + + def test_break_runs_ensure + assert_prism_eval(<<-CODE) +a = [] +while true + begin + break + ensure + a << 1 + end +end +a + CODE + end + + def test_EnsureNode + assert_prism_eval("begin; 1; ensure; 2; end") + assert_prism_eval("begin; 1; begin; 3; ensure; 4; end; ensure; 2; end") + assert_prism_eval(<<-CODE) + begin + a = 2 + ensure + end + CODE + assert_prism_eval(<<-CODE) + begin + a = 2 + ensure + a = 3 + end + a + CODE + + # Test that ensure block only evaluated once + assert_prism_eval(<<~RUBY) + res = [] + begin + begin + raise + ensure + res << $!.to_s + end + rescue + res + end + RUBY + + assert_prism_eval(<<-CODE) + a = 1 + begin + a = 2 + ensure + a = 3 + end + a + CODE + assert_prism_eval(<<-CODE) + a = 1 + begin + b = 2 + ensure + c = 3 + end + a + b + c + CODE + assert_prism_eval(<<~CODE) + foo = 1 + begin + ensure + begin + ensure + foo.nil? + end + end + CODE + assert_prism_eval(<<~CODE) + def test + ensure + {}.each do |key, value| + {}[key] = value + end + end + CODE + assert_prism_eval(<<~CODE) + def test + a = 1 + ensure + {}.each do |key, value| + {}[key] = a + end + end + CODE + assert_prism_eval(<<-CODE) + def self.prism_test_ensure_node + begin + ensure + end + return + end + prism_test_ensure_node + CODE + + # Test empty ensure block + assert_prism_eval(<<~RUBY) + res = [] + + begin + begin + raise + ensure + end + rescue + res << "rescue" + end + + res + RUBY + end + + def test_NextNode + assert_prism_eval("2.times do |i|; next if i == 1; end") + + assert_prism_eval(<<-CODE) + res = [] + i = 0 + while i < 5 + i += 1 + next if i == 3 + res << i + end + res + CODE + + assert_prism_eval(<<-CODE) + res = [] + (1..5).each do |i| + next if i.even? + res << i + end + res + CODE + + assert_prism_eval(<<-CODE) + (1..5).map do |i| + next i, :even if i.even? + i + end + CODE + + assert_prism_eval(<<-CODE) + res = [] + i = 0 + begin + i += 1 + next if i == 3 + res << i + end while i < 5 + res + CODE + + assert_prism_eval(<<-CODE) + while false + begin + ensure + end + next + end + CODE + + assert_prism_eval(<<~CODE) + [].each do + begin + rescue + next + end + end + CODE + end + + def test_RedoNode + assert_prism_eval(<<-CODE) + counter = 0 + + 5.times do |i| + counter += 1 + if i == 2 && counter < 3 + redo + end + end + CODE + + assert_prism_eval(<<-CODE) + for i in 1..5 + if i == 3 + i = 0 + redo + end + end + CODE + + assert_prism_eval(<<-CODE) + i = 0 + begin + i += 1 + redo if i == 3 + end while i < 5 + CODE + end + + def test_RescueNode + assert_prism_eval("begin; 1; rescue; 2; end") + assert_prism_eval(<<~CODE) + begin + 1 + rescue SyntaxError + 2 + end + CODE + assert_prism_eval(<<~CODE) + begin + 1 + raise 'boom' + rescue StandardError + 2 + end + CODE + assert_prism_eval(<<~CODE) + begin + a = 1 + rescue StandardError => e + end + CODE + assert_prism_eval(<<~CODE) + begin + raise StandardError + rescue StandardError => e + end + CODE + assert_prism_eval(<<~CODE) + begin + 1 + rescue StandardError => e + e + rescue SyntaxError => f + f + else + 4 + end + CODE + assert_prism_eval(<<-CODE) + begin + a = 2 + rescue + a = 3 + end + a + CODE + assert_prism_eval(<<-CODE) + a = 1 + begin + a = 2 + rescue + a = 3 + end + a + CODE + assert_prism_eval(<<-CODE) + a = 1 + begin + b = 2 + raise "bang" + rescue + c = 3 + end + a + b + c + CODE + assert_prism_eval("begin; rescue; end") + + assert_prism_eval(<<~CODE) + begin + rescue + args.each do |key, value| + tmp[key] = 1 + end + end + CODE + assert_prism_eval(<<~CODE) + 10.times do + begin + rescue + break + end + end + CODE + + # Test RescueNode with ElseNode + assert_prism_eval(<<~RUBY) + calls = [] + begin + begin + rescue RuntimeError + calls << 1 + else + calls << 2 + raise RuntimeError + end + rescue RuntimeError + end + + calls + RUBY + end + + def test_RescueModifierNode + assert_prism_eval("1.nil? rescue false") + assert_prism_eval("1.nil? rescue 1") + assert_prism_eval("raise 'bang' rescue nil") + assert_prism_eval("raise 'bang' rescue a = 1; a.nil?") + assert_prism_eval("a = 0 rescue (a += 1 && retry if a <= 1)") + end + + def test_RetryNode + assert_prism_eval(<<~CODE) + a = 1 + begin + a + raise "boom" + rescue + a += 1 + retry unless a > 1 + ensure + a = 3 + end + CODE + + assert_prism_eval(<<~CODE) + begin + rescue + foo = 2 + retry + end + CODE + + assert_prism_eval(<<~CODE) + begin + a = 2 + rescue + retry + end + CODE + end + + def test_ReturnNode + assert_prism_eval(<<-CODE) + def self.prism_test_return_node + return 1 + end + prism_test_return_node + CODE + + assert_prism_eval(<<-CODE) + def self.prism_test_return_node + return 1, 2 + end + prism_test_return_node + CODE + + assert_prism_eval(<<-CODE) + def self.prism_test_return_node + [1].each do |e| + return true + end + end + prism_test_return_node + CODE + + assert_prism_eval(<<-CODE) + def self.prism_test_return_node + [1].map do |i| + return i if i == 1 + 2 + end + end + prism_test_return_node + CODE + + assert_prism_eval(<<-CODE) + def self.prism_test_return_node(*args, **kwargs) + return *args, *args, **kwargs + end + prism_test_return_node(1, foo: 0) + CODE + end + + ############################################################################ + # Scopes/statements # + ############################################################################ + + def test_BlockNode + assert_prism_eval("[1, 2, 3].each { |num| num }") + + assert_prism_eval("[].tap { _1 }") + + assert_prism_eval("[].each { |a,| }") + assert_prism_eval("[[1, 2, 3]].map { |_, _, a| a }") + assert_prism_eval("[[1, 2, 3]].map { |_, a| a }") + + assert_prism_eval("[[]].map { |a| a }") + assert_prism_eval("[[]].map { |a| a }") + assert_prism_eval("[[]].map { |a, &block| a }") + assert_prism_eval("[[]].map { |a, &block| a }") + assert_prism_eval("[{}].map { |a,| }") + assert_prism_eval("[[]].map { |a,b=1| a }") + assert_prism_eval("[{}].map { |a,| }") + assert_prism_eval("[{}].map { |a| a }") + + # Test blocks with MultiTargetNode + assert_prism_eval("[[1, 2]].each.map { |(a), (b)| [a, b] }") + end + + def test_ClassNode + assert_prism_eval("class PrismClassA; end") + assert_prism_eval("class PrismClassA; end; class PrismClassB < PrismClassA; end") + assert_prism_eval("class PrismClassA; end; class PrismClassA::PrismClassC; end") + assert_prism_eval(<<-HERE + class PrismClassA; end + class PrismClassA::PrismClassC; end + class PrismClassB; end + class PrismClassB::PrismClassD < PrismClassA::PrismClassC; end + HERE + ) + end + + # Many of these tests are versions of tests at bootstraptest/test_method.rb + def test_DefNode + assert_prism_eval("def prism_test_def_node; end") + assert_prism_eval("a = Object.new; def a.prism_singleton; :ok; end; a.prism_singleton") + assert_prism_eval("def self.prism_test_def_node() 1 end; prism_test_def_node()") + assert_prism_eval("def self.prism_test_def_node(a,b) [a, b] end; prism_test_def_node(1,2)") + assert_prism_eval("def self.prism_test_def_node(a,x=7,y=1) x end; prism_test_def_node(7,1)") + assert_prism_eval("def self.prism_test_def_node(a = 1); x = 2; end; prism_test_def_node") + + # rest argument + assert_prism_eval("def self.prism_test_def_node(*a) a end; prism_test_def_node().inspect") + assert_prism_eval("def self.prism_test_def_node(*a) a end; prism_test_def_node(1).inspect") + assert_prism_eval("def self.prism_test_def_node(x,y,*a) a end; prism_test_def_node(7,7,1,2).inspect") + assert_prism_eval("def self.prism_test_def_node(x,y=7,*a) a end; prism_test_def_node(7).inspect") + assert_prism_eval("def self.prism_test_def_node(x,y,z=7,*a) a end; prism_test_def_node(7,7).inspect") + assert_prism_eval("def self.prism_test_def_node(x,y,z=7,zz=7,*a) a end; prism_test_def_node(7,7,7).inspect") + + # keyword arguments + assert_prism_eval("def self.prism_test_def_node(a: 1, b: 2, c: 4) a + b + c; end; prism_test_def_node(a: 2)") + assert_prism_eval("def self.prism_test_def_node(a: 1, b: 2, c: 4) a + b + c; end; prism_test_def_node(b: 3)") + assert_prism_eval(<<-CODE) + def self.prism_test_def_node(x = 1, y, a: 8, b: 2, c: 4) + a + b + c + x + y + end + prism_test_def_node(10, b: 3) + CODE + assert_prism_eval(<<-CODE) + def self.prism_test_def_node(a: []) + a + end + prism_test_def_node + CODE + + # block arguments + assert_prism_eval("def self.prism_test_def_node(&block) block end; prism_test_def_node{}.class") + assert_prism_eval("def self.prism_test_def_node(&block) block end; prism_test_def_node().inspect") + assert_prism_eval("def self.prism_test_def_node(a,b=7,*c,&block) b end; prism_test_def_node(7,1).inspect") + assert_prism_eval("def self.prism_test_def_node(a,b=7,*c,&block) c end; prism_test_def_node(7,7,1).inspect") + + # splat + assert_prism_eval("def self.prism_test_def_node(a) a end; prism_test_def_node(*[1])") + assert_prism_eval("def self.prism_test_def_node(x,a) a end; prism_test_def_node(7,*[1])") + assert_prism_eval("def self.prism_test_def_node(x,y,a) a end; prism_test_def_node(7,7,*[1])") + assert_prism_eval("def self.prism_test_def_node(x,y,a,b,c) a end; prism_test_def_node(7,7,*[1,7,7])") + + # recursive call + assert_prism_eval("def self.prism_test_def_node(n) n == 0 ? 1 : prism_test_def_node(n-1) end; prism_test_def_node(5)") + + # instance method + assert_prism_eval("class PrismTestDefNode; def prism_test_def_node() 1 end end; PrismTestDefNode.new.prism_test_def_node") + assert_prism_eval("class PrismTestDefNode; def prism_test_def_node(*a) a end end; PrismTestDefNode.new.prism_test_def_node(1).inspect") + + # block argument + assert_prism_eval(<<-CODE) + def self.prism_test_def_node(&block) prism_test_def_node2(&block) end + def self.prism_test_def_node2() yield 1 end + prism_test_def_node2 {|a| a } + CODE + + # multi argument + assert_prism_eval(<<-CODE) + def self.prism_test_def_node(a, (b, *c, d)) + [a, b, c, d] + end + prism_test_def_node("a", ["b", "c", "d"]) + CODE + assert_prism_eval(<<-CODE) + def self.prism_test_def_node(a, (b, c, *)) + [a, b, c] + end + prism_test_def_node("a", ["b", "c"]) + CODE + assert_prism_eval(<<-CODE) + def self.prism_test_def_node(a, (*, b, c)) + [a, b, c] + end + prism_test_def_node("a", ["b", "c"]) + CODE + + # recursive multis + assert_prism_eval(<<-CODE) + def self.prism_test_def_node(a, (b, *c, (d, *e, f))) + [a, b, c, d, d, e, f] + end + prism_test_def_node("a", ["b", "c", ["d", "e", "f"]]) + CODE + + # Many arguments + assert_prism_eval(<<-CODE) + def self.prism_test_def_node(a, (b, *c, d), e = 1, *f, g, (h, *i, j), k:, l: 1, **m) + [a, b, c, d, e, f, g, h, i, j, k, l, m] + end + prism_test_def_node( + "a", + ["b", "c1", "c2", "d"], + "e", + "f1", "f2", + "g", + ["h", "i1", "i2", "j"], + k: "k", + l: "l", + m1: "m1", + m2: "m2" + ) + CODE + end + + def test_pow_parameters + assert_prism_eval("def self.m(a, **); end; method(:m).parameters") + end + + def test_star_parameters + assert_prism_eval("def self.m(a, *, b); end; method(:m).parameters") + end + + def test_repeated_block_params + assert_prism_eval("def self.x(&blk); blk; end; x { |_, _, _ = 1, *_, _:, _: 2, **_, &_| }.parameters") + end + + def test_repeated_proc_params + assert_prism_eval("proc {|_, _, _ = 1, *_, _:, _: 2, **_, &_| }.parameters") + end + + def test_forward_parameters_block + assert_prism_eval("def self.m(&); end; method(:m).parameters") + end + + def test_forward_parameters + assert_prism_eval("def self.m(...); end; method(:m).parameters") + end + + def test_repeated_block_underscore + assert_prism_eval("def self.m(_, **_, &_); _; end; method(:m).parameters") + end + + def test_repeated_kw_rest_underscore + assert_prism_eval("def self.m(_, **_); _; end; method(:m).parameters") + end + + def test_repeated_required_keyword_underscore + assert_prism_eval("def self.m(_, _, *_, _, _:); _; end; method(:m).parameters") + assert_prism_eval("def self.m(_, _, *_, _, _:, _: 2); _; end; method(:m).parameters") + end + + def test_repeated_required_post_underscore + assert_prism_eval("def self.m(_, _, *_, _); _; end; method(:m).parameters") + end + + def test_repeated_splat_underscore + assert_prism_eval("def self.m(_, _, _ = 1, _ = 2, *_); end; method(:m).parameters") + end + + def test_repeated_optional_underscore + assert_prism_eval("def self.m(a, _, _, _ = 1, _ = 2, b); end; method(:m).parameters") + end + + def test_repeated_required_underscore + assert_prism_eval("def self.m(a, _, _, b); end; method(:m).parameters") + end + + def test_locals_in_parameters + assert_prism_eval("def self.m(a = b = c = 1); [a, b, c]; end; self.m") + end + + def test_trailing_comma_on_block + assert_prism_eval("def self.m; yield [:ok]; end; m {|v0,| v0 }") + end + + def test_complex_default_params + assert_prism_eval("def self.foo(a:, b: '2'.to_i); [a, b]; end; foo(a: 1)") + assert_prism_eval("def self.foo(a:, b: 2, c: '3'.to_i); [a, b, c]; end; foo(a: 1)") + end + + def test_numbered_params + assert_prism_eval("[1, 2, 3].then { _3 }") + assert_prism_eval("1.then { one = 1; one + _1 }") + end + + def test_rescue_with_ensure + assert_prism_eval(<<-CODE) +begin + begin + raise "a" + rescue + raise "b" + ensure + raise "c" + end +rescue => e + e.message +end + CODE + end + + def test_required_kwarg_ordering + assert_prism_eval("def self.foo(a: 1, b:); [a, b]; end; foo(b: 2)") + end + + def test_trailing_keyword_method_params + # foo(1, b: 2, c: 3) # argc -> 3 + assert_prism_eval("def self.foo(a, b:, c:); [a, b, c]; end; foo(1, b: 2, c: 3)") + end + + def test_keyword_method_params_only + # foo(a: 1, b: 2) # argc -> 2 + assert_prism_eval("def self.foo(a:, b:); [a, b]; end; foo(a: 1, b: 2)") + end + + def test_keyword_method_params_with_splat + # foo(a: 1, **b) # argc -> 1 + assert_prism_eval("def self.foo(a:, b:); [a, b]; end; b = { b: 2 }; foo(a: 1, **b)") + end + + def test_positional_and_splat_keyword_method_params + # foo(a, **b) # argc -> 2 + assert_prism_eval("def self.foo(a, b); [a, b]; end; b = { b: 2 }; foo(1, **b)") + end + + def test_positional_and_splat_method_params + # foo(a, *b, c, *d, e) # argc -> 2 + assert_prism_eval("def self.foo(a, b, c, d, e); [a, b, c, d, e]; end; b = [2]; d = [4]; foo(1, *b, 3, *d, 5)") + end + + def test_positional_with_splat_and_splat_keyword_method_params + # foo(a, *b, c, *d, **e) # argc -> 3 + assert_prism_eval("def self.foo(a, b, c, d, e); [a, b, c, d, e]; end; b = [2]; d = [4]; e = { e: 5 }; foo(1, *b, 3, *d, **e)") + end + + def test_positional_with_splat_and_keyword_method_params + # foo(a, *b, c, *d, e:) # argc -> 3 + assert_prism_eval("def self.foo(a, b, c, d, e:); [a, b, c, d, e]; end; b = [2]; d = [4]; foo(1, *b, 3, *d, e: 5)") + end + + def test_leading_splat_and_keyword_method_params + # foo(*a, b:) # argc -> 2 + assert_prism_eval("def self.foo(a, b:); [a, b]; end; a = [1]; foo(*a, b: 2)") + end + + def test_repeated_method_params + assert_prism_eval("def self.foo(_a, _a); _a; end; foo(1, 2)") + end + + def test_splat_params_with_no_lefties + assert_prism_eval("def self.foo(v, (*)); v; end; foo(1, [2, 3, 4])") + end + + def test_method_parameters + assert_prism_eval(<<-CODE) + def self.prism_test_method_parameters(a, b=1, *c, d:, e: 2, **f, &g) + end + + method(:prism_test_method_parameters).parameters + CODE + + assert_prism_eval(<<-CODE) + def self.prism_test_method_parameters(d:, e: 2, **f, &g) + end + + method(:prism_test_method_parameters).parameters + CODE + + assert_prism_eval(<<-CODE) + def self.prism_test_method_parameters(**f, &g) + end + + method(:prism_test_method_parameters).parameters + CODE + + assert_prism_eval(<<-CODE) + def self.prism_test_method_parameters(&g) + end + + method(:prism_test_method_parameters).parameters + CODE + end + + def test_LambdaNode + assert_prism_eval("-> { to_s }.call") + end + + def test_LambdaNode_with_multiline_args + assert_prism_eval(<<-CODE) + -> (a, + b) { + a + b + }.call(1, 2) + CODE + end + + def test_ModuleNode + assert_prism_eval("module M; end") + assert_prism_eval("module M::N; end") + assert_prism_eval("module ::O; end") + end + + def test_ParenthesesNode + assert_prism_eval("()") + assert_prism_eval("(1)") + end + + def test_PreExecutionNode + assert_prism_eval("BEGIN { a = 1 }; 2", raw: true) + assert_prism_eval("b = 2; BEGIN { a = 1 }; a + b", raw: true) + end + + def test_PostExecutionNode + assert_prism_eval("END { 1 }") + assert_prism_eval("END { @b }; @b = 1") + assert_prism_eval("END { @b; 0 }; @b = 1") + assert_prism_eval("foo = 1; END { foo.nil? }") + assert_prism_eval("foo = 1; END { END { foo.nil? }}") + end + + def test_ProgramNode + assert_prism_eval("") + assert_prism_eval("1") + end + + def test_SingletonClassNode + assert_prism_eval("class << self; end") + end + + def test_StatementsNode + assert_prism_eval("1") + end + + def test_YieldNode + assert_prism_eval("def prism_test_yield_node; yield; end") + assert_prism_eval("def prism_test_yield_node; yield 1, 2; end") + assert_prism_eval("def prism_test_yield_node; yield **kw if condition; end") + + # Test case where there's a call directly after the yield call + assert_prism_eval("def prism_test_yield_node; yield; 1; end") + assert_prism_eval("def prism_test_yield_node; yield 1, 2; 1; end") + end + + ############################################################################ + # Calls / arguments # + ############################################################################ + + def test_ArgumentsNode + # assert_prism_eval("[].push 1") + end + + def test_BlockArgumentNode + assert_prism_eval("1.then(&:to_s)") + + # Test anonymous block forwarding + assert_prism_eval(<<~RUBY) + o = Object.new + def o.foo(&) = yield + def o.bar(&) = foo(&) + + o.bar { :ok } + RUBY + end + + def test_BlockLocalVariableNode + assert_prism_eval(<<-CODE + pm_var = "outer scope variable" + + 1.times { |;pm_var| pm_var = "inner scope variable"; pm_var } + CODE + ) + + assert_prism_eval(<<-CODE + pm_var = "outer scope variable" + + 1.times { |;pm_var| pm_var = "inner scope variable"; pm_var } + pm_var + CODE + ) + end + + def test_CallNode + assert_prism_eval("to_s") + + # with arguments + assert_prism_eval("eval '1'") + + # with arguments and popped + assert_prism_eval("eval '1'; 1") + + # With different types of calling arguments + assert_prism_eval(<<-CODE) + def self.prism_test_call_node_double_splat(**); end + prism_test_call_node_double_splat(b: 1, **{}) + CODE + assert_prism_eval(<<-CODE) + prism_test_call_node_double_splat(:b => 1) + CODE + + assert_prism_eval(<<-CODE) + def self.prism_test_call_node_splat(*); end + prism_test_call_node_splat(*[], 1) + CODE + + assert_prism_eval("prism_test_call_node_splat(*[], 1, 2)") + + assert_prism_eval(<<~RUBY) + def self.prism_test_call_node_splat_and_double_splat(a, b, **opts); end + prism_test_call_node_splat_and_double_splat(*[1], 2, **{}) + RUBY + + assert_prism_eval(<<-CODE) + class Foo + def []=(a, b) + 1234 + end + end + + def self.foo(i, j) + tbl = Foo.new + tbl[i] = j + end + foo(1, 2) + CODE + + assert_prism_eval(<<-CODE) + class Foo + def i=(a) + 1234 + end + end + + def self.foo(j) + tbl = Foo.new + tbl.i = j + end + foo(1) + CODE + + assert_prism_eval(<<-CODE) + foo = Object.new + def foo.[]=(k,v); 42; end + foo.[]=(1,2) + CODE + + # With splat inside of []= + assert_prism_eval(<<~RUBY) + obj = Object.new + def obj.[]=(a, b); 10; end + obj[*[1]] = 3 + RUBY + + assert_prism_eval(<<-CODE) + def self.prism_opt_var_trail_hash(a = nil, *b, c, **d); end + prism_opt_var_trail_hash("a") + prism_opt_var_trail_hash("a", c: 1) + prism_opt_var_trail_hash("a", "b") + prism_opt_var_trail_hash("a", "b", "c") + prism_opt_var_trail_hash("a", "b", "c", c: 1) + prism_opt_var_trail_hash("a", "b", "c", "c" => 0, c: 1) + CODE + + assert_prism_eval(<<-CODE) + def self.foo(*args, **kwargs) = [args, kwargs] + + [ + foo(2 => 3), + foo([] => 42), + foo(a: 42, b: 61), + foo(1, 2, 3, a: 42, "b" => 61), + foo(:a => 42, :b => 61), + ] + CODE + + assert_prism_eval(<<-CODE) + class PrivateMethod + def initialize + self.instance_var + end + private + attr_accessor :instance_var + end + pm = PrivateMethod.new + pm.send(:instance_var) + CODE + + # Testing safe navigation operator + assert_prism_eval(<<-CODE) + def self.test_prism_call_node + if [][0]&.first + 1 + end + end + test_prism_call_node + CODE + + # Test opt_str_freeze instruction when calling #freeze on a string literal + assert_prism_eval(<<~RUBY) + "foo".freeze.equal?("foo".freeze) + RUBY + # Test encoding in opt_str_freeze + assert_prism_eval(<<~'RUBY', raw: true) + # -*- coding: us-ascii -*- + "\xff".freeze.encoding + RUBY + + # Test opt_aref_with instruction when calling [] with a string + assert_prism_eval(<<~RUBY) + ObjectSpace.count_objects + + h = {"abc" => 1} + before = ObjectSpace.count_objects[:T_STRING] + 5.times{ h["abc"] } + after = ObjectSpace.count_objects[:T_STRING] + + before == after + RUBY + + # Test opt_aset_with instruction when calling []= with a string key + assert_prism_eval(<<~RUBY) + ObjectSpace.count_objects + + h = {"abc" => 1} + before = ObjectSpace.count_objects[:T_STRING] + 5.times{ h["abc"] = 2} + after = ObjectSpace.count_objects[:T_STRING] + + before == after + RUBY + end + + def test_CallAndWriteNode + assert_prism_eval(<<-CODE + class PrismTestSubclass; end + def PrismTestSubclass.test_call_and_write_node; end; + PrismTestSubclass.test_call_and_write_node &&= 1 + CODE + ) + + assert_prism_eval(<<-CODE + def PrismTestSubclass.test_call_and_write_node + "str" + end + def PrismTestSubclass.test_call_and_write_node=(val) + val + end + PrismTestSubclass.test_call_and_write_node &&= 1 + CODE + ) + + assert_prism_eval(<<-CODE + def self.test_call_and_write_node; end; + self.test_call_and_write_node &&= 1 + CODE + ) + + assert_prism_eval(<<-CODE + def self.test_call_and_write_node + "str" + end + def self.test_call_and_write_node=(val) + val + end + self.test_call_and_write_node &&= 1 + CODE + ) + + assert_prism_eval(<<-CODE) + def self.test_prism_call_node; end + def self.test_prism_call_node=(val) + val + end + self&.test_prism_call_node &&= 1 + CODE + + assert_prism_eval(<<-CODE) + def self.test_prism_call_node + 2 + end + def self.test_prism_call_node=(val) + val + end + self&.test_prism_call_node &&= 1 + CODE + end + + def test_CallOrWriteNode + assert_prism_eval(<<-CODE + class PrismTestSubclass; end + def PrismTestSubclass.test_call_or_write_node; end; + def PrismTestSubclass.test_call_or_write_node=(val) + val + end + PrismTestSubclass.test_call_or_write_node ||= 1 + CODE + ) + + assert_prism_eval(<<-CODE + def PrismTestSubclass.test_call_or_write_node + "str" + end + PrismTestSubclass.test_call_or_write_node ||= 1 + CODE + ) + + assert_prism_eval(<<-CODE + def self.test_call_or_write_node; end; + def self.test_call_or_write_node=(val) + val + end + self.test_call_or_write_node ||= 1 + CODE + ) + + assert_prism_eval(<<-CODE + def self.test_call_or_write_node + "str" + end + self.test_call_or_write_node ||= 1 + CODE + ) + + assert_prism_eval(<<-CODE) + def self.test_prism_call_node + 2 + end + def self.test_prism_call_node=(val) + val + end + self&.test_prism_call_node ||= 1 + CODE + + assert_prism_eval(<<-CODE) + def self.test_prism_call_node; end + def self.test_prism_call_node=(val) + val + end + self&.test_prism_call_node ||= 1 + CODE + end + + def test_CallOperatorWriteNode + assert_prism_eval(<<-CODE + class PrismTestSubclass; end + def PrismTestSubclass.test_call_operator_write_node + 2 + end + def PrismTestSubclass.test_call_operator_write_node=(val) + val + end + PrismTestSubclass.test_call_operator_write_node += 1 + CODE + ) + end + + def test_ForwardingArgumentsNode + assert_prism_eval(<<-CODE) + def prism_test_forwarding_arguments_node(...); end; + def prism_test_forwarding_arguments_node1(...) + prism_test_forwarding_arguments_node(...) + end + CODE + + assert_prism_eval(<<-CODE) + def prism_test_forwarding_arguments_node(...); end; + def prism_test_forwarding_arguments_node1(a, ...) + prism_test_forwarding_arguments_node(1,2, 3, ...) + end + CODE + + assert_prism_eval(<<~RUBY) + o = Object.new + def o.bar(a, b, c) = [a, b, c] + def o.foo(...) = 1.times { bar(...) } + + o.foo(1, 2, 3) + RUBY + end + + def test_ForwardingSuperNode + assert_prism_eval("class Forwarding; def to_s; super; end; end") + assert_prism_eval("class Forwarding; def eval(code); super { code }; end; end") + assert_prism_eval(<<-CODE) + class A + def initialize(a, b) + end + end + + class B < A + attr_reader :res + def initialize(a, b, *) + super + @res = [a, b] + end + end + + B.new(1, 2).res + CODE + end + + def test_KeywordHashNode + assert_prism_eval("[a: [:b, :c]]") + end + + def test_SuperNode + assert_prism_eval("def to_s; super 1; end") + assert_prism_eval("def to_s; super(); end") + assert_prism_eval("def to_s; super('a', :b, [1,2,3]); end") + assert_prism_eval("def to_s; super(1, 2, 3, &:foo); end") + end + + ############################################################################ + # Methods / parameters # + ############################################################################ + + def test_AliasGlobalVariableNode + assert_prism_eval("alias $prism_foo $prism_bar") + end + + def test_AliasMethodNode + assert_prism_eval("alias :prism_a :to_s") + end + + def test_BlockParameterNode + assert_prism_eval("def prism_test_block_parameter_node(&bar) end") + assert_prism_eval("->(b, c=1, *d, e, &f){}") + + # Test BlockParameterNode with no name + assert_prism_eval("->(&){}") + assert_prism_eval("def prism_test_block_parameter_node(&); end") + end + + def test_BlockParametersNode + assert_prism_eval("Object.tap { || }") + assert_prism_eval("[1].map { |num| num }") + assert_prism_eval("[1].map { |a; b| b = 2; a + b}") + + # Test block parameters with multiple _ + assert_prism_eval(<<~RUBY) + [[1, 2, 3, 4, 5, 6]].map { |(_, _, _, _, _, _)| _ } + RUBY + end + + def test_FowardingParameterNode + assert_prism_eval("def prism_test_forwarding_parameter_node(...); end") + end + + def test_KeywordRestParameterNode + assert_prism_eval("def prism_test_keyword_rest_parameter_node(a, **b); end") + assert_prism_eval("Object.tap { |**| }") + + # Test that KeywordRestParameterNode creates a copy + assert_prism_eval(<<~RUBY) + hash = {} + o = Object.new + def o.foo(**a) = a[:foo] = 1 + + o.foo(**hash) + hash + RUBY + end + + def test_NoKeywordsParameterNode + assert_prism_eval("def prism_test_no_keywords(**nil); end") + assert_prism_eval("def prism_test_no_keywords(a, b = 2, **nil); end") + end + + def test_OptionalParameterNode + assert_prism_eval("def prism_test_optional_param_node(bar = nil); end") + end + + def test_OptionalKeywordParameterNode + assert_prism_eval("def prism_test_optional_keyword_param_node(bar: nil); end") + + # Test with optional argument and method call in OptionalKeywordParameterNode + assert_prism_eval(<<~RUBY) + o = Object.new + def o.foo = 1 + def o.bar(a = nil, b: foo) = b + o.bar + RUBY + end + + def test_ParametersNode + assert_prism_eval("def prism_test_parameters_node(bar, baz); end") + assert_prism_eval("def prism_test_parameters_node(a, b = 2); end") + end + + def test_RequiredParameterNode + assert_prism_eval("def prism_test_required_param_node(bar); end") + assert_prism_eval("def prism_test_required_param_node(foo, bar); end") + end + + def test_RequiredKeywordParameterNode + assert_prism_eval("def prism_test_required_param_node(bar:); end") + assert_prism_eval("def prism_test_required_param_node(foo:, bar:); end") + assert_prism_eval("-> a, b = 1, c:, d:, &e { a }") + end + + def test_RestParameterNode + assert_prism_eval("def prism_test_rest_parameter_node(*a); end") + end + + def test_UndefNode + assert_prism_eval("def prism_undef_node_1; end; undef prism_undef_node_1") + assert_prism_eval(<<-HERE + def prism_undef_node_2 + end + def prism_undef_node_3 + end + undef prism_undef_node_2, prism_undef_node_3 + HERE + ) + assert_prism_eval(<<-HERE + def prism_undef_node_4 + end + undef :'prism_undef_node_#{4}' + HERE + ) + end + + ############################################################################ + # Pattern matching # + ############################################################################ + + def test_AlternationPatternNode + assert_prism_eval("1 in 1 | 2") + assert_prism_eval("1 in 2 | 1") + assert_prism_eval("1 in 2 | 3 | 4 | 1") + assert_prism_eval("1 in 2 | 3") + end + + def test_ArrayPatternNode + assert_prism_eval("[] => []") + + ["in", "=>"].each do |operator| + ["", "Array"].each do |constant| + assert_prism_eval("[1, 2, 3] #{operator} #{constant}[1, 2, 3]") + + assert_prism_eval("[1, 2, 3] #{operator} #{constant}[*]") + assert_prism_eval("[1, 2, 3] #{operator} #{constant}[1, *]") + assert_prism_eval("[1, 2, 3] #{operator} #{constant}[1, 2, *]") + assert_prism_eval("[1, 2, 3] #{operator} #{constant}[1, 2, 3, *]") + + assert_prism_eval("[1, 2, 3] #{operator} #{constant}[*foo]") + assert_prism_eval("[1, 2, 3] #{operator} #{constant}[1, *foo]") + assert_prism_eval("[1, 2, 3] #{operator} #{constant}[1, 2, *foo]") + assert_prism_eval("[1, 2, 3] #{operator} #{constant}[1, 2, 3, *foo]") + + assert_prism_eval("[1, 2, 3] #{operator} #{constant}[*, 3]") + assert_prism_eval("[1, 2, 3] #{operator} #{constant}[*, 2, 3]") + assert_prism_eval("[1, 2, 3] #{operator} #{constant}[*, 1, 2, 3]") + + assert_prism_eval("[1, 2, 3] #{operator} #{constant}[*foo, 3]") + assert_prism_eval("[1, 2, 3] #{operator} #{constant}[*foo, 2, 3]") + assert_prism_eval("[1, 2, 3] #{operator} #{constant}[*foo, 1, 2, 3]") + end + end + + assert_prism_eval("begin; Object.new => [1, 2, 3]; rescue NoMatchingPatternError; true; end") + assert_prism_eval("begin; [1, 2, 3] => Object[1, 2, 3]; rescue NoMatchingPatternError; true; end") + end + + def test_CapturePatternNode + assert_prism_eval("[1] => [Integer => foo]") + end + + def test_CaseMatchNode + assert_prism_eval(<<~RUBY) + case [1, 2, 3] + in [1, 2, 3] + 4 + end + RUBY + + assert_prism_eval(<<~RUBY) + case { a: 5, b: 6 } + in [1, 2, 3] + 4 + in { a: 5, b: 6 } + 7 + end + RUBY + + assert_prism_eval(<<~RUBY) + case [1, 2, 3, 4] + in [1, 2, 3] + 4 + in { a: 5, b: 6 } + 7 + else + end + RUBY + + assert_prism_eval(<<~RUBY) + case [1, 2, 3, 4] + in [1, 2, 3] + 4 + in { a: 5, b: 6 } + 7 + else + 8 + end + RUBY + + assert_prism_eval(<<~RUBY) + case [1, 2, 3] + in [1, 2, 3] unless to_s + in [1, 2, 3] if to_s.nil? + in [1, 2, 3] + true + end + RUBY + end + + def test_FindPatternNode + ["in", "=>"].each do |operator| + ["", "Array"].each do |constant| + assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 1, 2, 3, 4, 5, *]") + + assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 1, *]") + assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 3, *]") + assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 5, *]") + + assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 1, 2, *]") + assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 2, 3, *]") + assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 3, 4, *]") + assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 4, 5, *]") + + assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 1, 2, 3, *]") + assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 2, 3, 4, *]") + assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 3, 4, 5, *]") + + assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 1, 2, 3, 4, *]") + assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 2, 3, 4, 5, *]") + + assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*foo, 3, *]") + assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*foo, 3, 4, *]") + assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*foo, 3, 4, 5, *]") + assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*foo, 1, 2, 3, 4, *]") + + assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 3, *foo]") + assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 3, 4, *foo]") + assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 3, 4, 5, *foo]") + assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*, 1, 2, 3, 4, *foo]") + + assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*foo, 3, *bar]") + assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*foo, 3, 4, *bar]") + assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*foo, 3, 4, 5, *bar]") + assert_prism_eval("[1, 2, 3, 4, 5] #{operator} #{constant}[*foo, 1, 2, 3, 4, *bar]") + end + end + + assert_prism_eval("[1, [2, [3, [4, [5]]]]] => [*, [*, [*, [*, [*]]]]]") + assert_prism_eval("[1, [2, [3, [4, [5]]]]] => [1, [2, [3, [4, [5]]]]]") + + assert_prism_eval("begin; Object.new => [*, 2, *]; rescue NoMatchingPatternError; true; end") + assert_prism_eval("begin; [1, 2, 3] => Object[*, 2, *]; rescue NoMatchingPatternError; true; end") + end + + def test_HashPatternNode + assert_prism_eval("{} => {}") + + [["{ ", " }"], ["Hash[", "]"]].each do |(prefix, suffix)| + assert_prism_eval("{} => #{prefix} **nil #{suffix}") + + assert_prism_eval("{ a: 1, b: 2, c: 3 } => #{prefix} a: 1 #{suffix}") + assert_prism_eval("{ a: 1, b: 2, c: 3 } => #{prefix} a: 1, b: 2 #{suffix}") + assert_prism_eval("{ a: 1, b: 2, c: 3 } => #{prefix} b: 2, c: 3 #{suffix}") + assert_prism_eval("{ a: 1, b: 2, c: 3 } => #{prefix} a: 1, b: 2, c: 3 #{suffix}") + + assert_prism_eval("{ a: 1, b: 2, c: 3 } => #{prefix} ** #{suffix}") + assert_prism_eval("{ a: 1, b: 2, c: 3 } => #{prefix} a: 1, ** #{suffix}") + assert_prism_eval("{ a: 1, b: 2, c: 3 } => #{prefix} a: 1, b: 2, ** #{suffix}") + assert_prism_eval("{ a: 1, b: 2, c: 3 } => #{prefix} b: 2, c: 3, ** #{suffix}") + assert_prism_eval("{ a: 1, b: 2, c: 3 } => #{prefix} a: 1, b: 2, c: 3, ** #{suffix}") + + assert_prism_eval("{ a: 1, b: 2, c: 3 } => #{prefix} **foo #{suffix}") + assert_prism_eval("{ a: 1, b: 2, c: 3 } => #{prefix} a: 1, **foo #{suffix}") + assert_prism_eval("{ a: 1, b: 2, c: 3 } => #{prefix} a: 1, b: 2, **foo #{suffix}") + assert_prism_eval("{ a: 1, b: 2, c: 3 } => #{prefix} b: 2, c: 3, **foo #{suffix}") + assert_prism_eval("{ a: 1, b: 2, c: 3 } => #{prefix} a: 1, b: 2, c: 3, **foo #{suffix}") + + assert_prism_eval("{ a: 1 } => #{prefix} a: 1, **nil #{suffix}") + assert_prism_eval("{ a: 1, b: 2, c: 3 } => #{prefix} a: 1, b: 2, c: 3, **nil #{suffix}") + end + + assert_prism_eval("{ a: { b: { c: 1 } } } => { a: { b: { c: 1 } } }") + end + + def test_MatchPredicateNode + assert_prism_eval("1 in 1") + assert_prism_eval("1.0 in 1.0") + assert_prism_eval("1i in 1i") + assert_prism_eval("1r in 1r") + + assert_prism_eval("\"foo\" in \"foo\"") + assert_prism_eval("\"foo \#{1}\" in \"foo \#{1}\"") + + assert_prism_eval("false in false") + assert_prism_eval("nil in nil") + assert_prism_eval("self in self") + assert_prism_eval("true in true") + + assert_prism_eval("5 in 0..10") + assert_prism_eval("5 in 0...10") + + assert_prism_eval("[\"5\"] in %w[5]") + + assert_prism_eval("Prism in Prism") + assert_prism_eval("Prism in ::Prism") + + assert_prism_eval(":prism in :prism") + assert_prism_eval("%s[prism\#{1}] in %s[prism\#{1}]") + assert_prism_eval("\"foo\" in /.../") + assert_prism_eval("\"foo1\" in /...\#{1}/") + assert_prism_eval("4 in ->(v) { v.even? }") + + assert_prism_eval("5 in foo") + + assert_prism_eval("1 in 2") + end + + def test_MatchRequiredNode + assert_prism_eval("1 => 1") + assert_prism_eval("1.0 => 1.0") + assert_prism_eval("1i => 1i") + assert_prism_eval("1r => 1r") + + assert_prism_eval("\"foo\" => \"foo\"") + assert_prism_eval("\"foo \#{1}\" => \"foo \#{1}\"") + + assert_prism_eval("false => false") + assert_prism_eval("nil => nil") + assert_prism_eval("true => true") + + assert_prism_eval("5 => 0..10") + assert_prism_eval("5 => 0...10") + + assert_prism_eval("[\"5\"] => %w[5]") + + assert_prism_eval(":prism => :prism") + assert_prism_eval("%s[prism\#{1}] => %s[prism\#{1}]") + assert_prism_eval("\"foo\" => /.../") + assert_prism_eval("\"foo1\" => /...\#{1}/") + assert_prism_eval("4 => ->(v) { v.even? }") + + assert_prism_eval("5 => foo") + end + + def test_PinnedExpressionNode + assert_prism_eval("4 in ^(4)") + end + + def test_PinnedVariableNode + assert_prism_eval("module Prism; @@prism = 1; 1 in ^@@prism; end") + assert_prism_eval("module Prism; @prism = 1; 1 in ^@prism; end") + assert_prism_eval("$prism = 1; 1 in ^$prism") + assert_prism_eval("prism = 1; 1 in ^prism") + end + + ############################################################################ + # Miscellaneous # + ############################################################################ + + def test_eval + assert_prism_eval("eval('1 + 1')", raw: true) + assert_prism_eval("a = 1; eval('a + 1')", raw: true) + + assert_prism_eval(<<~CODE, raw: true) + def prism_eval_splat(**bar) + eval("bar") + end + prism_eval_splat(bar: 10) + CODE + + assert_prism_eval(<<~CODE, raw: true) + def prism_eval_keywords(baz:) + eval("baz") + end + prism_eval_keywords(baz: 10) + CODE + + assert_prism_eval(<<~CODE, raw: true) + [1].each do |a| + [2].each do |b| + c = 3 + eval("a + b + c") + end + end + CODE + + assert_prism_eval(<<~CODE, raw: true) + def prism_eval_binding(b) + eval("bar", b) + end + + bar = :ok + prism_eval_binding(binding) + CODE + end + + def test_ScopeNode + assert_separately(%w[], <<~'RUBY') + def compare_eval(source) + ruby_eval = RubyVM::InstructionSequence.compile("module A; " + source + "; end").eval + prism_eval = RubyVM::InstructionSequence.compile_prism("module B; " + source + "; end").eval + + assert_equal ruby_eval, prism_eval + end + + def assert_prism_eval(source) + $VERBOSE, verbose_bak = nil, $VERBOSE + + begin + compare_eval(source) + + # Test "popped" functionality + compare_eval("#{source}; 1") + ensure + $VERBOSE = verbose_bak + end + end + + assert_prism_eval("a = 1; 1.times do; { a: }; end") + assert_prism_eval("a = 1; def foo(a); a; end") + RUBY + end + + ############################################################################ + # Errors # + ############################################################################ + + def test_MissingNode + # TODO + end + + ############################################################################ + # Encoding # + ############################################################################ + + def test_encoding + assert_prism_eval('"perĂ²"') + assert_prism_eval(":perĂ²") + end + + def test_parse_file + assert_nothing_raised do + RubyVM::InstructionSequence.compile_file_prism(__FILE__) + end + + error = assert_raise Errno::ENOENT do + RubyVM::InstructionSequence.compile_file_prism("idontexist.rb") + end + + assert_equal "No such file or directory - idontexist.rb", error.message + + assert_raise TypeError do + RubyVM::InstructionSequence.compile_file_prism(nil) + end + end + + private + + def compare_eval(source, raw:, location:) + source = raw ? source : "class Prism::TestCompilePrism\n#{source}\nend" + + ruby_eval = RubyVM::InstructionSequence.compile(source).eval + prism_eval = RubyVM::InstructionSequence.compile_prism(source).eval + + if ruby_eval.is_a? Proc + assert_equal ruby_eval.class, prism_eval.class, "@#{location.path}:#{location.lineno}" + else + assert_equal ruby_eval, prism_eval, "@#{location.path}:#{location.lineno}" + end + end + + def assert_prism_eval(source, raw: false) + location = caller_locations(1, 1).first + $VERBOSE, verbose_bak = nil, $VERBOSE + + begin + compare_eval(source, raw:, location:) + + # Test "popped" functionality + compare_eval("#{source}; 1", raw:, location:) + ensure + $VERBOSE = verbose_bak + end + end + end +end |