summaryrefslogtreecommitdiff
path: root/test/ruby/test_syntax.rb
diff options
context:
space:
mode:
Diffstat (limited to 'test/ruby/test_syntax.rb')
-rw-r--r--test/ruby/test_syntax.rb1207
1 files changed, 1084 insertions, 123 deletions
diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb
index c5c3737b30..b355128a73 100644
--- a/test/ruby/test_syntax.rb
+++ b/test/ruby/test_syntax.rb
@@ -13,8 +13,7 @@ class TestSyntax < Test::Unit::TestCase
def assert_syntax_files(test)
srcdir = File.expand_path("../../..", __FILE__)
srcdir = File.join(srcdir, test)
- assert_separately(%W[--disable-gem - #{srcdir}],
- __FILE__, __LINE__, <<-'eom', timeout: Float::INFINITY)
+ assert_separately(%W[- #{srcdir}], __FILE__, __LINE__, <<-'eom', timeout: Float::INFINITY)
dir = ARGV.shift
for script in Dir["#{dir}/**/*.rb"].sort
assert_valid_syntax(IO::read(script), script)
@@ -66,6 +65,143 @@ class TestSyntax < Test::Unit::TestCase
f&.close!
end
+ def test_script_lines_encoding
+ require 'tmpdir'
+ Dir.mktmpdir do |dir|
+ File.write(File.join(dir, "script_lines.rb"), "SCRIPT_LINES__ = {}\n")
+ assert_in_out_err(%w"-r./script_lines -w -Ke", "puts __ENCODING__.name",
+ %w"EUC-JP", /-K is specified/, chdir: dir)
+ end
+ end
+
+ def test_anonymous_block_forwarding
+ assert_syntax_error("def b; c(&); end", /no anonymous block parameter/)
+ assert_syntax_error("def b(&) ->(&) {c(&)} end", /anonymous block parameter is also used/)
+ assert_valid_syntax("def b(&) ->() {c(&)} end")
+ assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
+ def b(&); c(&) end
+ def c(&); yield 1 end
+ a = nil
+ b{|c| a = c}
+ assert_equal(1, a)
+
+ def inner
+ yield
+ end
+
+ def block_only(&)
+ inner(&)
+ end
+ assert_equal(1, block_only{1})
+
+ def pos(arg1, &)
+ inner(&)
+ end
+ assert_equal(2, pos(nil){2})
+
+ def pos_kwrest(arg1, **kw, &)
+ inner(&)
+ end
+ assert_equal(3, pos_kwrest(nil){3})
+
+ def no_kw(arg1, **nil, &)
+ inner(&)
+ end
+ assert_equal(4, no_kw(nil){4})
+
+ def rest_kw(*a, kwarg: 1, &)
+ inner(&)
+ end
+ assert_equal(5, rest_kw{5})
+
+ def kw(kwarg:1, &)
+ inner(&)
+ end
+ assert_equal(6, kw{6})
+
+ def pos_kw_kwrest(arg1, kwarg:1, **kw, &)
+ inner(&)
+ end
+ assert_equal(7, pos_kw_kwrest(nil){7})
+
+ def pos_rkw(arg1, kwarg1:, &)
+ inner(&)
+ end
+ assert_equal(8, pos_rkw(nil, kwarg1: nil){8})
+
+ def all(arg1, arg2, *rest, post1, post2, kw1: 1, kw2: 2, okw1:, okw2:, &)
+ inner(&)
+ end
+ assert_equal(9, all(nil, nil, nil, nil, okw1: nil, okw2: nil){9})
+
+ def all_kwrest(arg1, arg2, *rest, post1, post2, kw1: 1, kw2: 2, okw1:, okw2:, **kw, &)
+ inner(&)
+ end
+ assert_equal(10, all_kwrest(nil, nil, nil, nil, okw1: nil, okw2: nil){10})
+
+ def evaled(&)
+ eval("inner(&)")
+ end
+ assert_equal(1, evaled{1})
+ end;
+ end
+
+ def test_anonymous_rest_forwarding
+ assert_syntax_error("def b; c(*); end", /no anonymous rest parameter/)
+ assert_syntax_error("def b; c(1, *); end", /no anonymous rest parameter/)
+ assert_syntax_error("def b(*) ->(*) {c(*)} end", /anonymous rest parameter is also used/)
+ assert_syntax_error("def b(a, *) ->(*) {c(1, *)} end", /anonymous rest parameter is also used/)
+ assert_syntax_error("def b(*) ->(a, *) {c(*)} end", /anonymous rest parameter is also used/)
+ assert_valid_syntax("def b(*) ->() {c(*)} end")
+ assert_valid_syntax("def b(a, *) ->() {c(1, *)} end")
+ assert_valid_syntax("def b(*) ->(a) {c(*)} end")
+ assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
+ def b(*); c(*) end
+ def c(*a); a end
+ def d(*); b(*, *) end
+ def e(*); eval("b(*)") end
+ assert_equal([1, 2], b(1, 2))
+ assert_equal([1, 2, 1, 2], d(1, 2))
+ assert_equal([1, 2], e(1, 2))
+ end;
+ end
+
+ def test_anonymous_keyword_rest_forwarding
+ assert_syntax_error("def b; c(**); end", /no anonymous keyword rest parameter/)
+ assert_syntax_error("def b; c(k: 1, **); end", /no anonymous keyword rest parameter/)
+ assert_syntax_error("def b(**) ->(**) {c(**)} end", /anonymous keyword rest parameter is also used/)
+ assert_syntax_error("def b(k:, **) ->(**) {c(k: 1, **)} end", /anonymous keyword rest parameter is also used/)
+ assert_syntax_error("def b(**) ->(k:, **) {c(**)} end", /anonymous keyword rest parameter is also used/)
+ assert_valid_syntax("def b(**) ->() {c(**)} end")
+ assert_valid_syntax("def b(k:, **) ->() {c(k: 1, **)} end")
+ assert_valid_syntax("def b(**) ->(k:) {c(**)} end")
+ assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
+ begin;
+ def b(**); c(**) end
+ def c(**kw); kw end
+ def d(**); b(k: 1, **) end
+ def e(**); b(**, k: 1) end
+ def f(a: nil, **); b(**) end
+ def g(**); eval("b(**)") end
+ assert_equal({a: 1, k: 3}, b(a: 1, k: 3))
+ assert_equal({a: 1, k: 3}, d(a: 1, k: 3))
+ assert_equal({a: 1, k: 1}, e(a: 1, k: 3))
+ assert_equal({k: 3}, f(a: 1, k: 3))
+ assert_equal({a: 1, k: 3}, g(a: 1, k: 3))
+ end;
+ end
+
+ def test_argument_forwarding_with_anon_rest_kwrest_and_block
+ assert_syntax_error("def f(*, **, &); g(...); end", /unexpected \.\.\./)
+ assert_syntax_error("def f(...); g(*); end", /no anonymous rest parameter/)
+ assert_syntax_error("def f(...); g(0, *); end", /no anonymous rest parameter/)
+ assert_syntax_error("def f(...); g(**); end", /no anonymous keyword rest parameter/)
+ assert_syntax_error("def f(...); g(x: 1, **); end", /no anonymous keyword rest parameter/)
+ assert_syntax_error("def f(...); g(&); end", /no anonymous block parameter/)
+ end
+
def test_newline_in_block_parameters
bug = '[ruby-dev:45292]'
["", "a", "a, b"].product(["", ";x", [";", "x"]]) do |params|
@@ -81,7 +217,7 @@ class TestSyntax < Test::Unit::TestCase
blocks = [['do end', 'do'], ['{}', 'brace']],
*|
[%w'. dot', %w':: colon'].product(methods, blocks) do |(c, n1), (m, n2), (b, n3)|
- m = m.tr_s('()', ' ').strip if n2 == 'do'
+ m = m.tr_s('()', ' ').strip if n3 == 'do'
name = "test_#{n3}_block_after_blockcall_#{n1}_#{n2}_arg"
code = "#{blockcall}#{c}#{m} #{b}"
define_method(name) {assert_valid_syntax(code, bug6115)}
@@ -93,11 +229,64 @@ class TestSyntax < Test::Unit::TestCase
assert_valid_syntax("tap (proc do end)", __FILE__, bug9726)
end
- def test_methodref_literal
- assert_separately [], <<-EOS
- eval 'nil.:|;1'
- 1000.times{eval 'nil.:|;1'}
- EOS
+ def test_hash_kwsplat_hash
+ kw = {}
+ h = {a: 1}
+ assert_equal({}, {**{}})
+ assert_equal({}, {**kw})
+ assert_equal(h, {**h})
+ assert_equal(false, {**{}}.frozen?)
+ assert_equal(false, {**kw}.equal?(kw))
+ assert_equal(false, {**h}.equal?(h))
+ end
+
+ def test_array_kwsplat_hash
+ kw = {}
+ h = {a: 1}
+ a = []
+ assert_equal([], [**{}])
+ assert_equal([], [**kw])
+ assert_equal([h], [**h])
+ assert_equal([{}], [{}])
+ assert_equal([kw], [kw])
+ assert_equal([h], [h])
+
+ assert_equal([1], [1, **{}])
+ assert_equal([1], [1, **kw])
+ assert_equal([1, h], [1, **h])
+ assert_equal([1, {}], [1, {}])
+ assert_equal([1, kw], [1, kw])
+ assert_equal([1, h], [1, h])
+
+ assert_equal([], [*a, **{}])
+ assert_equal([], [*a, **kw])
+ assert_equal([h], [*a, **h])
+ assert_equal([{}], [*a, {}])
+ assert_equal([kw], [*a, kw])
+ assert_equal([h], [*a, h])
+
+ assert_equal([1], [1, *a, **{}])
+ assert_equal([1], [1, *a, **kw])
+ assert_equal([1, h], [1, *a, **h])
+ assert_equal([1, {}], [1, *a, {}])
+ assert_equal([1, kw], [1, *a, kw])
+ assert_equal([1, h], [1, *a, h])
+
+ assert_equal([], [**kw, **kw])
+ assert_equal([], [**kw, **{}, **kw])
+ assert_equal([1], [1, **kw, **{}, **kw])
+
+ assert_equal([{}], [{}, **kw, **kw])
+ assert_equal([kw], [kw, **kw, **kw])
+ assert_equal([h], [h, **kw, **kw])
+ assert_equal([h, h], [h, **kw, **kw, **h])
+
+ assert_equal([h, {:a=>2}], [h, **{}, **h, a: 2])
+ assert_equal([h, h], [h, **{}, a: 2, **h])
+ assert_equal([h, h], [h, a: 2, **{}, **h])
+ assert_equal([h, h], [h, a: 2, **h, **{}])
+ assert_equal([h, {:a=>2}], [h, **h, a: 2, **{}])
+ assert_equal([h, {:a=>2}], [h, **h, **{}, a: 2])
end
def test_normal_argument
@@ -150,29 +339,52 @@ class TestSyntax < Test::Unit::TestCase
bug10315 = '[ruby-core:65368] [Bug #10315]'
o = KW2.new
- assert_equal([23, 2], o.kw(**{k1: 22}, **{k1: 23}), bug10315)
+ begin
+ verbose_bak, $VERBOSE = $VERBOSE, nil
+ assert_equal([23, 2], eval("o.kw(**{k1: 22}, **{k1: 23})"), bug10315)
+ ensure
+ $VERBOSE = verbose_bak
+ end
h = {k3: 31}
assert_raise(ArgumentError) {o.kw(**h)}
h = {"k1"=>11, k2: 12}
- assert_raise(TypeError) {o.kw(**h)}
+ assert_raise(ArgumentError) {o.kw(**h)}
end
def test_keyword_duplicated
bug10315 = '[ruby-core:65625] [Bug #10315]'
a = []
def a.add(x) push(x); x; end
- def a.f(k:) k; end
+ b = a.clone
+ def a.f(k:, **) k; end
+ def b.f(k:) k; end
+ a.clear
+ r = nil
+ assert_warn(/duplicated/) {r = eval("b.f(k: b.add(1), k: b.add(2))")}
+ assert_equal(2, r)
+ assert_equal([1, 2], b, bug10315)
+ b.clear
+ r = nil
+ assert_warn(/duplicated/) {r = eval("a.f(k: a.add(1), j: a.add(2), k: a.add(3), k: a.add(4))")}
+ assert_equal(4, r)
+ assert_equal([1, 2, 3, 4], a)
a.clear
r = nil
- assert_warn(/duplicated/) {r = eval("a.f(k: a.add(1), k: a.add(2))")}
+ assert_warn(/duplicated/) {r = eval("b.f(**{k: b.add(1), k: b.add(2)})")}
assert_equal(2, r)
- assert_equal([1, 2], a, bug10315)
+ assert_equal([1, 2], b, bug10315)
+ b.clear
+ r = nil
+ assert_warn(/duplicated/) {r = eval("a.f(**{k: a.add(1), j: a.add(2), k: a.add(3), k: a.add(4)})")}
+ assert_equal(4, r)
+ assert_equal([1, 2, 3, 4], a)
a.clear
r = nil
- assert_warn(/duplicated/) {r = eval("a.f({k: a.add(1), k: a.add(2)})")}
+ _z = {}
+ assert_warn(/duplicated/) {r = eval("a.f(k: a.add(1), **_z, k: a.add(2))")}
assert_equal(2, r)
- assert_equal([1, 2], a, bug10315)
+ assert_equal([1, 2], a)
end
def test_keyword_empty_splat
@@ -194,33 +406,25 @@ class TestSyntax < Test::Unit::TestCase
end
def test_keyword_self_reference
- bug9593 = '[ruby-core:61299] [Bug #9593]'
- o = Object.new
- assert_warn(/circular argument reference - var/) do
- o.instance_eval("def foo(var: defined?(var)) var end")
- end
- assert_equal(42, o.foo(var: 42))
- assert_equal("local-variable", o.foo, bug9593)
-
- o = Object.new
- assert_warn(/circular argument reference - var/) do
- o.instance_eval("def foo(var: var) var end")
- end
- assert_nil(o.foo, bug9593)
+ assert_valid_syntax("def foo(var: defined?(var)) var end")
+ assert_valid_syntax("def foo(var: var) var end")
+ assert_valid_syntax("def foo(var: bar(var)) var end")
+ assert_valid_syntax("def foo(var: bar {var}) var end")
+ assert_valid_syntax("def foo(var: (1 in ^var)); end")
o = Object.new
- assert_warn(/circular argument reference - var/) do
- o.instance_eval("def foo(var: bar(var)) var end")
+ assert_warn("") do
+ o.instance_eval("def foo(var: bar {|var| var}) var end")
end
o = Object.new
- assert_warn(/circular argument reference - var/) do
- o.instance_eval("def foo(var: bar {var}) var end")
+ assert_warn("") do
+ o.instance_eval("def foo(var: bar {| | var}) var end")
end
o = Object.new
assert_warn("") do
- o.instance_eval("def foo(var: bar {|var| var}) var end")
+ o.instance_eval("def foo(var: bar {|| var}) var end")
end
o = Object.new
@@ -232,61 +436,63 @@ class TestSyntax < Test::Unit::TestCase
assert_warn("") do
o.instance_eval("proc {|var: 1| var}")
end
+
+ o = Object.new
+ assert_nil(o.instance_eval("def foo(bar: bar) = bar; foo"))
end
def test_keyword_invalid_name
bug11663 = '[ruby-core:71356] [Bug #11663]'
- o = o = Object.new
- assert_syntax_error('def o.foo(arg1?:) end', /arg1\?/, bug11663)
- assert_syntax_error('def o.foo(arg1?:, arg2:) end', /arg1\?/, bug11663)
+ assert_syntax_error('def foo(arg1?:) end', /arg1\?/, bug11663)
+ assert_syntax_error('def foo(arg1?:, arg2:) end', /arg1\?/, bug11663)
assert_syntax_error('proc {|arg1?:|}', /arg1\?/, bug11663)
assert_syntax_error('proc {|arg1?:, arg2:|}', /arg1\?/, bug11663)
bug10545 = '[ruby-dev:48742] [Bug #10545]'
- assert_syntax_error('def o.foo(FOO: a) end', /constant/, bug10545)
- assert_syntax_error('def o.foo(@foo: a) end', /instance variable/)
- assert_syntax_error('def o.foo(@@foo: a) end', /class variable/)
+ assert_syntax_error('def foo(FOO: a) end', /constant/, bug10545)
+ assert_syntax_error('def foo(@foo: a) end', /instance variable/)
+ assert_syntax_error('def foo(@@foo: a) end', /class variable/)
end
- def test_optional_self_reference
- bug9593 = '[ruby-core:61299] [Bug #9593]'
- o = Object.new
- assert_warn(/circular argument reference - var/) do
- o.instance_eval("def foo(var = defined?(var)) var end")
- end
- assert_equal(42, o.foo(42))
- assert_equal("local-variable", o.foo, bug9593)
-
- o = Object.new
- assert_warn(/circular argument reference - var/) do
- o.instance_eval("def foo(var = var) var end")
- end
- assert_nil(o.foo, bug9593)
+ def test_keywords_specified_and_not_accepted
+ assert_syntax_error('def foo(a:, **nil) end', /unexpected/)
+ assert_syntax_error('def foo(a:, **nil, &b) end', /unexpected/)
+ assert_syntax_error('def foo(**a, **nil) end', /unexpected/)
+ assert_syntax_error('def foo(**a, **nil, &b) end', /unexpected/)
+ assert_syntax_error('def foo(**nil, **a) end', /unexpected/)
+ assert_syntax_error('def foo(**nil, **a, &b) end', /unexpected/)
- o = Object.new
- assert_warn(/circular argument reference - var/) do
- o.instance_eval("def foo(var = bar(var)) var end")
- end
+ assert_syntax_error('proc do |a:, **nil| end', /unexpected/)
+ assert_syntax_error('proc do |a:, **nil, &b| end', /unexpected/)
+ assert_syntax_error('proc do |**a, **nil| end', /unexpected/)
+ assert_syntax_error('proc do |**a, **nil, &b| end', /unexpected/)
+ assert_syntax_error('proc do |**nil, **a| end', /unexpected/)
+ assert_syntax_error('proc do |**nil, **a, &b| end', /unexpected/)
+ end
- o = Object.new
- assert_warn(/circular argument reference - var/) do
- o.instance_eval("def foo(var = bar {var}) var end")
- end
+ def test_optional_self_reference
+ assert_valid_syntax("def foo(var = defined?(var)) var end")
+ assert_valid_syntax("def foo(var = var) var end")
+ assert_valid_syntax("def foo(var = bar(var)) var end")
+ assert_valid_syntax("def foo(var = bar {var}) var end")
+ assert_valid_syntax("def foo(var = (def bar;end; var)) var end")
+ assert_valid_syntax("def foo(var = (def self.bar;end; var)) var end")
+ assert_valid_syntax("def foo(var = (1 in ^var)); end")
o = Object.new
- assert_warn(/circular argument reference - var/) do
- o.instance_eval("def foo(var = (def bar;end; var)) var end")
+ assert_warn("") do
+ o.instance_eval("def foo(var = bar {|var| var}) var end")
end
o = Object.new
- assert_warn(/circular argument reference - var/) do
- o.instance_eval("def foo(var = (def self.bar;end; var)) var end")
+ assert_warn("") do
+ o.instance_eval("def foo(var = bar {| | var}) var end")
end
o = Object.new
assert_warn("") do
- o.instance_eval("def foo(var = bar {|var| var}) var end")
+ o.instance_eval("def foo(var = bar {|| var}) var end")
end
o = Object.new
@@ -298,6 +504,9 @@ class TestSyntax < Test::Unit::TestCase
assert_warn("") do
o.instance_eval("proc {|var = 1| var}")
end
+
+ o = Object.new
+ assert_nil(o.instance_eval("def foo(bar: bar) = bar; foo"))
end
def test_warn_grouped_expression
@@ -315,10 +524,6 @@ class TestSyntax < Test::Unit::TestCase
end
def test_warn_balanced
- warning = <<WARN
-test:1: warning: `%s' after local variable or literal is interpreted as binary operator
-test:1: warning: even though it seems like %s
-WARN
[
[:**, "argument prefix"],
[:*, "argument prefix"],
@@ -332,7 +537,9 @@ WARN
all_assertions do |a|
["puts 1 #{op}0", "puts :a #{op}0", "m = 1; puts m #{op}0"].each do |src|
a.for(src) do
- assert_warning(warning % [op, syn], src) do
+ warning = /'#{Regexp.escape(op)}' after local variable or literal is interpreted as binary operator.+?even though it seems like #{syn}/m
+
+ assert_warning(warning, src) do
assert_valid_syntax(src, "test", verbose: true)
end
end
@@ -483,6 +690,8 @@ WARN
assert_equal(42, obj.foo(42))
assert_equal(42, obj.foo(2, _: 0))
assert_equal(2, obj.foo(x: 2, _: 0))
+ ensure
+ self.class.remove_method(:foo)
end
def test_duplicated_opt_kw
@@ -522,8 +731,8 @@ WARN
end
def test_duplicated_when
- w = 'warning: duplicated `when\' clause with line 3 is ignored'
- assert_warning(/3: #{w}.+4: #{w}.+4: #{w}.+5: #{w}.+5: #{w}/m) {
+ w = ->(line) { "warning: 'when' clause on line #{line} duplicates 'when' clause on line 3 and is ignored" }
+ assert_warning(/#{w[3]}.+#{w[4]}.+#{w[4]}.+#{w[5]}.+#{w[5]}/m) {
eval %q{
case 1
when 1, 1
@@ -532,7 +741,7 @@ WARN
end
}
}
- assert_warning(/#{w}/) {#/3: #{w}.+4: #{w}.+5: #{w}.+5: #{w}/m){
+ assert_warning(/#{w[3]}.+#{w[4]}.+#{w[5]}.+#{w[5]}/m) {
a = a = 1
eval %q{
case 1
@@ -542,10 +751,28 @@ WARN
end
}
}
+ assert_warning(/#{w[3]}/) {
+ eval %q{
+ case 1
+ when __LINE__, __LINE__
+ when 3, 3
+ when 3, 3
+ end
+ }
+ }
+ assert_warning(/#{w[3]}/) {
+ eval %q{
+ case 1
+ when __FILE__, __FILE__
+ when "filename", "filename"
+ when "filename", "filename"
+ end
+ }, binding, "filename"
+ }
end
def test_duplicated_when_check_option
- w = /duplicated `when\' clause with line 3 is ignored/
+ w = /'when' clause on line 4 duplicates 'when' clause on line 3 and is ignored/
assert_in_out_err(%[-wc], "#{<<~"begin;"}\n#{<<~'end;'}", ["Syntax OK"], w)
begin;
case 1
@@ -590,6 +817,11 @@ WARN
def test_do_block_after_lambda
bug11380 = '[ruby-core:70067] [Bug #11380]'
assert_valid_syntax('p -> { :hello }, a: 1 do end', bug11380)
+
+ assert_valid_syntax('->(opt = (foo.[] bar)) {}')
+ assert_valid_syntax('->(opt = (foo.[]= bar)) {}')
+ assert_valid_syntax('->(opt = (foo.[] bar)) do end')
+ assert_valid_syntax('->(opt = (foo.[]= bar)) do end')
end
def test_reserved_method_no_args
@@ -600,7 +832,7 @@ WARN
def test_unassignable
gvar = global_variables
%w[self nil true false __FILE__ __LINE__ __ENCODING__].each do |kwd|
- assert_raise(SyntaxError) {eval("#{kwd} = nil")}
+ assert_syntax_error("#{kwd} = nil", /Can't .* #{kwd}$/)
assert_equal(gvar, global_variables)
end
end
@@ -673,6 +905,16 @@ e"
assert_dedented_heredoc(expect, result)
end
+ def test_dedented_heredoc_with_leading_blank_line
+ # the blank line has six leading spaces
+ result = " \n" \
+ " b\n"
+ expect = " \n" \
+ "b\n"
+ assert_dedented_heredoc(expect, result)
+ end
+
+
def test_dedented_heredoc_with_blank_more_indented_line_escaped
result = " a\n" \
"\\ \\ \\ \\ \\ \\ \n" \
@@ -780,7 +1022,7 @@ eom
end
def test_dedented_heredoc_concatenation
- assert_equal("\n0\n1", eval("<<~0 '1'\n \n0\#{}\n0"))
+ assert_equal(" \n0\n1", eval("<<~0 '1'\n \n0\#{}\n0"))
end
def test_heredoc_mixed_encoding
@@ -819,6 +1061,14 @@ eom
assert_not_match(/end-of-input/, e.message)
end
+ def test_invalid_regexp
+ bug20295 = '[ruby-core:116913] [Bug #20295]'
+
+ assert_syntax_error("/[/=~s", /premature end of char-class/, bug20295)
+ assert_syntax_error("/(?<>)/=~s", /group name is empty/, bug20295)
+ assert_syntax_error("/(?<a>[)/=~s", /premature end of char-class/, bug20295)
+ end
+
def test_lineno_operation_brace_block
expected = __LINE__ + 1
actual = caller_lineno\
@@ -831,7 +1081,7 @@ eom
["p ", ""], # no-pop
["", "p Foo::Bar"], # pop
].each do |p1, p2|
- src = <<-EOM.gsub(/^\s*\n/, '')
+ src = <<~EOM
class Foo
#{"Bar = " + preset if preset}
end
@@ -863,7 +1113,7 @@ eom
["p ", ""], # no-pop
["", "p ::Bar"], # pop
].each do |p1, p2|
- src = <<-EOM.gsub(/^\s*\n/, '')
+ src = <<~EOM
#{"Bar = " + preset if preset}
class Foo
#{p1}::Bar #{op}= 42
@@ -931,9 +1181,14 @@ eom
def test_warning_for_cr
feature8699 = '[ruby-core:56240] [Feature #8699]'
- assert_warning(/encountered \\r/, feature8699) do
- eval("\r""__id__\r")
+ s = assert_warning(/encountered \\r/, feature8699) do
+ eval("'\r'\r")
+ end
+ assert_equal("\r", s)
+ s = assert_warning('') do
+ eval("'\r'\r\n")
end
+ assert_equal("\r", s)
end
def test_unexpected_fraction
@@ -959,6 +1214,26 @@ eom
assert_syntax_error('0...%q.', /unterminated string/, bug10957)
end
+ def test_range_at_eol
+ assert_warn(/\.\.\. at EOL/) {eval("1...\n2")}
+ assert_warn('') {eval("(1...)")}
+ assert_warn('') {eval("(1...\n2)")}
+ assert_warn('') {eval("{a: 1...\n2}")}
+
+ assert_warn(/\.\.\. at EOL/) do
+ assert_valid_syntax('foo.[]= ...', verbose: true)
+ end
+ assert_warn(/\.\.\. at EOL/) do
+ assert_valid_syntax('foo.[] ...', verbose: true)
+ end
+ assert_warn(/\.\.\. at EOL/) do
+ assert_syntax_error('foo.[]= bar, ...', /unexpected/, verbose: true)
+ end
+ assert_warn(/\.\.\. at EOL/) do
+ assert_syntax_error('foo.[] bar, ...', /unexpected/, verbose: true)
+ end
+ end
+
def test_too_big_nth_ref
bug11192 = '[ruby-core:69393] [Bug #11192]'
assert_warn(/too big/, bug11192) do
@@ -980,7 +1255,54 @@ eom
def test_fluent_dot
assert_valid_syntax("a\n.foo")
assert_valid_syntax("a\n&.foo")
- assert_valid_syntax("a\n.:foo")
+ assert_valid_syntax("a #\n#\n.foo\n")
+ assert_valid_syntax("a #\n#\n&.foo\n")
+ end
+
+ def test_fluent_and
+ assert_valid_syntax("a\n" "&& foo")
+ assert_valid_syntax("a\n" "and foo")
+
+ assert_equal(:ok, eval("#{<<~"begin;"}\n#{<<~'end;'}"))
+ begin;
+ a = true
+ if a
+ && (a = :ok; true)
+ a
+ end
+ end;
+
+ assert_equal(:ok, eval("#{<<~"begin;"}\n#{<<~'end;'}"))
+ begin;
+ a = true
+ if a
+ and (a = :ok; true)
+ a
+ end
+ end;
+ end
+
+ def test_fluent_or
+ assert_valid_syntax("a\n" "|| foo")
+ assert_valid_syntax("a\n" "or foo")
+
+ assert_equal(:ok, eval("#{<<~"begin;"}\n#{<<~'end;'}"))
+ begin;
+ a = false
+ if a
+ || (a = :ok; true)
+ a
+ end
+ end;
+
+ assert_equal(:ok, eval("#{<<~"begin;"}\n#{<<~'end;'}"))
+ begin;
+ a = false
+ if a
+ or (a = :ok; true)
+ a
+ end
+ end;
end
def test_safe_call_in_massign_lhs
@@ -988,6 +1310,20 @@ eom
assert_syntax_error("a&.x,=0", /multiple assignment destination/)
end
+ def test_safe_call_in_for_variable
+ assert_valid_syntax("for x&.bar in []; end")
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
+ begin;
+ foo = nil
+ for foo&.bar in [1]; end
+ assert_nil(foo)
+
+ foo = Struct.new(:bar).new
+ for foo&.bar in [1]; end
+ assert_equal(1, foo.bar)
+ end;
+ end
+
def test_no_warning_logop_literal
assert_warning("") do
eval("true||raise;nil")
@@ -1001,22 +1337,25 @@ eom
end
def test_warning_literal_in_condition
- assert_warn(/literal in condition/) do
+ assert_warn(/string literal in condition/) do
eval('1 if ""')
end
- assert_warn(/literal in condition/) do
- eval('1 if //')
+ assert_warning(/string literal in condition/) do
+ eval('1 if __FILE__')
end
- assert_warn(/literal in condition/) do
- eval('1 if true..false')
+ assert_warn(/regex literal in condition/) do
+ eval('1 if //')
end
assert_warning(/literal in condition/) do
eval('1 if 1')
end
assert_warning(/literal in condition/) do
+ eval('1 if __LINE__')
+ end
+ assert_warning(/symbol literal in condition/) do
eval('1 if :foo')
end
- assert_warning(/literal in condition/) do
+ assert_warning(/symbol literal in condition/) do
eval('1 if :"#{"foo".upcase}"')
end
@@ -1040,6 +1379,27 @@ eom
end
end
+ def test_warning_literal_in_flip_flop
+ assert_warn(/literal in flip-flop/) do
+ eval('1 if ""..false')
+ end
+ assert_warning(/literal in flip-flop/) do
+ eval('1 if :foo..false')
+ end
+ assert_warning(/literal in flip-flop/) do
+ eval('1 if :"#{"foo".upcase}"..false')
+ end
+ assert_warn(/literal in flip-flop/) do
+ eval('1 if ""...false')
+ end
+ assert_warning(/literal in flip-flop/) do
+ eval('1 if :foo...false')
+ end
+ assert_warning(/literal in flip-flop/) do
+ eval('1 if :"#{"foo".upcase}"...false')
+ end
+ end
+
def test_alias_symbol
bug8851 = '[ruby-dev:47681] [Bug #8851]'
formats = ['%s', ":'%s'", ':"%s"', '%%s(%s)']
@@ -1065,7 +1425,7 @@ eom
end
def test_parenthesised_statement_argument
- assert_syntax_error("foo(bar rescue nil)", /unexpected `rescue' modifier/)
+ assert_syntax_error("foo(bar rescue nil)", /unexpected 'rescue' modifier/)
assert_valid_syntax("foo (bar rescue nil)")
end
@@ -1087,7 +1447,7 @@ eom
def test_block_after_cmdarg_in_paren
bug11873 = '[ruby-core:72482] [Bug #11873]'
- def bug11873.p(*);end;
+ def bug11873.p(*, &);end;
assert_raise(LocalJumpError, bug11873) do
bug11873.instance_eval do
@@ -1115,6 +1475,25 @@ eom
assert_valid_syntax 'p :foo, {proc do end => proc do end, b: proc do end}', bug13073
end
+ def test_invalid_encoding_symbol
+ assert_syntax_error('{"\xC3": 1}', "invalid symbol")
+ end
+
+ def test_invalid_symbol_in_hash_memory_leak
+ assert_no_memory_leak([], "#{<<-'begin;'}", "#{<<-'end;'}", rss: true)
+ str = '{"\xC3": 1}'.force_encoding("UTF-8")
+ code = proc do
+ eval(str)
+ raise "unreachable"
+ rescue SyntaxError
+ end
+
+ 1_000.times(&code)
+ begin;
+ 1_000_000.times(&code)
+ end;
+ end
+
def test_do_after_local_variable
obj = Object.new
def obj.m; yield; end
@@ -1163,9 +1542,10 @@ eom
"#{return}"
raise((return; "should not raise"))
begin raise; ensure return; end; self
- begin raise; ensure return; end and self
nil&defined?0--begin e=no_method_error(); return; 0;end
return puts('ignored') #=> ignored
+ BEGIN {return}
+ END {return if false}
end;
.split(/\n/).map {|s|[(line+=1), *s.split(/#=> /, 2)]}
failed = proc do |n, s|
@@ -1195,6 +1575,76 @@ eom
end
end
+ def test_eval_return_toplevel
+ feature4840 = '[ruby-core:36785] [Feature #4840]'
+ line = __LINE__+2
+ code = "#{<<~"begin;"}#{<<~'end;'}"
+ begin;
+ eval "return"; raise
+ begin eval "return"; rescue SystemExit; exit false; end
+ begin eval "return"; ensure puts "ensured"; end #=> ensured
+ begin ensure eval "return"; end
+ begin raise; ensure; eval "return"; end
+ begin raise; rescue; eval "return"; end
+ eval "return false"; raise
+ eval "return 1"; raise
+ "#{eval "return"}"
+ raise((eval "return"; "should not raise"))
+ begin raise; ensure eval "return"; end; self
+ begin raise; ensure eval "return"; end and self
+ eval "return puts('ignored')" #=> ignored
+ BEGIN {eval "return"}
+ end;
+ .split(/\n/).map {|s|[(line+=1), *s.split(/#=> /, 2)]}
+ failed = proc do |n, s|
+ RubyVM::InstructionSequence.compile(s, __FILE__, nil, n).disasm
+ end
+ Tempfile.create(%w"test_return_ .rb") do |lib|
+ lib.close
+ args = %W[-W0 -r#{lib.path}]
+ all_assertions_foreach(feature4840, *[:main, :lib].product([:class, :top], code)) do |main, klass, (n, s, *ex)|
+ if klass == :class
+ s = "class X; #{s}; end"
+ if main == :main
+ assert_in_out_err(%[-W0], s, ex, /return/, proc {failed[n, s]}, success: false)
+ else
+ File.write(lib, s)
+ assert_in_out_err(args, "", ex, /return/, proc {failed[n, s]}, success: false)
+ end
+ else
+ if main == :main
+ assert_in_out_err(%[-W0], s, ex, [], proc {failed[n, s]}, success: true)
+ else
+ File.write(lib, s)
+ assert_in_out_err(args, "", ex, [], proc {failed[n, s]}, success: true)
+ end
+ end
+ end
+ end
+ end
+
+ def test_return_toplevel_with_argument
+ assert_warn(/argument of top-level return is ignored/) {eval("return 1")}
+ end
+
+ def test_return_in_proc_in_class
+ assert_in_out_err(['-e', 'class TestSyntax; proc{ return }.call; end'], "", [], /^-e:1:.*unexpected return \(LocalJumpError\)/)
+ end
+
+ def test_return_in_END
+ assert_normal_exit('END {return}')
+ end
+
+ def test_return_in_BEGIN_in_eval
+ # `BEGIN` in `eval` is allowed, even inside a method, and `return`
+ # from that block exits from that method without `LocalJumpError`.
+ obj = Object.new
+ def obj.ok
+ eval("BEGIN {return :ok}")
+ end
+ assert_equal :ok, assert_nothing_raised(LocalJumpError) {obj.ok}
+ end
+
def test_syntax_error_in_rescue
bug12613 = '[ruby-core:76531] [Bug #12613]'
assert_syntax_error("#{<<-"begin;"}\n#{<<-"end;"}", /Invalid retry/, bug12613)
@@ -1213,13 +1663,13 @@ eom
end
def test_syntax_error_at_newline
- expected = "\n ^"
+ expected = /(\n|\| ) \^/
assert_syntax_error("%[abcdef", expected)
assert_syntax_error("%[abcdef\n", expected)
end
def test_invalid_jump
- assert_in_out_err(%w[-e redo], "", [], /^-e:1: /)
+ assert_in_out_err(%w[-e redo], "", [], /^-e:1: |~ Invalid redo/)
end
def test_keyword_not_parens
@@ -1320,6 +1770,15 @@ eom
assert_nil obj.test
end
+ def test_assignment_return_in_loop
+ obj = Object.new
+ def obj.test
+ x = nil
+ _y = (return until x unless x)
+ end
+ assert_nil obj.test, "[Bug #16695]"
+ end
+
def test_method_call_location
line = __LINE__+5
e = assert_raise(NoMethodError) do
@@ -1346,6 +1805,49 @@ eom
assert_equal(line, e.backtrace_locations[0].lineno)
end
+ def test_methoddef_endless
+ assert_valid_syntax('private def foo = 42')
+ assert_valid_syntax('private def foo() = 42')
+ assert_valid_syntax('private def inc(x) = x + 1')
+ assert_valid_syntax('private def obj.foo = 42')
+ assert_valid_syntax('private def obj.foo() = 42')
+ assert_valid_syntax('private def obj.inc(x) = x + 1')
+ k = Class.new do
+ class_eval('def rescued(x) = raise("to be caught") rescue "instance #{x}"')
+ class_eval('def self.rescued(x) = raise("to be caught") rescue "class #{x}"')
+ end
+ assert_equal("class ok", k.rescued("ok"))
+ assert_equal("instance ok", k.new.rescued("ok"))
+
+ error = /setter method cannot be defined in an endless method definition/
+ assert_syntax_error('def foo=() = 42', error)
+ assert_syntax_error('def obj.foo=() = 42', error)
+ assert_syntax_error('def foo=() = 42 rescue nil', error)
+ assert_syntax_error('def obj.foo=() = 42 rescue nil', error)
+ end
+
+ def test_methoddef_endless_command
+ assert_valid_syntax('def foo = puts "Hello"')
+ assert_valid_syntax('def foo() = puts "Hello"')
+ assert_valid_syntax('def foo(x) = puts x')
+ assert_valid_syntax('def obj.foo = puts "Hello"')
+ assert_valid_syntax('def obj.foo() = puts "Hello"')
+ assert_valid_syntax('def obj.foo(x) = puts x')
+ k = Class.new do
+ class_eval('def rescued(x) = raise "to be caught" rescue "instance #{x}"')
+ class_eval('def self.rescued(x) = raise "to be caught" rescue "class #{x}"')
+ end
+ assert_equal("class ok", k.rescued("ok"))
+ assert_equal("instance ok", k.new.rescued("ok"))
+
+ assert_valid_syntax('private def foo = puts "Hello"')
+ assert_valid_syntax('private def foo() = puts "Hello"')
+ assert_valid_syntax('private def foo(x) = puts x')
+ assert_valid_syntax('private def obj.foo = puts "Hello"')
+ assert_valid_syntax('private def obj.foo() = puts "Hello"')
+ assert_valid_syntax('private def obj.foo(x) = puts x')
+ end
+
def test_methoddef_in_cond
assert_valid_syntax('while def foo; tap do end; end; break; end')
assert_valid_syntax('while def foo a = tap do end; end; break; end')
@@ -1359,38 +1861,156 @@ eom
def test_command_with_cmd_brace_block
assert_valid_syntax('obj.foo (1) {}')
assert_valid_syntax('obj::foo (1) {}')
+ assert_valid_syntax('bar {}')
+ assert_valid_syntax('Bar {}')
+ assert_valid_syntax('bar() {}')
+ assert_valid_syntax('Bar() {}')
+ assert_valid_syntax('Foo::bar {}')
+ assert_valid_syntax('Foo::Bar {}')
+ assert_valid_syntax('Foo::bar() {}')
+ assert_valid_syntax('Foo::Bar() {}')
+ end
+
+ def test_command_newline_in_tlparen_args
+ assert_valid_syntax("p (1\n2\n),(3),(4)")
+ assert_valid_syntax("p (\n),(),()")
+ assert_valid_syntax("a.b (1\n2\n),(3),(4)")
+ assert_valid_syntax("a.b (\n),(),()")
+ end
+
+ def test_command_semicolon_in_tlparen_at_the_first_arg
+ bug19281 = '[ruby-core:111499] [Bug #19281]'
+ assert_valid_syntax('p (1;2),(3),(4)', bug19281)
+ assert_valid_syntax('p (;),(),()', bug19281)
+ assert_valid_syntax('a.b (1;2),(3),(4)', bug19281)
+ assert_valid_syntax('a.b (;),(),()', bug19281)
+ end
+
+ def test_command_do_block_call_with_empty_args_brace_block
+ assert_valid_syntax('cmd 1, 2 do end.m() { blk_body }')
end
def test_numbered_parameter
- assert_valid_syntax('proc {@1}')
- assert_equal(3, eval('[1,2].then {@1+@2}'))
- assert_equal("12", eval('[1,2].then {"#@1#@2"}'))
- assert_equal(3, eval('->{@1+@2}.call(1,2)'))
- assert_equal(4, eval('->(a=->{@1}){a}.call.call(4)'))
- assert_equal(5, eval('-> a: ->{@1} {a}.call.call(5)'))
- assert_syntax_error('proc {|| @1}', /ordinary parameter is defined/)
- assert_syntax_error('proc {|;a| @1}', /ordinary parameter is defined/)
- assert_syntax_error("proc {|\n| @1}", /ordinary parameter is defined/)
- assert_syntax_error('proc {|x| @1}', /ordinary parameter is defined/)
- assert_syntax_error('->(){@1}', /ordinary parameter is defined/)
- assert_syntax_error('->(x){@1}', /ordinary parameter is defined/)
- assert_syntax_error('->x{@1}', /ordinary parameter is defined/)
- assert_syntax_error('->x:@2{}', /ordinary parameter is defined/)
- assert_syntax_error('->x=@1{}', /ordinary parameter is defined/)
- assert_syntax_error('proc {@1 = nil}', /Can't assign to numbered parameter @1/)
- assert_syntax_error('proc {@01}', /leading zero/)
- assert_syntax_error('proc {@1_}', /unexpected/)
- assert_syntax_error('proc {@9999999999999999}', /too large/)
- assert_syntax_error('@1', /outside block/)
- end
-
- def test_pipeline_operator
- assert_valid_syntax('x |> y')
- x = nil
- assert_equal("121", eval('x = 12 |> pow(2) |> to_s 11'))
- assert_equal(12, x)
- assert_equal([2, 4, 6], eval("1.. |> take 3\n|> map do @1 * 2 end"))
- assert_syntax_error('a|>-b', /unexpected '-'/)
+ assert_valid_syntax('proc {_1}')
+ assert_equal(3, eval('[1,2].then {_1+_2}'))
+ assert_equal("12", eval('[1,2].then {"#{_1}#{_2}"}'))
+ assert_equal([1, 2], eval('[1,2].then {_1}'))
+ assert_equal(3, eval('->{_1+_2}.call(1,2)'))
+ assert_equal(4, eval('->(a=->{_1}){a}.call.call(4)'))
+ assert_equal(5, eval('-> a: ->{_1} {a}.call.call(5)'))
+ assert_syntax_error('proc {|| _1}', /ordinary parameter is defined/)
+ assert_syntax_error('proc {|;a| _1}', /ordinary parameter is defined/)
+ assert_syntax_error("proc {|\n| _1}", /ordinary parameter is defined/)
+ assert_syntax_error('proc {|x| _1}', /ordinary parameter is defined/)
+ assert_syntax_error('proc {_1; proc {_2}}', /numbered parameter is already used/)
+ assert_syntax_error('proc {proc {_1}; _2}', /numbered parameter is already used/)
+ assert_syntax_error('->(){_1}', /ordinary parameter is defined/)
+ assert_syntax_error('->(x){_1}', /ordinary parameter is defined/)
+ assert_syntax_error('->x{_1}', /ordinary parameter is defined/)
+ assert_syntax_error('->x:_2{}', /ordinary parameter is defined/)
+ assert_syntax_error('->x=_1{}', /ordinary parameter is defined/)
+ assert_syntax_error('-> {_1; -> {_2}}', /numbered parameter is already used/)
+ assert_syntax_error('-> {-> {_1}; _2}', /numbered parameter is already used/)
+ assert_syntax_error('proc {_1; _1 = nil}', /Can't assign to numbered parameter _1/)
+ assert_syntax_error('proc {_1 = nil}', /_1 is reserved for numbered parameter/)
+ assert_syntax_error('_2=1', /_2 is reserved for numbered parameter/)
+ assert_syntax_error('proc {|_3|}', /_3 is reserved for numbered parameter/)
+ assert_syntax_error('def x(_4) end', /_4 is reserved for numbered parameter/)
+ assert_syntax_error('def _5; end', /_5 is reserved for numbered parameter/)
+ assert_syntax_error('def self._6; end', /_6 is reserved for numbered parameter/)
+ assert_raise_with_message(NameError, /undefined local variable or method '_1'/) {
+ eval('_1')
+ }
+ ['class C', 'class << C', 'module M', 'def m', 'def o.m'].each do |c|
+ assert_valid_syntax("->{#{c};->{_1};end;_1}\n")
+ assert_valid_syntax("->{_1;#{c};->{_1};end}\n")
+ end
+
+ 1.times {
+ [
+ _1,
+ assert_equal([:a], eval("[:a].map{_1}")),
+ assert_raise(NameError) {eval("_1")},
+ ]
+ }
+
+ assert_valid_syntax("proc {def foo(_);end;_1}")
+ assert_valid_syntax("p { [_1 **2] }")
+ assert_valid_syntax("proc {_1;def foo();end;_1}")
+ end
+
+ def test_it
+ assert_valid_syntax('proc {it}')
+ assert_syntax_error('[1,2].then {it+_2}', /'it' is already used/)
+ assert_syntax_error('[1,2].then {_2+it}', /numbered parameter is already used/)
+ assert_equal([1, 2], eval('[1,2].then {it}'))
+ assert_syntax_error('[1,2].then {"#{it}#{_2}"}', /'it' is already used/)
+ assert_syntax_error('[1,2].then {"#{_2}#{it}"}', /numbered parameter is already used/)
+ assert_syntax_error('->{it+_2}.call(1,2)', /'it' is already used/)
+ assert_syntax_error('->{_2+it}.call(1,2)', /numbered parameter is already used/)
+ assert_equal(4, eval('->(a=->{it}){a}.call.call(4)'))
+ assert_equal(5, eval('-> a: ->{it} {a}.call.call(5)'))
+ assert_syntax_error('proc {|| it}', /ordinary parameter is defined/)
+ assert_syntax_error('proc {|;a| it}', /ordinary parameter is defined/)
+ assert_syntax_error("proc {|\n| it}", /ordinary parameter is defined/)
+ assert_syntax_error('proc {|x| it}', /ordinary parameter is defined/)
+ assert_equal([1, 2], eval('1.then {[it, 2.then {_1}]}'))
+ assert_equal([2, 1], eval('1.then {[2.then {_1}, it]}'))
+ assert_syntax_error('->(){it}', /ordinary parameter is defined/)
+ assert_syntax_error('->(x){it}', /ordinary parameter is defined/)
+ assert_syntax_error('->x{it}', /ordinary parameter is defined/)
+ assert_syntax_error('->x:_1{}', /ordinary parameter is defined/)
+ assert_syntax_error('->x=it{}', /ordinary parameter is defined/)
+ assert_valid_syntax('-> {it; -> {_2}}')
+ assert_valid_syntax('-> {-> {it}; _2}')
+ assert_equal([1, nil], eval('proc {that=it; it=nil; [that, it]}.call(1)'))
+ assert_equal(1, eval('proc {it = 1}.call'))
+ assert_warning(/1: warning: assigned but unused variable - it/) {
+ assert_equal(2, eval('a=Object.new; def a.foo; it = 2; end; a.foo'))
+ }
+ assert_equal(3, eval('proc {|it| it}.call(3)'))
+ assert_equal(4, eval('a=Object.new; def a.foo(it); it; end; a.foo(4)'))
+ assert_equal(5, eval('a=Object.new; def a.it; 5; end; a.it'))
+ assert_equal(6, eval('a=Class.new; a.class_eval{ def it; 6; end }; a.new.it'))
+ assert_equal([7, 8], eval('a=[]; [7].each { a << it; [8].each { a << it } }; a'))
+ assert_raise_with_message(NameError, /undefined local variable or method 'it'/) do
+ eval('it')
+ end
+ ['class C', 'class << C', 'module M', 'def m', 'def o.m'].each do |c|
+ assert_valid_syntax("->{#{c};->{it};end;it}\n")
+ assert_valid_syntax("->{it;#{c};->{it};end}\n")
+ end
+ 1.times do
+ [
+ assert_equal(0, it),
+ assert_equal([:a], eval('[:a].map{it}')),
+ assert_raise(NameError) {eval('it')},
+ ]
+ end
+ assert_valid_syntax('proc {def foo(_);end;it}')
+ assert_syntax_error('p { [it **2] }', /unexpected \*\*/)
+ assert_equal(1, eval('1.then { raise rescue it }'))
+ assert_equal(2, eval('1.then { 2.then { raise rescue it } }'))
+ assert_equal(3, eval('3.then { begin; raise; rescue; it; end }'))
+ assert_equal(4, eval('4.tap { begin; raise ; rescue; raise rescue it; end; }'))
+ assert_equal(5, eval('a = 0; 5.then { begin; nil; ensure; a = it; end }; a'))
+ assert_equal(6, eval('a = 0; 6.then { begin; nil; rescue; ensure; a = it; end }; a'))
+ assert_equal(7, eval('a = 0; 7.then { begin; raise; ensure; a = it; end } rescue a'))
+ assert_equal(8, eval('a = 0; 8.then { begin; raise; rescue; ensure; a = it; end }; a'))
+ assert_equal(/9/, eval('9.then { /#{it}/o }'))
+ end
+
+ def test_it_with_splat_super_method
+ bug21256 = '[ruby-core:121592] [Bug #21256]'
+
+ a = Class.new do
+ define_method(:foo) { it }
+ end
+ b = Class.new(a) do
+ def foo(*args) = super
+ end
+
+ assert_equal(1, b.new.foo(1), bug21256)
end
def test_value_expr_in_condition
@@ -1401,6 +2021,347 @@ eom
assert_valid_syntax("tap {a = (break unless true)}")
end
+ def test_value_expr_in_singleton
+ mesg = /void value expression/
+ assert_syntax_error("class << (return); end", mesg)
+ end
+
+ def test_tautological_condition
+ assert_valid_syntax("def f() return if false and invalid; nil end")
+ assert_valid_syntax("def f() return unless true or invalid; nil end")
+ end
+
+ def test_argument_forwarding
+ assert_valid_syntax('def foo(...) bar(...) end')
+ assert_valid_syntax('def foo(...) end')
+ assert_valid_syntax('def foo(a, ...) bar(...) end')
+ assert_valid_syntax("def foo ...\n bar(...)\nend")
+ assert_valid_syntax("def foo a, ...\n bar(...)\nend")
+ assert_valid_syntax("def foo b = 1, ...\n bar(...)\nend")
+ assert_valid_syntax("def foo ...; bar(...); end")
+ assert_valid_syntax("def foo a, ...; bar(...); end")
+ assert_valid_syntax("def foo b = 1, ...; bar(...); end")
+ assert_valid_syntax("(def foo ...\n bar(...)\nend)")
+ assert_valid_syntax("(def foo ...; bar(...); end)")
+ assert_valid_syntax("def (1...).foo ...; bar(...); end")
+ assert_valid_syntax("def (tap{1...}).foo ...; bar(...); end")
+ assert_valid_syntax('def ==(...) end')
+ assert_valid_syntax('def [](...) end')
+ assert_valid_syntax('def nil(...) end')
+ assert_valid_syntax('def true(...) end')
+ assert_valid_syntax('def false(...) end')
+ assert_valid_syntax('->a=1...{}')
+ unexpected = /unexpected \.{3}/
+ assert_syntax_error('iter do |...| end', /unexpected/)
+ assert_syntax_error('iter {|...|}', /unexpected/)
+ assert_syntax_error('->... {}', unexpected)
+ assert_syntax_error('->(...) {}', unexpected)
+ assert_syntax_error('->a,... {}', unexpected)
+ assert_syntax_error('->(a,...) {}', unexpected)
+ assert_syntax_error('->a=1,... {}', unexpected)
+ assert_syntax_error('->(a=1,...) {}', unexpected)
+ assert_syntax_error('def foo(x, y, z) bar(...); end', /unexpected/)
+ assert_syntax_error('def foo(x, y, z) super(...); end', /unexpected/)
+ assert_syntax_error('def foo(...) yield(...); end', /unexpected/)
+ assert_syntax_error('def foo(...) return(...); end', /unexpected/)
+ assert_syntax_error('def foo(...) a = (...); end', /unexpected/)
+ assert_syntax_error('def foo(...) [...]; end', /unexpected/)
+ assert_syntax_error('def foo(...) foo[...]; end', /unexpected/)
+ assert_syntax_error('def foo(...) foo[...] = x; end', /unexpected/)
+ assert_syntax_error('def foo(...) foo(...) { }; end', /both block arg and actual block given/)
+ assert_syntax_error('def foo(...) defined?(...); end', /unexpected/)
+ assert_syntax_error('def foo(*rest, ...) end', '... after rest argument')
+ assert_syntax_error('def foo(*, ...) end', '... after rest argument')
+
+ obj1 = Object.new
+ def obj1.bar(*args, **kws, &block)
+ if block
+ block.call(args, kws)
+ else
+ [args, kws]
+ end
+ end
+ obj4 = obj1.clone
+ obj5 = obj1.clone
+ obj6 = obj1.clone
+ obj1.instance_eval('def foo(...) bar(...) end', __FILE__, __LINE__)
+ obj4.instance_eval("def foo ...\n bar(...)\n""end", __FILE__, __LINE__)
+ obj5.instance_eval("def foo ...; bar(...); end", __FILE__, __LINE__)
+ obj6.instance_eval('def foo(...) eval("bar(...)") end', __FILE__, __LINE__)
+
+ klass = Class.new {
+ def foo(*args, **kws, &block)
+ if block
+ block.call(args, kws)
+ else
+ [args, kws]
+ end
+ end
+ }
+ obj2 = klass.new
+ obj2.instance_eval('def foo(...) super(...) end', __FILE__, __LINE__)
+
+ obj3 = Object.new
+ def obj3.bar(*args, &block)
+ if kws = Hash.try_convert(args.last)
+ args.pop
+ else
+ kws = {}
+ end
+ if block
+ block.call(args, kws)
+ else
+ [args, kws]
+ end
+ end
+ obj3.instance_eval('def foo(...) bar(...) end', __FILE__, __LINE__)
+
+ [obj1, obj2, obj3, obj4, obj5, obj6].each do |obj|
+ assert_warning('') {
+ assert_equal([[1, 2, 3], {k1: 4, k2: 5}], obj.foo(1, 2, 3, k1: 4, k2: 5) {|*x| x})
+ }
+ assert_warning('') {
+ assert_equal([[1, 2, 3], {k1: 4, k2: 5}], obj.foo(1, 2, 3, k1: 4, k2: 5))
+ }
+ array = obj == obj3 ? [] : [{}]
+ assert_warning('') {
+ assert_equal([array, {}], obj.foo({}) {|*x| x})
+ }
+ assert_warning('') {
+ assert_equal([array, {}], obj.foo({}))
+ }
+ assert_equal(-1, obj.method(:foo).arity)
+ parameters = obj.method(:foo).parameters
+ assert_equal(:rest, parameters.dig(0, 0))
+ assert_equal(:keyrest, parameters.dig(1, 0))
+ assert_equal(:block, parameters.dig(2, 0))
+ end
+ end
+
+ def test_argument_forwarding_with_leading_arguments
+ obj = Object.new
+ def obj.bar(*args, **kws, &block)
+ if block
+ block.call(args, kws)
+ else
+ [args, kws]
+ end
+ end
+ obj.instance_eval('def foo(_a, ...) bar(...) end', __FILE__, __LINE__)
+ assert_equal [[], {}], obj.foo(1)
+ assert_equal [[2], {}], obj.foo(1, 2)
+ assert_equal [[2, 3], {}], obj.foo(1, 2, 3)
+ assert_equal [[], {a: 1}], obj.foo(1, a: 1)
+ assert_equal [[2], {a: 1}], obj.foo(1, 2, a: 1)
+ assert_equal [[2, 3], {a: 1}], obj.foo(1, 2, 3, a: 1)
+ assert_equal [[2, 3], {a: 1}], obj.foo(1, 2, 3, a: 1){|args, kws| [args, kws]}
+
+ obj.singleton_class.send(:remove_method, :foo)
+ obj.instance_eval('def foo(...) bar(1, ...) end', __FILE__, __LINE__)
+ assert_equal [[1], {}], obj.foo
+ assert_equal [[1, 1], {}], obj.foo(1)
+ assert_equal [[1, 1, 2], {}], obj.foo(1, 2)
+ assert_equal [[1, 1, 2, 3], {}], obj.foo(1, 2, 3)
+ assert_equal [[1], {a: 1}], obj.foo(a: 1)
+ assert_equal [[1, 1], {a: 1}], obj.foo(1, a: 1)
+ assert_equal [[1, 1, 2], {a: 1}], obj.foo(1, 2, a: 1)
+ assert_equal [[1, 1, 2, 3], {a: 1}], obj.foo(1, 2, 3, a: 1)
+ assert_equal [[1, 1, 2, 3], {a: 1}], obj.foo(1, 2, 3, a: 1){|args, kws| [args, kws]}
+
+ obj.singleton_class.send(:remove_method, :foo)
+ obj.instance_eval('def foo(a, ...) bar(a, ...) end', __FILE__, __LINE__)
+ assert_equal [[4], {}], obj.foo(4)
+ assert_equal [[4, 2], {}], obj.foo(4, 2)
+ assert_equal [[4, 2, 3], {}], obj.foo(4, 2, 3)
+ assert_equal [[4], {a: 1}], obj.foo(4, a: 1)
+ assert_equal [[4, 2], {a: 1}], obj.foo(4, 2, a: 1)
+ assert_equal [[4, 2, 3], {a: 1}], obj.foo(4, 2, 3, a: 1)
+ assert_equal [[4, 2, 3], {a: 1}], obj.foo(4, 2, 3, a: 1){|args, kws| [args, kws]}
+
+ obj.singleton_class.send(:remove_method, :foo)
+ obj.instance_eval('def foo(_a, ...) bar(1, ...) end', __FILE__, __LINE__)
+ assert_equal [[1], {}], obj.foo(4)
+ assert_equal [[1, 2], {}], obj.foo(4, 2)
+ assert_equal [[1, 2, 3], {}], obj.foo(4, 2, 3)
+ assert_equal [[1], {a: 1}], obj.foo(4, a: 1)
+ assert_equal [[1, 2], {a: 1}], obj.foo(4, 2, a: 1)
+ assert_equal [[1, 2, 3], {a: 1}], obj.foo(4, 2, 3, a: 1)
+ assert_equal [[1, 2, 3], {a: 1}], obj.foo(4, 2, 3, a: 1){|args, kws| [args, kws]}
+
+ obj.singleton_class.send(:remove_method, :foo)
+ obj.instance_eval('def foo(_a, _b, ...) bar(...) end', __FILE__, __LINE__)
+ assert_equal [[], {}], obj.foo(4, 5)
+ assert_equal [[2], {}], obj.foo(4, 5, 2)
+ assert_equal [[2, 3], {}], obj.foo(4, 5, 2, 3)
+ assert_equal [[], {a: 1}], obj.foo(4, 5, a: 1)
+ assert_equal [[2], {a: 1}], obj.foo(4, 5, 2, a: 1)
+ assert_equal [[2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1)
+ assert_equal [[2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1){|args, kws| [args, kws]}
+
+ obj.singleton_class.send(:remove_method, :foo)
+ obj.instance_eval('def foo(_a, _b, ...) bar(1, ...) end', __FILE__, __LINE__)
+ assert_equal [[1], {}], obj.foo(4, 5)
+ assert_equal [[1, 2], {}], obj.foo(4, 5, 2)
+ assert_equal [[1, 2, 3], {}], obj.foo(4, 5, 2, 3)
+ assert_equal [[1], {a: 1}], obj.foo(4, 5, a: 1)
+ assert_equal [[1, 2], {a: 1}], obj.foo(4, 5, 2, a: 1)
+ assert_equal [[1, 2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1)
+ assert_equal [[1, 2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1){|args, kws| [args, kws]}
+
+ obj.singleton_class.send(:remove_method, :foo)
+ obj.instance_eval('def foo(_a, ...) bar(1, 2, ...) end', __FILE__, __LINE__)
+ assert_equal [[1, 2], {}], obj.foo(5)
+ assert_equal [[1, 2, 5], {}], obj.foo(4, 5)
+ assert_equal [[1, 2, 5, 2], {}], obj.foo(4, 5, 2)
+ assert_equal [[1, 2, 5, 2, 3], {}], obj.foo(4, 5, 2, 3)
+ assert_equal [[1, 2, 5], {a: 1}], obj.foo(4, 5, a: 1)
+ assert_equal [[1, 2, 5, 2], {a: 1}], obj.foo(4, 5, 2, a: 1)
+ assert_equal [[1, 2, 5, 2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1)
+ assert_equal [[1, 2, 5, 2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1){|args, kws| [args, kws]}
+
+ obj.singleton_class.send(:remove_method, :foo)
+ obj.instance_eval('def foo(a, b, ...) bar(b, a, ...) end', __FILE__, __LINE__)
+ assert_equal [[5, 4], {}], obj.foo(4, 5)
+ assert_equal [[5, 4, 2], {}], obj.foo(4, 5, 2)
+ assert_equal [[5, 4, 2, 3], {}], obj.foo(4, 5, 2, 3)
+ assert_equal [[5, 4], {a: 1}], obj.foo(4, 5, a: 1)
+ assert_equal [[5, 4, 2], {a: 1}], obj.foo(4, 5, 2, a: 1)
+ assert_equal [[5, 4, 2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1)
+ assert_equal [[5, 4, 2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1){|args, kws| [args, kws]}
+
+ obj.singleton_class.send(:remove_method, :foo)
+ obj.instance_eval('def foo(a, _b, ...) bar(a, ...) end', __FILE__, __LINE__)
+ assert_equal [[4], {}], obj.foo(4, 5)
+ assert_equal [[4, 2], {}], obj.foo(4, 5, 2)
+ assert_equal [[4, 2, 3], {}], obj.foo(4, 5, 2, 3)
+ assert_equal [[4], {a: 1}], obj.foo(4, 5, a: 1)
+ assert_equal [[4, 2], {a: 1}], obj.foo(4, 5, 2, a: 1)
+ assert_equal [[4, 2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1)
+ assert_equal [[4, 2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1){|args, kws| [args, kws]}
+
+ obj.singleton_class.send(:remove_method, :foo)
+ obj.instance_eval('def foo(a, ...) bar(a, 1, ...) end', __FILE__, __LINE__)
+ assert_equal [[4, 1], {}], obj.foo(4)
+ assert_equal [[4, 1, 5], {}], obj.foo(4, 5)
+ assert_equal [[4, 1, 5, 2], {}], obj.foo(4, 5, 2)
+ assert_equal [[4, 1, 5, 2, 3], {}], obj.foo(4, 5, 2, 3)
+ assert_equal [[4, 1, 5], {a: 1}], obj.foo(4, 5, a: 1)
+ assert_equal [[4, 1, 5, 2], {a: 1}], obj.foo(4, 5, 2, a: 1)
+ assert_equal [[4, 1, 5, 2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1)
+ assert_equal [[4, 1, 5, 2, 3], {a: 1}], obj.foo(4, 5, 2, 3, a: 1){|args, kws| [args, kws]}
+
+ obj.singleton_class.send(:remove_method, :foo)
+ obj.instance_eval("def foo a, ...\n bar(a, ...)\n"" end", __FILE__, __LINE__)
+ assert_equal [[4], {}], obj.foo(4)
+ assert_equal [[4, 2], {}], obj.foo(4, 2)
+ assert_equal [[4, 2, 3], {}], obj.foo(4, 2, 3)
+ assert_equal [[4], {a: 1}], obj.foo(4, a: 1)
+ assert_equal [[4, 2], {a: 1}], obj.foo(4, 2, a: 1)
+ assert_equal [[4, 2, 3], {a: 1}], obj.foo(4, 2, 3, a: 1)
+ assert_equal [[4, 2, 3], {a: 1}], obj.foo(4, 2, 3, a: 1){|args, kws| [args, kws]}
+
+ obj.singleton_class.send(:remove_method, :foo)
+ obj.instance_eval("def foo a, ...; bar(a, ...); end", __FILE__, __LINE__)
+ assert_equal [[4], {}], obj.foo(4)
+ assert_equal [[4, 2], {}], obj.foo(4, 2)
+ assert_equal [[4, 2, 3], {}], obj.foo(4, 2, 3)
+ assert_equal [[4], {a: 1}], obj.foo(4, a: 1)
+ assert_equal [[4, 2], {a: 1}], obj.foo(4, 2, a: 1)
+ assert_equal [[4, 2, 3], {a: 1}], obj.foo(4, 2, 3, a: 1)
+ assert_equal [[4, 2, 3], {a: 1}], obj.foo(4, 2, 3, a: 1){|args, kws| [args, kws]}
+
+ exp = eval("-> (a: nil) {a...1}")
+ assert_equal 0...1, exp.call(a: 0)
+ end
+
+ def test_argument_forwarding_with_super
+ assert_valid_syntax('def foo(...) super {}; end')
+ assert_valid_syntax('def foo(...) super() {}; end')
+ assert_syntax_error('def foo(...) super(...) {}; end', /both block arg and actual block/)
+ assert_syntax_error('def foo(...) super(1, ...) {}; end', /both block arg and actual block/)
+ end
+
+ def test_argument_forwarding_with_super_memory_leak
+ assert_no_memory_leak([], "#{<<-'begin;'}", "#{<<-'end;'}", rss: true)
+ code = proc do
+ eval("def foo(...) super(...) {}; end")
+ raise "unreachable"
+ rescue SyntaxError
+ end
+
+ 1_000.times(&code)
+ begin;
+ 100_000.times(&code)
+ end;
+ end
+
+ def test_class_module_Object_ancestors
+ assert_ruby_status([], <<-RUBY)
+ m = Module.new
+ m::Bug18832 = 1
+ include m
+ class Bug18832; end
+ RUBY
+ assert_ruby_status([], <<-RUBY)
+ m = Module.new
+ m::Bug18832 = 1
+ include m
+ module Bug18832; end
+ RUBY
+ end
+
+ def test_cdhash
+ assert_separately([], <<-RUBY)
+ n = case 1 when 2r then false else true end
+ assert_equal(n, true, '[ruby-core:103759] [Bug #17854]')
+ RUBY
+ assert_separately([], <<-RUBY)
+ n = case 3/2r when 1.5r then true else false end
+ assert_equal(n, true, '[ruby-core:103759] [Bug #17854]')
+ RUBY
+ assert_separately([], <<-RUBY)
+ n = case 1i when 1i then true else false end
+ assert_equal(n, true, '[ruby-core:103759] [Bug #17854]')
+ RUBY
+ end
+
+ def test_defined_in_short_circuit_if_condition
+ bug = '[ruby-core:20501]'
+ conds = [
+ "false && defined?(Some::CONSTANT)",
+ "true || defined?(Some::CONSTANT)",
+ "(false && defined?(Some::CONSTANT))", # parens exercise different code path
+ "(true || defined?(Some::CONSTANT))",
+ "@val && false && defined?(Some::CONSTANT)",
+ "@val || true || defined?(Some::CONSTANT)"
+ ]
+
+ conds.each do |cond|
+ code = %Q{
+ def my_method
+ var = "there"
+ if #{cond}
+ var = "here"
+ end
+ raise var
+ end
+ begin
+ my_method
+ rescue
+ print 'ok'
+ else
+ print 'ng'
+ end
+ }
+ # Invoke in a subprocess because the bug caused a segfault using the parse.y compiler.
+ # Don't use assert_separately because the bug was best reproducible in a clean slate,
+ # without test env loaded.
+ out, _err, status = EnvUtil.invoke_ruby(["--disable-gems"], code, true, false)
+ assert_predicate(status, :success?, bug)
+ assert_equal 'ok', out
+ end
+ end
+
private
def not_label(x) @result = x; @not_label ||= nil end
@@ -1435,7 +2396,7 @@ eom
end
end
- def caller_lineno(*)
+ def caller_lineno(*, &)
caller_locations(1, 1)[0].lineno
end
end