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.rb291
1 files changed, 246 insertions, 45 deletions
diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb
index 355f524e1a..b355128a73 100644
--- a/test/ruby/test_syntax.rb
+++ b/test/ruby/test_syntax.rb
@@ -139,6 +139,11 @@ class TestSyntax < Test::Unit::TestCase
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
@@ -156,8 +161,10 @@ class TestSyntax < Test::Unit::TestCase
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
@@ -177,10 +184,12 @@ class TestSyntax < Test::Unit::TestCase
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
@@ -190,6 +199,7 @@ class TestSyntax < Test::Unit::TestCase
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
@@ -207,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)}
@@ -233,6 +243,7 @@ class TestSyntax < Test::Unit::TestCase
def test_array_kwsplat_hash
kw = {}
h = {a: 1}
+ a = []
assert_equal([], [**{}])
assert_equal([], [**kw])
assert_equal([h], [**h])
@@ -247,6 +258,20 @@ class TestSyntax < Test::Unit::TestCase
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])
@@ -314,7 +339,12 @@ 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)}
@@ -376,12 +406,11 @@ class TestSyntax < Test::Unit::TestCase
end
def test_keyword_self_reference
- message = /circular argument reference - var/
- assert_syntax_error("def foo(var: defined?(var)) var end", message)
- assert_syntax_error("def foo(var: var) var end", message)
- assert_syntax_error("def foo(var: bar(var)) var end", message)
- assert_syntax_error("def foo(var: bar {var}) var end", message)
- assert_syntax_error("def foo(var: (1 in ^var)); end", message)
+ 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("") do
@@ -407,6 +436,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_keyword_invalid_name
@@ -440,14 +472,13 @@ class TestSyntax < Test::Unit::TestCase
end
def test_optional_self_reference
- message = /circular argument reference - var/
- assert_syntax_error("def foo(var = defined?(var)) var end", message)
- assert_syntax_error("def foo(var = var) var end", message)
- assert_syntax_error("def foo(var = bar(var)) var end", message)
- assert_syntax_error("def foo(var = bar {var}) var end", message)
- assert_syntax_error("def foo(var = (def bar;end; var)) var end", message)
- assert_syntax_error("def foo(var = (def self.bar;end; var)) var end", message)
- assert_syntax_error("def foo(var = (1 in ^var)); end", message)
+ 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("") do
@@ -473,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
@@ -490,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"],
@@ -507,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
@@ -699,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
@@ -709,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
@@ -719,7 +751,7 @@ WARN
end
}
}
- assert_warning(/3: #{w}.+4: #{w}.+4: #{w}.+5: #{w}.+5: #{w}/m) {
+ assert_warning(/#{w[3]}/) {
eval %q{
case 1
when __LINE__, __LINE__
@@ -728,7 +760,7 @@ WARN
end
}
}
- assert_warning(/3: #{w}.+4: #{w}.+4: #{w}.+5: #{w}.+5: #{w}/m) {
+ assert_warning(/#{w[3]}/) {
eval %q{
case 1
when __FILE__, __FILE__
@@ -740,7 +772,7 @@ WARN
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
@@ -873,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" \
@@ -980,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
@@ -1019,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\
@@ -1209,11 +1259,71 @@ eom
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
assert_syntax_error("*a&.x=0", /multiple assignment destination/)
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")
@@ -1337,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
@@ -1553,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
@@ -1730,15 +1840,12 @@ eom
assert_equal("class ok", k.rescued("ok"))
assert_equal("instance ok", k.new.rescued("ok"))
- # Current technical limitation: cannot prepend "private" or something for command endless def
- error = /syntax error, unexpected string literal/
- error2 = /syntax error, unexpected local variable or method/
- assert_syntax_error('private def foo = puts "Hello"', error)
- assert_syntax_error('private def foo() = puts "Hello"', error)
- assert_syntax_error('private def foo(x) = puts x', error2)
- assert_syntax_error('private def obj.foo = puts "Hello"', error)
- assert_syntax_error('private def obj.foo() = puts "Hello"', error)
- assert_syntax_error('private def obj.foo(x) = puts x', error2)
+ 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
@@ -1779,6 +1886,10 @@ eom
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}'))
@@ -1861,6 +1972,7 @@ eom
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
@@ -1876,7 +1988,29 @@ eom
]
end
assert_valid_syntax('proc {def foo(_);end;it}')
- assert_syntax_error('p { [it **2] }', /unexpected \*\*arg/)
+ 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
@@ -1909,16 +2043,23 @@ eom
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/)
@@ -1942,9 +2083,11 @@ eom
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)
@@ -1973,7 +2116,7 @@ eom
end
obj3.instance_eval('def foo(...) bar(...) end', __FILE__, __LINE__)
- [obj1, obj2, obj3, obj4, obj5].each do |obj|
+ [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})
}
@@ -2131,14 +2274,35 @@ eom
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_separately([], <<-RUBY)
+ assert_ruby_status([], <<-RUBY)
m = Module.new
m::Bug18832 = 1
include m
class Bug18832; end
RUBY
- assert_separately([], <<-RUBY)
+ assert_ruby_status([], <<-RUBY)
m = Module.new
m::Bug18832 = 1
include m
@@ -2161,6 +2325,43 @@ eom
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
@@ -2195,7 +2396,7 @@ eom
end
end
- def caller_lineno(*)
+ def caller_lineno(*, &)
caller_locations(1, 1)[0].lineno
end
end