# frozen_string_literal: false require 'test/unit' class TestSyntax < Test::Unit::TestCase using Module.new { refine(Object) do def `(s) #` s end end } 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) dir = ARGV.shift for script in Dir["#{dir}/**/*.rb"].sort assert_valid_syntax(IO::read(script), script) end eom end def test_syntax_lib; assert_syntax_files("lib"); end def test_syntax_sample; assert_syntax_files("sample"); end def test_syntax_ext; assert_syntax_files("ext"); end def test_syntax_test; assert_syntax_files("test"); end def test_defined_empty_argument bug8220 = '[ruby-core:53999] [Bug #8220]' assert_ruby_status(%w[--disable-gem], 'puts defined? ()', bug8220) end def test_must_ascii_compatible require 'tempfile' f = Tempfile.new("must_ac_") Encoding.list.each do |enc| next unless enc.ascii_compatible? make_tmpsrc(f, "# -*- coding: #{enc.name} -*-") assert_nothing_raised(ArgumentError, enc.name) {load(f.path)} end Encoding.list.each do |enc| next if enc.ascii_compatible? make_tmpsrc(f, "# -*- coding: #{enc.name} -*-") assert_raise(ArgumentError, enc.name) {load(f.path)} end ensure f.close! if f end def test_script_lines require 'tempfile' f = Tempfile.new("bug4361_") bug4361 = '[ruby-dev:43168]' with_script_lines do |debug_lines| Encoding.list.each do |enc| next unless enc.ascii_compatible? make_tmpsrc(f, "# -*- coding: #{enc.name} -*-\n#----------------") load(f.path) assert_equal([f.path], debug_lines.keys) assert_equal([enc, enc], debug_lines[f.path].map(&:encoding), bug4361) end end ensure f.close! if f end def test_newline_in_block_parameters bug = '[ruby-dev:45292]' ["", "a", "a, b"].product(["", ";x", [";", "x"]]) do |params| params = ["|", *params, "|"].join("\n") assert_valid_syntax("1.times{#{params}}", __FILE__, "#{bug} #{params.inspect}") end end tap do |_, bug6115 = '[ruby-dev:45308]', blockcall = '["elem"].each_with_object [] do end', methods = [['map', 'no'], ['inject([])', 'with']], 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' name = "test_#{n3}_block_after_blockcall_#{n1}_#{n2}_arg" code = "#{blockcall}#{c}#{m} #{b}" define_method(name) {assert_valid_syntax(code, bug6115)} end end def test_do_block_in_cmdarg bug9726 = '[ruby-core:61950] [Bug #9726]' assert_valid_syntax("tap (proc do end)", __FILE__, bug9726) end def test_keyword_rest bug5989 = '[ruby-core:42455]' assert_valid_syntax("def kwrest_test(**a) a; end", __FILE__, bug5989) assert_valid_syntax("def kwrest_test2(**a, &b) end", __FILE__, bug5989) o = Object.new def o.kw(**a) a end assert_equal({}, o.kw, bug5989) assert_equal({foo: 1}, o.kw(foo: 1), bug5989) assert_equal({foo: 1, bar: 2}, o.kw(foo: 1, bar: 2), bug5989) EnvUtil.under_gc_stress do eval("def o.m(k: 0) k end") end assert_equal(42, o.m(k: 42), '[ruby-core:45744]') bug7922 = '[ruby-core:52744] [Bug #7922]' def o.bug7922(**) end assert_nothing_raised(ArgumentError, bug7922) {o.bug7922(foo: 42)} end class KW2 def kw(k1: 1, k2: 2) [k1, k2] end end def test_keyword_splat assert_valid_syntax("foo(**h)", __FILE__) o = KW2.new h = {k1: 11, k2: 12} assert_equal([11, 12], o.kw(**h)) assert_equal([11, 12], o.kw(k2: 22, **h)) assert_equal([11, 22], o.kw(**h, **{k2: 22})) assert_equal([11, 12], o.kw(**{k2: 22}, **h)) end def test_keyword_duplicated_splat bug10315 = '[ruby-core:65368] [Bug #10315]' o = KW2.new assert_equal([23, 2], o.kw(**{k1: 22}, **{k1: 23}), bug10315) h = {k3: 31} assert_raise(ArgumentError) {o.kw(**h)} h = {"k1"=>11, k2: 12} assert_raise(TypeError) {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 a.clear r = nil assert_warn(/duplicated/) {r = eval("a.f(k: a.add(1), k: a.add(2))")} assert_equal(2, r) assert_equal([1, 2], a, bug10315) a.clear r = nil assert_warn(/duplicated/) {r = eval("a.f({k: a.add(1), k: a.add(2)})")} assert_equal(2, r) assert_equal([1, 2], a, bug10315) end def test_keyword_empty_splat assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}") begin; bug10719 = '[ruby-core:67446] [Bug #10719]' assert_valid_syntax("foo(a: 1, **{})", bug10719) end; assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}") begin; bug13756 = '[ruby-core:82113] [Bug #13756]' assert_valid_syntax("defined? foo(**{})", bug13756) end; 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) o = Object.new assert_warn(/circular argument reference - var/) do o.instance_eval("def foo(var: bar(var)) var end") end o = Object.new assert_warn(/circular argument reference - var/) 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") end o = Object.new assert_warn("") do o.instance_eval("def foo(var: def bar(var) var; end) var end") end o = Object.new assert_warn("") do o.instance_eval("proc {|var: 1| var}") end end def test_keyword_invalid_name bug11663 = '[ruby-core:71356] [Bug #11663]' 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('proc {|arg1?:|}', /arg1\?/, bug11663) assert_syntax_error('proc {|arg1?:, arg2:|}', /arg1\?/, bug11663) 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) o = Object.new assert_warn(/circular argument reference - var/) do o.instance_eval("def foo(var = bar(var)) var end") end o = Object.new assert_warn(/circular argument reference - var/) do o.instance_eval("def foo(var = bar {var}) var end") end o = Object.new assert_warn(/circular argument reference - var/) do o.instance_eval("def foo(var = (def bar;end; 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") end o = Object.new assert_warn("") do o.instance_eval("def foo(var = bar {|var| var}) var end") end o = Object.new assert_warn("") do o.instance_eval("def foo(var = def bar(var) var; end) var end") end o = Object.new assert_warn("") do o.instance_eval("proc {|var = 1| var}") end end def test_warn_grouped_expression bug5214 = '[ruby-core:39050]' assert_warning("", bug5214) do assert_valid_syntax("foo \\\n(\n true)", "test", verbose: true) end end def test_warn_unreachable assert_warning("test:3: warning: statement not reached\n") do code = "loop do\n" "break\n" "foo\n" "end" assert_valid_syntax(code, "test", verbose: true) end end def test_warn_balanced warning = < (x, y) {}", __FILE__, feature6390) end def test_do_block_in_cmdarg_begin bug6419 = '[ruby-dev:45631]' assert_valid_syntax("p begin 1.times do 1 end end", __FILE__, bug6419) end def test_do_block_in_call_args bug9308 = '[ruby-core:59342] [Bug #9308]' assert_valid_syntax("bar def foo; self.each do end end", bug9308) end def test_do_block_in_lambda bug11107 = '[ruby-core:69017] [Bug #11107]' assert_valid_syntax('p ->() do a() do end end', bug11107) end def test_do_block_after_lambda bug11380 = '[ruby-core:70067] [Bug #11380]' assert_valid_syntax('p -> { :hello }, a: 1 do end', bug11380) end def test_reserved_method_no_args bug6403 = '[ruby-dev:45626]' assert_valid_syntax("def self; :foo; end", __FILE__, bug6403) end def test_unassignable gvar = global_variables %w[self nil true false __FILE__ __LINE__ __ENCODING__].each do |kwd| assert_raise(SyntaxError) {eval("#{kwd} = nil")} assert_equal(gvar, global_variables) end end Bug7559 = '[ruby-dev:46737]' def test_lineno_command_call_quote expected = __LINE__ + 1 actual = caller_lineno "a b c d e" assert_equal(expected, actual, "#{Bug7559}: ") end def assert_dedented_heredoc(expect, result, mesg = "") all_assertions(mesg) do |a| %w[eos "eos" 'eos' `eos`].each do |eos| a.for(eos) do assert_equal(eval("<<-#{eos}\n#{expect}eos\n"), eval("<<~#{eos}\n#{result}eos\n")) end end end end def test_dedented_heredoc_without_indentation result = " y\n" \ "z\n" expect = result assert_dedented_heredoc(expect, result) end def test_dedented_heredoc_with_indentation result = " a\n" \ " b\n" expect = " a\n" \ "b\n" assert_dedented_heredoc(expect, result) end def test_dedented_heredoc_with_blank_less_indented_line # the blank line has two leading spaces result = " a\n" \ " \n" \ " b\n" expect = "a\n" \ "\n" \ "b\n" assert_dedented_heredoc(expect, result) end def test_dedented_heredoc_with_blank_less_indented_line_escaped result = " a\n" \ "\\ \\ \n" \ " b\n" expect = result assert_dedented_heredoc(expect, result) end def test_dedented_heredoc_with_blank_more_indented_line # the blank line has six leading spaces result = " a\n" \ " \n" \ " b\n" expect = "a\n" \ " \n" \ "b\n" assert_dedented_heredoc(expect, result) end def test_dedented_heredoc_with_blank_more_indented_line_escaped result = " a\n" \ "\\ \\ \\ \\ \\ \\ \n" \ " b\n" expect = result assert_dedented_heredoc(expect, result) end def test_dedented_heredoc_with_empty_line result = " This would contain specially formatted text.\n" \ "\n" \ " That might span many lines\n" expect = 'This would contain specially formatted text.'"\n" \ ''"\n" \ 'That might span many lines'"\n" assert_dedented_heredoc(expect, result) end def test_dedented_heredoc_with_interpolated_expression result = ' #{1}a'"\n" \ " zy\n" expect = ' #{1}a'"\n" \ "zy\n" assert_dedented_heredoc(expect, result) end def test_dedented_heredoc_with_interpolated_string w = "" result = " \#{mesg} a\n" \ " zy\n" expect = '#{mesg} a'"\n" \ ' zy'"\n" assert_dedented_heredoc(expect, result) end def test_dedented_heredoc_with_concatenation bug11990 = '[ruby-core:72857] [Bug #11990] concatenated string should not be dedented' %w[eos "eos" 'eos'].each do |eos| assert_equal("x\n y", eval("<<~#{eos} ' y'\n x\neos\n"), "#{bug11990} with #{eos}") end %w[eos "eos" 'eos' `eos`].each do |eos| _, expect = eval("[<<~#{eos}, ' x']\n"" y\n""eos\n") assert_equal(' x', expect, bug11990) end end def test_lineno_after_heredoc bug7559 = '[ruby-dev:46737]' expected, _, actual = __LINE__, <