summaryrefslogtreecommitdiff
path: root/test/ruby/test_compile_prism.rb
diff options
context:
space:
mode:
Diffstat (limited to 'test/ruby/test_compile_prism.rb')
-rw-r--r--test/ruby/test_compile_prism.rb2671
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