diff options
Diffstat (limited to 'test/ruby/test_parse.rb')
| -rw-r--r-- | test/ruby/test_parse.rb | 1113 |
1 files changed, 1113 insertions, 0 deletions
diff --git a/test/ruby/test_parse.rb b/test/ruby/test_parse.rb new file mode 100644 index 0000000000..b725634a38 --- /dev/null +++ b/test/ruby/test_parse.rb @@ -0,0 +1,1113 @@ +# coding: US-ASCII +# frozen_string_literal: false +require 'test/unit' +require 'stringio' + +class TestParse < Test::Unit::TestCase + def setup + @verbose = $VERBOSE + $VERBOSE = nil + end + + def teardown + $VERBOSE = @verbose + end + + def test_else_without_rescue + x = eval <<-END, nil, __FILE__, __LINE__+1 + begin + else + 42 + end + END + assert_equal(42, x) + end + + def test_alias_backref + assert_raise(SyntaxError) do + eval <<-END, nil, __FILE__, __LINE__+1 + alias $foo $1 + END + end + end + + def test_command_call + t = Object.new + def t.foo(x); x; end + + a = false + b = c = d = true + assert_nothing_raised do + eval <<-END, nil, __FILE__, __LINE__+1 + a &&= t.foo 42 + b &&= t.foo 42 + c &&= t.foo nil + d &&= t.foo false + END + end + assert_equal([false, 42, nil, false], [a, b, c, d]) + + a = 3 + assert_nothing_raised { eval("a &= t.foo 5") } + assert_equal(1, a) + + a = [nil, nil, true, true] + assert_nothing_raised do + eval <<-END, nil, __FILE__, __LINE__+1 + a[0] ||= t.foo 42 + a[1] &&= t.foo 42 + a[2] ||= t.foo 42 + a[3] &&= t.foo 42 + END + end + assert_equal([42, nil, true, 42], a) + + o = Object.new + class << o + attr_accessor :foo, :bar, :Foo, :Bar, :baz, :qux + end + o.foo = o.Foo = o::baz = nil + o.bar = o.Bar = o::qux = 1 + assert_nothing_raised do + eval <<-END, nil, __FILE__, __LINE__+1 + o.foo ||= t.foo 42 + o.bar &&= t.foo 42 + o.Foo ||= t.foo 42 + o.Bar &&= t.foo 42 + o::baz ||= t.foo 42 + o::qux &&= t.foo 42 + END + end + assert_equal([42, 42], [o.foo, o.bar]) + assert_equal([42, 42], [o.Foo, o.Bar]) + assert_equal([42, 42], [o::baz, o::qux]) + + assert_raise(SyntaxError) do + eval <<-END, nil, __FILE__, __LINE__+1 + $1 ||= t.foo 42 + END + end + + def t.bar(x); x + yield; end + + a = b = nil + assert_nothing_raised do + eval <<-END, nil, __FILE__, __LINE__+1 + a = t.bar "foo" do + "bar" + end.gsub "ob", "OB" + b = t.bar "foo" do + "bar" + end::gsub "ob", "OB" + END + end + assert_equal("foOBar", a) + assert_equal("foOBar", b) + + a = nil + assert_nothing_raised do + t.instance_eval <<-END, __FILE__, __LINE__+1 + a = bar "foo" do "bar" end + END + end + assert_equal("foobar", a) + + a = nil + assert_nothing_raised do + eval <<-END, nil, __FILE__, __LINE__+1 + a = t::bar "foo" do "bar" end + END + end + assert_equal("foobar", a) + + def t.baz(*r) + @baz = r + (block_given? ? [yield] : []) + end + + assert_nothing_raised do + t.instance_eval "baz (1), 2" + end + assert_equal([1, 2], t.instance_eval { @baz }) + end + + def test_mlhs_node + c = Class.new + class << c + attr_accessor :foo, :bar, :Foo, :Bar + FOO = BAR = nil + end + + assert_nothing_raised do + eval <<-END, nil, __FILE__, __LINE__+1 + c::foo, c::bar = 1, 2 + c.Foo, c.Bar = 1, 2 + c::FOO, c::BAR = 1, 2 + END + end + assert_equal([1, 2], [c::foo, c::bar]) + assert_equal([1, 2], [c.Foo, c.Bar]) + assert_equal([1, 2], [c::FOO, c::BAR]) + end + + def test_dynamic_constant_assignment + assert_raise(SyntaxError) do + Object.new.instance_eval <<-END, __FILE__, __LINE__+1 + def foo + self::FOO, self::BAR = 1, 2 + ::FOO, ::BAR = 1, 2 + end + END + end + + assert_raise(SyntaxError) do + eval <<-END, nil, __FILE__, __LINE__+1 + $1, $2 = 1, 2 + END + end + + assert_raise(SyntaxError) do + Object.new.instance_eval <<-END, __FILE__, __LINE__+1 + def foo + ::FOO = 1 + end + END + end + + c = Class.new + c.freeze + assert_nothing_raised(SyntaxError) do + eval <<-END, nil, __FILE__, __LINE__+1 + if false + c::FOO &= 1 + ::FOO &= 1 + end + END + end + + assert_raise(SyntaxError) do + eval <<-END, nil, __FILE__, __LINE__+1 + $1 &= 1 + END + end + end + + def test_class_module + assert_raise(SyntaxError) do + eval <<-END, nil, __FILE__, __LINE__+1 + class foo; end + END + end + + assert_raise(SyntaxError) do + eval <<-END, nil, __FILE__, __LINE__+1 + def foo + class Foo; end + module Bar; end + end + END + end + + assert_nothing_raised(SyntaxError) do + eval <<-END, nil, __FILE__, __LINE__+1 + class Foo 1; end + END + end + end + + def test_op_name + o = Object.new + def o.>(x); x; end + def o./(x); x; end + + assert_nothing_raised do + o.instance_eval <<-END, __FILE__, __LINE__+1 + undef >, / + END + end + end + + def test_arg + o = Object.new + class << o + attr_accessor :foo, :bar, :Foo, :Bar, :baz, :qux + end + o.foo = o.Foo = o::baz = nil + o.bar = o.Bar = o::qux = 1 + assert_nothing_raised do + eval <<-END, nil, __FILE__, __LINE__+1 + o.foo ||= 42 + o.bar &&= 42 + o.Foo ||= 42 + o.Bar &&= 42 + o::baz ||= 42 + o::qux &&= 42 + END + end + assert_equal([42, 42], [o.foo, o.bar]) + assert_equal([42, 42], [o.Foo, o.Bar]) + assert_equal([42, 42], [o::baz, o::qux]) + + a = nil + assert_nothing_raised do + eval <<-END, nil, __FILE__, __LINE__+1 + a = -2.0 ** 2 + END + end + assert_equal(-4.0, a) + end + + def test_block_variable + o = Object.new + def o.foo(*r); yield(*r); end + + a = nil + assert_nothing_raised do + eval <<-END, nil, __FILE__, __LINE__+1 + o.foo 1 do|; a| a = 42 end + END + end + assert_nil(a) + end + + def test_bad_arg + assert_raise(SyntaxError) do + eval <<-END, nil, __FILE__, __LINE__+1 + def foo(FOO); end + END + end + + assert_raise(SyntaxError) do + eval <<-END, nil, __FILE__, __LINE__+1 + def foo(@foo); end + END + end + + assert_raise(SyntaxError) do + eval <<-END, nil, __FILE__, __LINE__+1 + def foo($foo); end + END + end + + assert_raise(SyntaxError) do + eval <<-END, nil, __FILE__, __LINE__+1 + def foo(@@foo); end + END + end + + o = Object.new + def o.foo(*r); yield(*r); end + + assert_raise(SyntaxError) do + eval <<-END, nil, __FILE__, __LINE__+1 + o.foo 1 {|; @a| @a = 42 } + END + end + end + + def test_do_lambda + a = b = nil + assert_nothing_raised do + eval <<-END, nil, __FILE__, __LINE__+1 + a = -> do + b = 42 + end + END + end + a.call + assert_equal(42, b) + end + + def test_block_call_colon2 + o = Object.new + def o.foo(x); x + yield; end + + a = b = nil + assert_nothing_raised do + o.instance_eval <<-END, __FILE__, __LINE__+1 + a = foo 1 do 42 end.to_s + b = foo 1 do 42 end::to_s + END + end + assert_equal("43", a) + assert_equal("43", b) + end + + def test_call_method + a = b = nil + assert_nothing_raised do + eval <<-END, nil, __FILE__, __LINE__+1 + a = proc {|x| x + "bar" }.("foo") + b = proc {|x| x + "bar" }::("foo") + END + end + assert_equal("foobar", a) + assert_equal("foobar", b) + end + + def test_xstring + assert_raise(Errno::ENOENT) do + eval("``") + end + end + + def test_words + assert_equal([], %W( )) + end + + def test_dstr + @@foo = 1 + assert_equal("foo 1 bar", "foo #@@foo bar") + "1" =~ /(.)/ + assert_equal("foo 1 bar", "foo #$1 bar") + end + + def test_dstr_disallowed_variable + bug8375 = '[ruby-core:54885] [Bug #8375]' + %w[@ @1 @. @@ @@1 @@. $ $%].each do |src| + src = '#'+src+' ' + str = assert_nothing_raised(SyntaxError, "#{bug8375} #{src.dump}") do + break eval('"'+src+'"') + end + assert_equal(src, str, bug8375) + end + end + + def test_dsym + assert_nothing_raised { eval(':""') } + end + + def assert_disallowed_variable(type, noname, *invalid) + assert_syntax_error(noname, "`#{noname}' without identifiers is not allowed as #{type} variable name") + invalid.each do |name| + assert_syntax_error(name, "`#{name}' is not allowed as #{type} variable name") + end + end + + def test_disallowed_instance_variable + assert_disallowed_variable("an instance", *%w[@ @1 @.]) + end + + def test_disallowed_class_variable + assert_disallowed_variable("a class", *%w[@@ @@1 @@.]) + end + + def test_disallowed_gloal_variable + assert_disallowed_variable("a global", *%w[$ $%]) + end + + def test_arg2 + o = Object.new + + assert_nothing_raised do + eval <<-END, nil, __FILE__, __LINE__+1 + def o.foo(a=42,*r,z,&b); b.call(r.inject(a*1000+z*100, :+)); end + END + end + assert_equal(-1405, o.foo(1,2,3,4) {|x| -x }) + assert_equal(-1302, o.foo(1,2,3) {|x| -x }) + assert_equal(-1200, o.foo(1,2) {|x| -x }) + assert_equal(-42100, o.foo(1) {|x| -x }) + assert_raise(ArgumentError) { o.foo() } + + assert_nothing_raised do + eval <<-END, nil, __FILE__, __LINE__+1 + def o.foo(a=42,z,&b); b.call(a*1000+z*100); end + END + end + assert_equal(-1200, o.foo(1,2) {|x| -x } ) + assert_equal(-42100, o.foo(1) {|x| -x } ) + assert_raise(ArgumentError) { o.foo() } + + assert_nothing_raised do + eval <<-END, nil, __FILE__, __LINE__+1 + def o.foo(*r,z,&b); b.call(r.inject(z*100, :+)); end + END + end + assert_equal(-303, o.foo(1,2,3) {|x| -x } ) + assert_equal(-201, o.foo(1,2) {|x| -x } ) + assert_equal(-100, o.foo(1) {|x| -x } ) + assert_raise(ArgumentError) { o.foo() } + end + + def test_duplicate_argument + assert_raise(SyntaxError) do + eval <<-END, nil, __FILE__, __LINE__+1 + 1.times {|&b?| } + END + end + + assert_raise(SyntaxError) do + eval <<-END, nil, __FILE__, __LINE__+1 + 1.times {|a, a|} + END + end + + assert_raise(SyntaxError) do + eval <<-END, nil, __FILE__, __LINE__+1 + def foo(a, a); end + END + end + end + + def test_define_singleton_error + assert_raise(SyntaxError) do + eval <<-END, nil, __FILE__, __LINE__+1 + def ("foo").foo; end + END + end + end + + def test_backquote + t = Object.new + + assert_nothing_raised do + eval <<-END, nil, __FILE__, __LINE__+1 + def t.`(x); "foo" + x + "bar"; end + END + end + a = b = nil + assert_nothing_raised do + eval <<-END, nil, __FILE__, __LINE__+1 + a = t.` "zzz" + 1.times {|;z| t.` ("zzz") } + END + t.instance_eval <<-END, __FILE__, __LINE__+1 + b = `zzz` + END + end + assert_equal("foozzzbar", a) + assert_equal("foozzzbar", b) + end + + def test_carrige_return + assert_equal(2, eval("1 +\r\n1")) + end + + def test_string + mesg = 'from the backslash through the invalid char' + + e = assert_syntax_error('"\xg1"', /hex escape/) + assert_equal(' ^', e.message.lines.last, mesg) + + e = assert_syntax_error('"\u{1234"', 'unterminated Unicode escape') + assert_equal(' ^', e.message.lines.last, mesg) + + e = assert_syntax_error('"\u{xxxx}"', 'invalid Unicode escape') + assert_equal(' ^', e.message.lines.last, mesg) + + e = assert_syntax_error('"\u{xxxx', 'Unicode escape') + assert_pattern_list([ + /.*: invalid Unicode escape\n.*\n/, + / \^/, + /\n/, + /.*: unterminated Unicode escape\n.*\n/, + / \^/, + /\n/, + /.*: unterminated string.*\n.*\n/, + / \^/, + ], e.message) + + e = assert_syntax_error('"\M1"', /escape character syntax/) + assert_equal(' ^~~', e.message.lines.last, mesg) + + e = assert_syntax_error('"\C1"', /escape character syntax/) + assert_equal(' ^~~', e.message.lines.last, mesg) + + src = '"\xD0\u{90'"\n""000000000000000000000000" + assert_syntax_error(src, /:#{__LINE__}: unterminated/o) + + assert_syntax_error('"\u{100000000}"', /invalid Unicode escape/) + assert_equal("", eval('"\u{}"')) + assert_equal("", eval('"\u{ }"')) + + assert_equal("\x81", eval('"\C-\M-a"')) + assert_equal("\177", eval('"\c?"')) + end + + def test_question + assert_raise(SyntaxError) { eval('?') } + assert_raise(SyntaxError) { eval('? ') } + assert_raise(SyntaxError) { eval("?\n") } + assert_raise(SyntaxError) { eval("?\t") } + assert_raise(SyntaxError) { eval("?\v") } + assert_raise(SyntaxError) { eval("?\r") } + assert_raise(SyntaxError) { eval("?\f") } + assert_raise(SyntaxError) { eval("?\f") } + assert_raise(SyntaxError) { eval(" ?a\x8a".force_encoding("utf-8")) } + assert_equal("\u{1234}", eval("?\u{1234}")) + assert_equal("\u{1234}", eval('?\u{1234}')) + end + + def test_percent + assert_equal(:foo, eval('%s(foo)')) + assert_raise(SyntaxError) { eval('%s') } + assert_raise(SyntaxError) { eval('%ss') } + assert_raise(SyntaxError) { eval('%z()') } + end + + def test_symbol + bug = '[ruby-dev:41447]' + sym = "foo\0bar".to_sym + assert_nothing_raised(SyntaxError, bug) do + assert_equal(sym, eval(":'foo\0bar'")) + end + assert_nothing_raised(SyntaxError, bug) do + assert_equal(sym, eval(':"foo\u0000bar"')) + end + assert_nothing_raised(SyntaxError, bug) do + assert_equal(sym, eval(':"foo\u{0}bar"')) + end + assert_nothing_raised(SyntaxError) do + assert_equal(:foobar, eval(':"foo\u{}bar"')) + assert_equal(:foobar, eval(':"foo\u{ }bar"')) + end + end + + def test_parse_string + assert_raise(SyntaxError) do + eval <<-END, nil, __FILE__, __LINE__+1 +/ + END + end + end + + def test_here_document + x = nil + + assert_raise(SyntaxError) do + eval %Q( +<\<FOO + ) + end + + assert_nothing_raised(SyntaxError) do + x = eval %q( +<<FOO +#$ +FOO + ) + end + assert_equal "\#$\n", x + + assert_raise(SyntaxError) do + eval %Q( +<\<\" + ) + end + + assert_raise(SyntaxError) do + eval %q( +<<`` + ) + end + + assert_raise(SyntaxError) do + eval %q( +<<-- + ) + end + + assert_nothing_raised(SyntaxError) do + x = eval %q( +<<FOO +#$ +foo +FOO + ) + end + assert_equal "\#$\nfoo\n", x + + assert_nothing_raised do + eval "x = <<""FOO\r\n1\r\nFOO" + end + assert_equal("1\n", x) + end + + def test_magic_comment + x = nil + assert_nothing_raised do + eval <<-END, nil, __FILE__, __LINE__+1 +# coding = utf-8 +x = __ENCODING__ + END + end + assert_equal(Encoding.find("UTF-8"), x) + + assert_raise(ArgumentError) do + eval <<-END, nil, __FILE__, __LINE__+1 +# coding = foobarbazquxquux_dummy_enconding +x = __ENCODING__ + END + end + end + + def test_utf8_bom + x = nil + assert_nothing_raised do + eval "\xef\xbb\xbf x = __ENCODING__" + end + assert_equal(Encoding.find("UTF-8"), x) + assert_raise(NameError) { eval "\xef" } + end + + def test_dot_in_next_line + x = nil + assert_nothing_raised do + eval <<-END, nil, __FILE__, __LINE__+1 + x = 1 + .to_s + END + end + assert_equal("1", x) + end + + def test_pow_asgn + x = 3 + assert_nothing_raised { eval("x **= 2") } + assert_equal(9, x) + end + + def test_embedded_rd + assert_raise(SyntaxError) do + eval <<-END, nil, __FILE__, __LINE__+1 +=begin + END + end + end + + def test_float + assert_equal(1.0/0, eval("1e10000")) + assert_raise(SyntaxError) { eval('1_E') } + assert_raise(SyntaxError) { eval('1E1E1') } + end + + def test_global_variable + assert_equal(nil, eval('$-x')) + assert_equal(nil, eval('alias $preserve_last_match $&')) + assert_equal(nil, eval('alias $& $test_parse_foobarbazqux')) + $test_parse_foobarbazqux = nil + assert_equal(nil, $&) + assert_equal(nil, eval('alias $& $preserve_last_match')) + assert_raise(SyntaxError) { eval('$#') } + end + + def test_invalid_instance_variable + assert_raise(SyntaxError) { eval('@#') } + assert_raise(SyntaxError) { eval('@') } + end + + def test_invalid_class_variable + assert_raise(SyntaxError) { eval('@@1') } + assert_raise(SyntaxError) { eval('@@') } + end + + def test_invalid_char + bug10117 = '[ruby-core:64243] [Bug #10117]' + invalid_char = /Invalid char `\\x01'/ + x = 1 + assert_in_out_err(%W"-e \x01x", "", [], invalid_char, bug10117) + assert_syntax_error("\x01x", invalid_char, bug10117) + assert_equal(nil, eval("\x04x")) + assert_equal 1, x + end + + def test_literal_concat + x = "baz" + assert_equal("foobarbaz", eval('"foo" "bar#{x}"')) + assert_equal("baz", x) + end + + def test_unassignable + assert_raise(SyntaxError) do + eval %q(self = 1) + end + assert_raise(SyntaxError) do + eval %q(nil = 1) + end + assert_raise(SyntaxError) do + eval %q(true = 1) + end + assert_raise(SyntaxError) do + eval %q(false = 1) + end + assert_raise(SyntaxError) do + eval %q(__FILE__ = 1) + end + assert_raise(SyntaxError) do + eval %q(__LINE__ = 1) + end + assert_raise(SyntaxError) do + eval %q(__ENCODING__ = 1) + end + assert_raise(SyntaxError) do + eval <<-END, nil, __FILE__, __LINE__+1 + def foo + FOO = 1 + end + END + end + assert_raise(SyntaxError) do + eval "#{<<~"begin;"}\n#{<<~'end;'}", nil, __FILE__, __LINE__+1 + begin; + x, true + end; + end + end + + def test_block_dup + assert_raise(SyntaxError) do + eval <<-END, nil, __FILE__, __LINE__+1 + foo(&proc{}) {} + END + end + end + + def test_set_backref + assert_raise(SyntaxError) do + eval <<-END, nil, __FILE__, __LINE__+1 + $& = 1 + END + end + end + + def test_arg_concat + o = Object.new + class << o; self; end.instance_eval do + define_method(:[]=) {|*r, &b| b.call(r) } + end + r = nil + assert_nothing_raised do + eval <<-END, nil, __FILE__, __LINE__+1 + o[&proc{|x| r = x }] = 1 + END + end + assert_equal([1], r) + end + + def test_void_expr_stmts_value + # This test checks if void contexts are warned correctly. + # Thus, warnings MUST NOT be suppressed. + $VERBOSE = true + stderr = $stderr + $stderr = StringIO.new("") + x = 1 + assert_nil eval("x; nil") + assert_nil eval("1+1; nil") + assert_nil eval("1.+(1); nil") + assert_nil eval("TestParse; nil") + assert_nil eval("::TestParse; nil") + assert_nil eval("x..x; nil") + assert_nil eval("x...x; nil") + assert_nil eval("self; nil") + assert_nil eval("nil; nil") + assert_nil eval("true; nil") + assert_nil eval("false; nil") + assert_nil eval("defined?(1); nil") + assert_equal 1, x + + assert_raise(SyntaxError) do + eval %q(1; next; 2) + end + + assert_equal(13, $stderr.string.lines.to_a.size) + $stderr = stderr + end + + def test_assign_in_conditional + assert_nothing_raised do + eval <<-END, nil, __FILE__, __LINE__+1 + (x, y = 1, 2) ? 1 : 2 + END + end + + assert_nothing_raised do + eval <<-END, nil, __FILE__, __LINE__+1 + if @x = true + 1 + else + 2 + end + END + end + end + + def test_literal_in_conditional + assert_nothing_raised do + eval <<-END, nil, __FILE__, __LINE__+1 + "foo" ? 1 : 2 + END + end + + assert_nothing_raised do + x = "bar" + eval <<-END, nil, __FILE__, __LINE__+1 + /foo#{x}baz/ ? 1 : 2 + END + end + + assert_nothing_raised do + eval <<-END, nil, __FILE__, __LINE__+1 + (true..false) ? 1 : 2 + END + end + + assert_nothing_raised do + eval <<-END, nil, __FILE__, __LINE__+1 + ("foo".."bar") ? 1 : 2 + END + end + + assert_nothing_raised do + x = "bar" + eval <<-END, nil, __FILE__, __LINE__+1 + :"foo#{"x"}baz" ? 1 : 2 + END + assert_equal "bar", x + end + end + + def test_no_blockarg + assert_raise(SyntaxError) do + eval <<-END, nil, __FILE__, __LINE__+1 + yield(&:+) + END + end + end + + def test_method_block_location + bug5614 = '[ruby-core:40936]' + expected = nil + e = assert_raise(NoMethodError) do + 1.times do + expected = __LINE__+1 + end.print do + # + end + end + actual = e.backtrace.first[/\A#{Regexp.quote(__FILE__)}:(\d+):/o, 1].to_i + assert_equal(expected, actual, bug5614) + end + + def test_shadowing_variable + assert_warning(/shadowing outer local variable/) {eval("a=1; tap {|a|}")} + a = "\u{3042}" + assert_warning(/#{a}/o) {eval("#{a}=1; tap {|#{a}|}")} + end + + def test_unused_variable + o = Object.new + assert_warning(/assigned but unused variable/) {o.instance_eval("def foo; a=1; nil; end")} + assert_warning(/assigned but unused variable/) {o.instance_eval("def bar; a=1; a(); end")} + a = "\u{3042}" + assert_warning(/#{a}/) {o.instance_eval("def foo; #{a}=1; nil; end")} + o = Object.new + assert_warning(/assigned but unused variable/) {o.instance_eval("def foo; tap {a=1; a()}; end")} + assert_warning('') {o.instance_eval("def bar; a=a=1; nil; end")} + end + + def test_named_capture_conflict + a = 1 + assert_warning('') {eval("a = 1; /(?<a>)/ =~ ''")} + a = "\u{3042}" + assert_warning('') {eval("#{a} = 1; /(?<#{a}>)/ =~ ''")} + end + + def test_rescue_in_command_assignment + bug = '[ruby-core:75621] [Bug #12402]' + all_assertions(bug) do |a| + a.for("lhs = arg") do + v = bug + v = raise(bug) rescue "ok" + assert_equal("ok", v) + end + a.for("lhs op_asgn arg") do + v = 0 + v += raise(bug) rescue 1 + assert_equal(1, v) + end + a.for("lhs[] op_asgn arg") do + v = [0] + v[0] += raise(bug) rescue 1 + assert_equal([1], v) + end + a.for("lhs.m op_asgn arg") do + k = Struct.new(:m) + v = k.new(0) + v.m += raise(bug) rescue 1 + assert_equal(k.new(1), v) + end + a.for("lhs::m op_asgn arg") do + k = Struct.new(:m) + v = k.new(0) + v::m += raise(bug) rescue 1 + assert_equal(k.new(1), v) + end + a.for("lhs.C op_asgn arg") do + k = Struct.new(:C) + v = k.new(0) + v.C += raise(bug) rescue 1 + assert_equal(k.new(1), v) + end + a.for("lhs::C op_asgn arg") do + v = Class.new + v::C ||= raise(bug) rescue 1 + assert_equal(1, v::C) + end + a.for("lhs = command") do + v = bug + v = raise bug rescue "ok" + assert_equal("ok", v) + end + a.for("lhs op_asgn command") do + v = 0 + v += raise bug rescue 1 + assert_equal(1, v) + end + a.for("lhs[] op_asgn command") do + v = [0] + v[0] += raise bug rescue 1 + assert_equal([1], v) + end + a.for("lhs.m op_asgn command") do + k = Struct.new(:m) + v = k.new(0) + v.m += raise bug rescue 1 + assert_equal(k.new(1), v) + end + a.for("lhs::m op_asgn command") do + k = Struct.new(:m) + v = k.new(0) + v::m += raise bug rescue 1 + assert_equal(k.new(1), v) + end + a.for("lhs.C op_asgn command") do + k = Struct.new(:C) + v = k.new(0) + v.C += raise bug rescue 1 + assert_equal(k.new(1), v) + end + a.for("lhs::C op_asgn command") do + v = Class.new + v::C ||= raise bug rescue 1 + assert_equal(1, v::C) + end + end + end + + def test_yyerror_at_eol + assert_syntax_error(" 0b", /\^/) + assert_syntax_error(" 0b\n", /\^/) + end + + def test_error_def_in_argument + assert_separately([], "#{<<-"begin;"}\n#{<<~"end;"}") + begin; + assert_syntax_error("def f r:def d; def f 0end", /unexpected/) + end; + + assert_syntax_error("def\nf(000)end", /^ \^~~/) + assert_syntax_error("def\nf(&)end", /^ \^/) + end + + def test_method_location_in_rescue + bug = '[ruby-core:79388] [Bug #13181]' + obj, line = Object.new, __LINE__+1 + def obj.location + # + raise + rescue + caller_locations(1, 1)[0] + end + + assert_equal(line, obj.location.lineno, bug) + end + + def test_negative_line_number + bug = '[ruby-core:80920] [Bug #13523]' + obj = Object.new + obj.instance_eval("def t(e = false);raise if e; __LINE__;end", "test", -100) + assert_equal(-100, obj.t, bug) + assert_equal(-100, obj.method(:t).source_location[1], bug) + e = assert_raise(RuntimeError) {obj.t(true)} + assert_equal(-100, e.backtrace_locations.first.lineno, bug) + end + + def test_file_in_indented_heredoc + name = '[ruby-core:80987] [Bug #13540]' # long enough to be shared + assert_equal(name+"\n", eval("#{<<-"begin;"}\n#{<<-'end;'}", nil, name)) + begin; + <<~HEREDOC + #{__FILE__} + HEREDOC + end; + end + + def test_unexpected_token_error + assert_raise(SyntaxError) do + eval('"x"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx') + end + end + + def test_unexpected_token_after_numeric + assert_raise_with_message(SyntaxError, /^ \^~~\z/) do + eval('0000xyz') + end + assert_raise_with_message(SyntaxError, /^ \^~~\z/) do + eval('1.2i1.1') + end + end + + def test_truncated_source_line + e = assert_raise_with_message(SyntaxError, /unexpected tIDENTIFIER/) do + eval("'0123456789012345678901234567890123456789' abcdefghijklmnopqrstuvwxyz0123456789 0123456789012345678901234567890123456789") + end + line = e.message.lines[1] + assert_operator(line, :start_with?, "...") + assert_operator(line, :end_with?, "...\n") + end + + def test_unterminated_regexp_error + e = assert_raise(SyntaxError) do + eval("/x") + end.message + assert_match(/unterminated regexp meets end of file/, e) + assert_not_match(/unexpected tSTRING_END/, e) + end + + def test_lparenarg + o = Struct.new(:x).new + def o.i(x) + self.x = x + end + o.instance_eval {i (-1.3).abs} + assert_equal(1.3, o.x) + o.instance_eval {i = 0; i (-1.3).abs} + assert_equal(1.3, o.x) + end + + def test_serial_comparison + assert_warning(/comparison '<' after/) do + $VERBOSE = true + x = 1 + eval("if false; 0 < x < 2; end") + end + end + + def test_eof_in_def + assert_raise(SyntaxError) { eval("def m\n\0""end") } + assert_raise(SyntaxError) { eval("def m\n\C-d""end") } + assert_raise(SyntaxError) { eval("def m\n\C-z""end") } + end + + def test_void_value_in_command_rhs + w = "void value expression" + ex = assert_syntax_error("x = return 1", w) + assert_equal(1, ex.message.scan(w).size, "same #{w.inspect} warning should be just once") + end + +=begin + def test_past_scope_variable + assert_warning(/past scope/) {catch {|tag| eval("BEGIN{throw tag}; tap {a = 1}; a")}} + end +=end +end |
